Django update model without UpdateView - django

I have a scenario where in my users profile they have an associated organisation. I need to be able to allow the users to select and set this organisation (user_organization), however I would like to do it without allowing them to just see a list (drop down menu) of all the organisations within the application. My work around for this was to issue each organisation with a unique code (org_code) and allow users to enter that code into a form and have the related organisation applied to their profile. I can easily understand the suedocode logic behind this, however I am unsure how to implement it within my views and forms. If anyone can advise me the best way to do this or point me in the correct direction to learn how? It would be appreciated. See my models below for clarification on how things fit together.
Profile:
class Profile(models.Model):
Super_Admin = "Super_Admin"
Admin = "Admin"
Manager = "Manager"
Developer = "Developer"
ROLE_CHOICES = (
(Super_Admin, 'Super_Admin'),
(Admin, 'Admin'),
(Manager, 'Manager'),
(Developer, 'Developer'),
)
user = models.OneToOneField(User, on_delete=models.CASCADE)
user_group = models.OneToOneField(Group, on_delete=models.CASCADE, blank=True, null=True)
user_organization = models.OneToOneField(Organization, on_delete=models.CASCADE, blank=True, null=True)
role = models.CharField(choices=ROLE_CHOICES, default="Developer", max_length=12)
activation_key = models.CharField(max_length=120, blank=True, null=True)
activated = models.BooleanField(default=False)
def __str__(self):
return self.user.username
Organizations:
class Organization(models.Model):
org_name = models.CharField(max_length=120, blank=False, unique=True)
org_code = models.CharField(max_length=120, blank=False, unique=True, default=GenerateOrganozationCode)

Answering using the extra info from the comments you've made above:
"I want them to be able to input a code into a text field which when submitted, if it matches the code (org_code) in the organization model it will then populate the (user_organization) in their Profile with the correct (org_name)"
Within the logic for the view, you need to extract the org_code. You should also make org_code of Organization the primary key of the object (you dont have to, but it would be easier if the pk was the org code). From here, you can map the org_code with the primary key value of Organization.
Organization.objects.get(pk=the_entered_org_code)
If you'd rather not assign org code as the primary key of the object, you can just filter for the org code.
Organization.objects.filter(org_code=the_entered_org_code)
This should get you started.

Related

Django access manytomany field from related_name in a view

I have what i think is a simple question but I am struggling to find out how it works. I get how related name works for foreign keys but with many to many fields it seems to break my brain.
I have two 3 models at play here. A User, TeamMember and Team Model as seen below.
User model is the built in django model.
#TeamMember Model
class TeamMember(models.Model):
member = models.ForeignKey(User, on_delete=models.SET(get_default_team_member), verbose_name='Member Name', related_name="team_members")
...
#Team Model
class Team(models.Model):
name = models.CharField(max_length=50)
manager = models.ForeignKey(TeamMember, on_delete=models.SET_NULL, related_name="managers", null=True, blank=True)
team_lead = models.ForeignKey(TeamMember, on_delete=models.SET_NULL, related_name="tls", null=True, blank=True)
tps = models.ForeignKey(TeamMember, on_delete=models.SET_NULL, related_name="tps", null=True, blank=True)
members = models.ManyToManyField(TeamMember, blank=True, related_name="members")
...
Now in a view i want to access a specific users team. I thought i could do this by doing something like this:
member = TeamMember.objects.get(pk=1)
member_team = member.members.name
However if I print member_name than it prints nothing. If I try to access any of the other fields on that model like member.members.team_lead.first_name it fails to find the team_lead field. I understand that this has a .all() attribute but i thought it was tied to the team object through the members field. So if that member matches the team it would give me the team. So I thought it might be an issue if the same member was linked to more than one team (which is possible) so i tired something like this member.members.all().first().name and i get an error that states it cannot get name from NoneType.
Is there an easy way to get the team name from a relationship like this or am i better off just doing a team query with the user?
Thanks,
jAC
First of all, I would like to point out that you are not using the related_name (and related_query_name parameters in a proper way). I think this SO post will help you to understand the concept in a better way.
So, I would change the related_name (and related_query_name) values in the Team model as below,
class Team(models.Model):
name = models.CharField(max_length=50)
manager = models.ForeignKey(
TeamMember,
on_delete=models.SET_NULL,
related_name="teams",
related_query_name="team",
null=True,
blank=True,
)
team_lead = models.ForeignKey(
TeamMember,
on_delete=models.SET_NULL,
related_name="teams",
related_query_name="team",
null=True,
blank=True,
)
tps = models.ForeignKey(
TeamMember,
on_delete=models.SET_NULL,
related_name="teams",
related_query_name="team",
null=True,
blank=True,
)
members = models.ManyToManyField(
TeamMember, blank=True, related_name="teams", related_query_name="team"
)
...
Now in a view i want to access a specific user's team.
Since the Team and TeamMember models are connected via ManyToManyField, you may have "zero or more" Teams associated with a single TeamMember
So, the following query will get you all the teams associated with a particular TeamMemeber
team_member = TeamMember.objects.get(pk=1)
all_teams = team_member.teams.all()
You can also iterate over the QuerySet as,
team_member = TeamMember.objects.get(pk=1)
for team in team_member.teams.all():
print(team.name)
For anyone wondering what I did based on JPG's advice was the for loop option
team_member = TeamMember.objects.get(pk=1)
teams = [t.name for t in team_member.members.all()]
I personally do not care which team i get as my need in this case is just to pass a team through even if it is none. So i just use team = team[0] if teams.count() > 0 else "No team"

Django - Extending models with multi role users and multiple requirement types

I am new to Django only 2 weeks and I am stuck with scenario I need help with.
This is kind of core concept and I apologize in advance for my lack of knowledge.
I've extended the base user model like below with multiple roles. Now each role has distinct requirements for their profiles.
I need help to understand that how to extend Students or Staff further. There are two scenario I need to extend them.
Both can have similar extended models like addresses below.
Both can have different extended models like Students have CurrentCourse and Staff does not. Staff has Salary model and Students does not.
class User(AbstractUser):
is_student = models.BooleanField(default=True)
is_staff = models.BooleanField(default=True)
class Student(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
dob = models.CharField(max_length=30, blank=True)
location = models.CharField(max_length=30, blank=True)
class CurrentCourse(models.Model):
student = models.OneToOneField(Student,
on_delete=model.CASCADE)
....
class Staff(models.Model):
ROLES = (('Teacher', 'Teacher'), ('Professor', 'Professor'), ('Administration', 'Administration'))
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
role = models.CharField(max_length=100, choices=ROLES)
website = models.CharField(max_length=100, blank=True)
Both types of Users can have multiple addresses and both Students and Staff needs addressess, not sure how to extend this with ForeignKey.
class Address(models.Model):
user = ?
street = models.CharField(max_length=200)
city = models.CharField(max_length=50)
country = models.CharField(max_length=50)
class Salary(models.Model):
staff = models.OneToOneField(Staff, on_delete=models.CASCADE)
current_salary = models.DecimalField(max_digits=10, decimal_places=2)
Finally please let me know how can I apply validators on each model for instance I did sub-classed 'User' to all models instead of Student or Staff. How to apply a validator on OneToOneField like below? I thought that if I apply a validator like:
staff_validator(value):
I can't call user in validator function and not sure if it will be global or within model its applied on with indent.
class Salary(models.Model):
staff = models.OneToOneField(User, on_delete=models.CASCADE, validators=[staff_validator])
Thank you in advance for your kind help for this.
Imagine the real world scenario where your Users are People and People can have different roles with the time. Like if I am a Student today I belong to "People" category and if I will be Professor tomorrow still I will belong to People category.
So People can wear different hats or adapt different roles but they still will be People. Similarly your User model will always be part of all the roles you need to define in database. So you need to extend your User model for not only all the roles in fact for models you believe User can be part of and it should be like below:
class User(AbstractUser):
is_student = models.BooleanField(default=False)
is_teacher = models.BooleanField(default=False)
.....
class Student(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='foo')
.....
class Courses(models.Model):
user = models.ManyToMany(User)
You need to consult official docs with three types of models. Abstract models, Proxy models and Inherited models for more clarity. Also for creating view for your application you can create checks for your users with decorators. Here is a good example as per your question.

Django Multiple User Profiles with Class based views (Best Practices)

I have a website where there are two kinds of users (say) : students and tutors.
Both types have common login functionality, age, gender etc but have distinct attributes such as report cards for students and degree certificates for tutors.
I read this question : Django - Multiple User Profiles and setup my profiles as shown below:
class UserProfile(models.Model):
user = models.OneToOneField(User, primary_key=True, related_name='profile')
mobile = models.CharField(max_length=10, blank=False, null=True)
picture = models.ImageField(
upload_to='images/', default='images/newuser.png')
age = models.IntegerField(null=True)
slug = models.SlugField()
...
And two other models that link to the above. Eg:
class StudentProfile(models.Model):
profile = models.ForeignKey(UserProfile, related_name="user_profile")
#more custom attributes
class TutorProfile(models.Model):
profile = models.ForeignKey(UserProfile, related_name="doctor_profile")
#more custom attributes
Now my questions:
1) SlugField is defined on the UserProfile attribute but will ideally use the User.username field. This means a join between these two tables will happen each time. Is this to be expected?
2) Assuming I am using class based views, editing/viewing the profile will depend on the UserProfile in question. But I want the user to be able to edit/view all his details on the same page. Thus, I will have to fetch TutorProfile / StudentProfile too and add custom logic to ensure updates happen to them too.
It seems to me that there should be a proper way of handling these situations (Since a lot of websites have similar requirements). What are the best practices to be followed in such a situation?
While searching for answers, I came across a solution which I think might suit my needs (posting here to welcome critique and help out others who might be looking for answers).
Taken from Django Design patterns and Best Practices
class UserProfile(models.Model):
user = models.OneToOneField(User, primary_key=True, related_name='profile')
mobile = models.CharField(max_length=10, blank=False, null=True)
picture = models.ImageField(
upload_to='images/', default='images/newuser.png')
age = models.IntegerField(null=True)
gender = models.IntegerField(null=True)
user_type = models.CharField(max_length=20, choices=UserTypes.CHOICES)
slg = models.SlugField()
class Meta(object):
abstract = True
class StudentProfile(models.Model):
report_card = models.FileField(upload_to="/temp")
house = models.CharField()
class Meta(object):
abstract = True
class TutorProfile(models.Model):
performance = models.IntegerField(default=0)
salary = models.IntegerField(default=0)
class Meta(object):
abstract = True
One base abstract class and two specific classes which cover the various user profiles. Keeping them separate like this makes it easy for us to reason about the various fields present in each user type.
Finally,
class Profile(UserProfile, StudentProfile, TutorProfile):
pass
This is the model used as the settings.AUTH_USER_MODEL.
Overall, the advantages I see:
Single DB call on user edit/view page.
Easier to think about overall.
Disadvantage : Lots of wasted space.
Anyone has any better suggestions?

Django Admin dynamic drop down list from list_values

I have the following models:
class Name(models.Model):
device_type = models.CharField('Device Type', max_length=30, blank=True, null=True)
class Device(models.Model):
DEVICE_TYPE_CHOICES = (
('Router', 'Router'),
('Switch', 'Switch'),
('Firewall', 'Firewall'),
('Load Balancer', 'Load Balancer'),
)
device_name = models.CharField('Device Name', max_length=100)
device_type = models.CharField('Device Type', max_length=20, blank=True, null=True, choices=DEVICE_TYPE_CHOICES)
Given the above, right now when I create a new object using my Device model, choices for device_type are defined statically using the Field.choices method and this shows up in Django Admin as a drop down list with the four choices shown.
What I really want is to have that list of choices defined dynamically based on the following concept which basically says "from the Name model/table return a list of all values found in the 'device_type' column":
Name.objects.all().values_list('device_type')
I'm just not sure how to put this into practice. I'm not sure how to take the list which I already know how to get from my database and incorporate it into the Field.objects method so that those items show up as choices in my drop down menu.
Can anyone point me in the right direction? Thanks in advance for any assistance.
UPDATE AFTER FOREIGN KEY FIX:
Now my models look like this:
class Name(models.Model): # device type naming standards
device_type = models.CharField('Device Type', max_length=30, blank=True, null=True)
device_alias = models.CharField('Device Alias', max_length=30, blank=True, null=True)
def __unicode__(self):
return self.device_type
class Device(models.Model):
location_name = models.ForeignKey('Location')
device_name = models.CharField('Device Name', max_length=100)
device_type = models.ForeignKey('Name')
#device_type = models.CharField('Device Type', max_length=20, blank=True, null=True, choices=DEVICE_TYPE_CHOICES)
Now you'll see in my Device model I have two ForeignKeys. I need Location to be a ForeignKey because I want to put Devices categorically under a specific Location. I need the device type ForeignKey per my previous question. When I have two ForeignKeys like this, when I go to my Device table in Django Admin, I see no devices (even though they were in the table before I created the device_type ForeignKey. If I comment out the device_type ForeignKey and uncomment out the last line to go back to what I had before (after updating the schema with 'makemigrations' and 'migrate', now the devices show up again. Obviously I need the devices to show up in my device table. Am I allowed to have multiple ForeignKeys? I'm sure I'm not understanding something critical here.
If you will use ForeignKey, you'll get the list of name devices
device_type = models.ForeignKey('Name')
def __unicode__(self):
return self.device_type
Hope it help you.

Django Multi-Table Inheritance VS Specifying Explicit OneToOne Relationship in Models

Hope all this makes sense :) I'll clarify via comments if necessary. Also, I am experimenting using bold text in this question, and will edit it out if I (or you) find it distracting. With that out of the way...
Using django.contrib.auth gives us User and Group, among other useful things that I can't do without (like basic messaging).
In my app I have several different types of users. A user can be of only one type. That would easily be handled by groups, with a little extra care. However, these different users are related to each other in hierarchies / relationships.
Let's take a look at these users: -
Principals - "top level" users
Administrators - each administrator reports to a Principal
Coordinators - each coordinator reports to an Administrator
Apart from these there are other user types that are not directly related, but may get related later on. For example, "Company" is another type of user, and can have various "Products", and products may be supervised by a "Coordinator". "Buyer" is another kind of user that may buy products.
Now all these users have various other attributes, some of which are common to all types of users and some of which are distinct only to one user type. For example, all types of users have to have an address. On the other hand, only the Principal user belongs to a "BranchOffice".
Another point, which was stated above, is that a User can only ever be of one type.
The app also needs to keep track of who created and/or modified Principals, Administrators, Coordinators, Companies, Products etc. (So that's two more links to the User model.)
In this scenario, is it a good idea to use Django's multi-table inheritance as follows: -
from django.contrib.auth.models import User
class Principal(User):
#
#
#
branchoffice = models.ForeignKey(BranchOffice)
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalcreator")
modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalmodifier")
#
#
#
Or should I go about doing it like this: -
class Principal(models.Model):
#
#
#
user = models.OneToOneField(User, blank=True)
branchoffice = models.ForeignKey(BranchOffice)
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalcreator")
modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalmodifier")
#
#
#
Please keep in mind that there are other user types that are related via foreign keys, for example: -
class Administrator(models.Model):
#
#
#
principal = models.ForeignKey(Principal, help_text="The supervising principal for this Administrator")
user = models.OneToOneField(User, blank=True)
province = models.ForeignKey( Province)
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(User, editable=False, blank=True, related_name="administratorcreator")
modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="administratormodifier")
I am aware that Django does use a one-to-one relationship for multi-table inheritance behind the scenes. I am just not qualified enough to decide which is a more sound approach.
I'd like to expand on the solution by #thornomad.
Extending Django's User class directly can cause all kinds of trouble with the internal django.auth mechanisms. What I've done in a similar situation is precisely what #thornomad suggests - I made my own UserProfile model linked one-to-one with the Django User model, in which I held additional user data and from which I inherited models for different types of users.
Something to fit what you described:
class UserProfile(models.Model):
user = models.OneToOneField(User, blank=True, related_name='profile')
class Meta:
abstract = True
class PositionHolderUserProfile(UserProfile):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(PositionHolderUserProfile, editable=False, blank=True, related_name="created_users")
modified_by = models.ForeignKey(PositionHolderUserProfile, editable=False, blank=True, related_name="modified_users")
class Principal(PositionHolderUserProfile):
branchoffice = models.ForeignKey(BranchOffice)
class Administrator(PositionHolderUserProfile):
superior = models.ForeignKey(Principal, related_name="subordinates")
province = models.ForeignKey(Province)
class Coordinator(PositionHolderUserProfile):
superior = models.ForeignKey(Administrator, related_name="subordinates")
class Company(UserProfile):
name = models.CharField(max_length=50)
class Product(models.Model):
name = models.CharField(max_length=50)
produced_by = models.ForeignKey(Company)
class Buyer(UserProfile):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
products_bought = models.ManyToManyField(Product)
I recently switched over to using models that inherit off of contrib.auto.models.User. My general observation is that in theory they're great, but sometimes they don't get auto-magically handled like they're supposed to.
I think your decision regarding inheritance vs. OneToOne comes down to this:
Do I want to have Django automatically do something right 95% of the time, and need to debug that other 5%
-OR-
Do I want to do something manually myself 100% of the time
If you haven't seen it, the Scott Barham blog has a great post about inheriting off of User, and also building a custom back end to make sure that your custom object is being returned -- Extending the Django User.
Additionally of interest would be the AutoOneToOne field provided by django-annoying. It's sort of a hybrid of the two approaches here -- there's no inheritance taking place, but Django is taking care of creating the matching OneToOneField if it's not present.
Also, thornomad does make a good point about the redundancy in your models. You could easily implement an abstract class to clean that up as so (assuming you're doing manual OneToOne):
class BaseExtendedUser(models.Model):
user = models.OneToOneField(User, blank=True, related_name='profile')
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(User, editable=False, blank=True, related_name="created_users")
modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="modified_users")
class Meta:
abstract = True
class Administrator(BaseExtendedUser):
province = models.ForeignKey(Province)
class Principal(BaseExtendedUser):
branchoffice = models.ForeignKey(BranchOffice)
I don't think I would inherit the User model, rather use a custom UserProfile - leaving the contrib.auth model alone. With the custom UserProfile model, you could setup a base user profile model that can be a part of all your different user types.
Just looking at it quickly, too, I would look carefully at any models that repeat all the same fields (like your last two Principle and Administrator models). Combining the built in group functionality with the user profile idea may do what you are looking for.
Please consider what happens in the data model when a Coordinator gets promoted to a Principal. I would not use inheritance in this case at all. Please reconsider the previous poster's suggestion "Combining the built in group functionality with the user profile idea may do what you are looking for."
Do you need objects of your user classes to act like an auth.User anywhere? That would be the most obvious reason to use inheritance over OneToOne. One pro of the OneToOne approach would be the ease in which you can change over to another User model, if that's a concern.
The real issue I see with what you have above (by either method) is that there doesn't appear to be anything stopping you from having a Principal object and an Administrator object share the same User. OneToOneField can only guarantee a one-to-one mapping between any two relations.