Using Django we have two types of Users (Teachers and Students) with common fields and uncommon fields
In our wizard we first POST the common fields to /users with an extra type_field
Every operation after this should be able to figure out which model (Teacher or Student) it needs to use.
We are thinking of making two models ( Teacher and Student ) with an one-to-one field.
But how do we hookup the type_field to the right Model on every operation?
You dont have to go for an extra field since you are already having two different classes for students and teachers. A simple approach may looks like below.
from django.contrib.auth.models import User
class Teacher(User):
extra_field_1 = models.Fieldtype()
...
...
class Student(User):
extra_field_1 = models.Fieldtype()
...
...
You can provide both type of users same registration form and upon clicking next take them to next page based on the value of I am a teacher/student field. In that case I suggest you to use atomic blocks if you dont want to save data in case registration procedure fails at some point or user have selected a wrong choice and they want to go back. By this approach each inherited models have username, first_name, last_name and email that you dont have to insert any of these to Teacher or student model.
Then you have to create forms for each model. You may use modelform A much better approach will be using class based views since that reduce a lot of code and stick to dry principles.
You may use something like:
class Person(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
# more common fields
class Student(Person):
specific_field_to_student = models.CharField(max_length=255)
# more fields
class Teacher(Person):
specific_field_to_teacher = models.CharField(max_length=255)
# more fields
In your database you will have 3 tables (yourapp_person, yourapp_student and yourapp_teacher). Now, if type_field value is student, you will use Student model to create user, if it is teacher, you will use Teacher model.
Note: You may need to make Person model above a subclass of the built-in User model.
Edit:
I have edited the model above to take into account the requirements in the comments below.
Now, to retrieve user by id, you can use the following code in your view:
user = Person.objects.get(id=id) # id is the view param
if hasattr(user, 'student'):
print("Student")
else: # hasattr(user, 'teacher')
print("Teacher")
Related
Sorry for the dumb question but I can't figure it out.
I have a class based view for a form. I like to make some changes if the request.user is equal with something.
I used before in some other views request.user.profile.leader that gives me a boolean answer. Thats OK.
Now in this class based view I like to use a very similar stuff but with another model like this: request.user.trust.group but it gives me nothing. What am I doing wrong?
If you haven't customized your user model, then profile will appear on it as a reverse descriptor of an one-to-one field on another model (by way of related_name having been set or inferred), i.e. you have something like
class Profile(Model):
user = models.OneToOneField(User, related_name="profile")
leader = models.BooleanField(...)
somewhere.
If you expect a trust field to be there, then you'd need something similar:
class Trust(Model):
user = models.OneToOneField(User, related_name="trust")
group = ...
On the other hand, if you do have an entirely custom user model, then those attributes could appear directly on it:
class CustomUser(AbstractBaseUser):
profile = models.ForeignKey(Profile, ...)
trust = models.ForeignKey(Trust, ...)
I have built a project with a React frontend and a Django Rest Framework (DRF) API for my backend. Things are going well but I am quite curious about best practices for data saved against your models.
As an example, I have a user model with first_name and last_name fields. There is a one-to-one relationship between my user and the two user domains (let's say Buyer and Seller as an example). In addition, we have a foreign key relationship for Seller on something like a Product.
On my UI, I have tables that display the data and in general, the majority of tables display the user's full name (first_name + last_name).
Generally, all table fields I will want to filter by and order by via API (eg. {base_url}/product/?ordering=full_name).
I decided that I wanted the data returned from the REST API to represent the table data so I am now returning full_name by augmenting the serializer of Buyer and Seller to have full name using a SerializerMethodField like so:
full_name = serializers.SerializerMethodField()
...
def get_full_name(self, obj) -> str:
return obj.user.get_full_name()
However, I will also need to do this in all places where I want to show the Buyer/Seller where they are referenced by a Foreign Key.
So let's say we need it in the Product, I would have something like:
seller_first_name = serializers.CharField(source='seller.user.first_name')
seller_last_name = serializers.CharField(source='seller.user.last_name')
seller_full_name = serializers.SerializerMethodField()
...
def get_seller_full_name(self, obj) -> str:
return obj.seller.user.get_full_name()
I guess I'm curious if it would be best to store all of these fields directly on the user object (first_name, last_name, and full_name). There is clearly some duplication here, so I assume Django would have a way for me to auto-write full_name based on first_name and last_name without requiring it to be passed in the API.
I'm sure it can be done either way but this is my first project using DRF as a backend and I would rather hear some other's experience now rather than learn myself in a year and have to do a large refactoring later on.
Thanks for any advice in advance.
All the best,
Brandon
You can define a property in your model:
class YourModel(models.Model):
...
#property
def full_name(self):
return f'{self.first_name} {self.last_name}'
Then you can access this in any serializer using source keyword argument:
full_name = serializers.CharField(source='YourModel.full_name')
Here's the doc
I dont understand Django Intermediary models at all. Let's say that i want to add one more additional field to User model, and store relationship between two users in another model(intermediary) like this:
class Contact(models.Model):
user_from = models.ForeignKey(User...)
user_to = models.ForeignKey(User...)
...
And the field i want to add is:
following = models.ManyToManyField('self',
through=Contact,
related_name='followers',
symetrical=False)
What really happen when i call user_object.followers.all() and what is the difference between user_object.following.all() are they both the same?
In my project I have to deal with different type of users e.g. costumers and employers. Each type of user have its own fields and permissions: costumer can buy things whereas employer can not.
I have read the Django docs and it looks like there are two options:
Costumize the AbstractUser class and adding all the fields of costumers and employers. Then use the permission system to grant/revoke permission or create a group for each type of user. The downside here is that there unused fields.
Adopt the proxy model:
from django.contrib.auth.models import User
from django.db import models
class Costumer(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
class Meta:
db_table = 'costumers'
permissions = (
("access_this", "User may access this"),
)
ordering = []
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
class Meta:
db_table = 'employers'
permissions = (
("access_that", "User may access that"),
)
ordering = []
This case seems more reasonable but I don't know how to deal with the permissions. Consider I'd like to use #permission_required instead of checking the type (if the user has a specific field) because it seems more legit for Django system.
So in the end, what is the best way to approach such scenario?
The first solution is much better.
The downside here is that there unused fields.
I disagree with this, you don't have to store all the fields within the User model. Also, if you're talking about 5 fields for example, that doesn't really matter.
You can extend AbtractUser and also use some composition; you don't have to put all the fields there:
class User(AbstractUser):
email = ...
...
# for the employer, let's say you want to save the company details
company = models.ForeignKey('myapp.Company', null=True, blank=True)
...
# for the customer
customer_details = models.ForeignKey('...')
This way you could record a user_type if you want or deduce the type from the foreign key (if there is a company, it's an employer).
To help you more with the model, I need to know what differentiate an employer from a customer in your application. Note that with that solution, an instance of User could be both.
Concerning the permissions, I feel like it's a separate problem; I'd recommend you to sort it last. If the design you pick is close to the reality and you get the features working; adding custom permissions will be really easy.
I am creating a Django app and this questions comes to hit me again (I've already run into it but I resolved it in a way I don't think is correct).
So I have these models:
def Person(models.Model):
name = models.CharField(max_length=30)
status = models.ManyToManyField(Status)
def Status(models.Model):
name = models.CharField(max_length=30)
Any person may have up to 3 status (or none). And this was supposed to be a string array containing only a few possible status. e.g. happy, lucky, strong.
However a CharField with choices would not be possible here, as one person may feel happy and strong at the same time.
Is my current approach the best one possible?
You will need to validate the statuses count for each person in the clean method.
from django.db import models
from django.core.exceptions import ValidationError
class Status(models.Model):
name = models.CharField(max_length=30)
class Person(models.Model):
name = models.CharField(max_length=30)
status = models.ManyToManyField(Status)
def clean(self):
# check person status count
if self.status.all().count() > 3:
raise ValidationError("Person can not have more than 3 statuses")
Update
Since it's a ManyToMany relationship, you will never get this validated during the creation of the object, unless you have a separate form for adding statuses for persons.
If you have statuses field in the same form of person creation, then this check MUST be in the form.
class PersonForm(forms.ModelForm):
class Meta:
model = Person
def clean(self):
statuses = self.cleaned_data.get('status')
if status.count() > 3:
raise ValidationError("Person can not have more than 3 statuses")
Why this method
This design of models will allow you to have many different ways of querying. such as get people who are happy! or count of people who are sad and finally get people with similar statuses
Just a quick addition - if you're using PostgreSQL (and that's probably the best decision) you could also use an Array Field, for which you can specify a max size.
https://docs.djangoproject.com/en/1.8/ref/contrib/postgres/fields/
Another option is to use a tagging library like Django-taggit