I'm trying to create a custom class Employee that uses the default authentication. I am able to successfully register but the fields associated with Django's User class are stored in User while my custom fields are stored in Employee. How can I get everything stored in Employee?
This is my forms.py:
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'password')
class EmployeeForm(forms.ModelForm):
class Meta:
model = Employee
fields = ('sales', 'hours')
And my models.py:
class Patient(models.Model):
user = models.OneToOneField(User)
sales = models.CharField(max_length=30)
hours = models.CharField(max_length=30)
Like I said, I would prefer to use the default authentication and therefore avoid using a custom backend
What you're using is generally called a User Profile which will not store the added fields in the same table (which you have found out). MOre info on that can be found in Extending existing user model.
What you're wanting to do (and the method that I typically need to use in my projects) is Substitute a custom user model. It is a bit more complicated, but once you get the hang of it it's not too bad. It will allow you to store all your User fields in one table instead of in separate tables/models.
Related
I am using Django rest framework for a project. In the project, there is a Student model, which has a foreign key referring to a user object.
class Student(models.Model):
user = models.OneToOneField(
User,
on_delete=models.CASCADE
)
When I read from Student, I would also like to get all the fields of the nested user. I can do that with nested serializers:
class StudentSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
class Meta:
model = Student
fields = '__all__'
However, if I want to create a new user I cannot use the same serializer, because there is no way to pass a foreign key to the user field, and Django does no support nested create.
I am currently using an additional field user_id = serializers.IntegerField(write_only=True) to get the foreign key for write, and customized the create method to handle the logic. I also tried using two different serializers for creating and fetching data. Both way worked, but I am just wondering if there is a more intuitive way to implement this? A more convenient or standard way perhaps? Is there some syntax that works like: if read: user = UserSerializer() that avoids creating two different things for the same serializer field under different conditions?
i think u should follow docs
https://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers
class StudentSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Student
fields = '__all__'
def create(self, validated_data):
user_data = validated_data.pop('user')
validated_data["user"] = User.objects.create(user_data)
student = Student.objects.create(**validated_data)
return student
You can also think about doing it in one transaction.
https://docs.djangoproject.com/en/4.0/topics/db/transactions/
I've been going back and forward between two tutorials on creating custom user models:
https://simpleisbetterthancomplex.com/tutorial/2018/01/18/how-to-implement-multiple-user-types-with-django.html
and https://wsvincent.com/django-tips-custom-user-model/
So far here is my code:
Model:
class CustomUser(AbstractUser):
is_admin = models.BooleanField('admin status', default=False)
is_areamanager = models.BooleanField('areamanager status', default=False)
is_sitemanager = models.BooleanField('sitemanager status', default=False)
Form:
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = CustomUser
class CustomUserChangeForm(UserChangeForm):
class Meta(UserChangeForm.Meta):
model = CustomUser
Admin:
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
list_display = ['email', 'username',]
admin.site.register(CustomUser, CustomUserAdmin)
I have hit a bit of a wall at this point. I'm not sure what direction to go in with restricting content to users. My general idea is I want admins to access everything, area managers to have the next level of access, site manager after that, then regular users (false on all boolean checks) to have base privileges.
Is this the best route to go for this kind of implementation? Where should I go from here and why?
Don't extend the AbstractUser, user Django built-in groups and permissions to create class of users with different privileges:
https://docs.djangoproject.com/en/3.0/topics/auth/default/#groups
If you need to add more info to you user, a common pattern is to create a UserProfile:
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
address = models.CharField(max_length=140)
age = ...
A couple of suggestions:
Use the AbstractUser just in case of specific use cases (for example when you need to customize the AuthenticationBackend)
Use the user ID and not the user profile id as FK in models (it is more easy to retrieve it from requests)
For basic use cases just adding a 'role' field to the UserProfile is enough for implementing a simple logic
I am trying to add some custom fields to a user, and extend the UserCreationForm so that I can add these fields when the user is created. I am following the docs but when I try to load the page to create a user I get an error: Unknown field(s) (username) specified for Customer.
The docs that I am following: Custom User and Auth Forms
models.py
class User(AbstractUser):
is_restaurant = models.BooleanField(default=False)
is_customer = models.BooleanField(default=False)
class Customer(models.Model):
user = models.OneToOneField(User, primary_key=True, on_delete=models.CASCADE)
address = models.CharField(max_length=200)
def __str__(self):
return self.user.get_full_name()
forms.py
class CustomerSignUpForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = Customer
fields = UserCreationForm.Meta.fields + ('address',)
I understand that username is not part of the Customer class, but the docs appear to be doing the same thing...
The doc says:
If your custom user model is a simple subclass of AbstractUser, then
you can extend these forms in this manner...
In other words this will work only in case you want to add to the form is_restaurant or is_customer fields:
class CustomerSignUpForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = User
fields = UserCreationForm.Meta.fields + ('is_restaurant',)
But in your case Customer is not subclass of AbstractUser, since this method is not working for you. As a workaround you can try to work with two separate forms in the same time as suggested in this answer.
I'm using Django 1.6.8, and Django Rest 2.4.4. I have a Person model with separate models for Address and PhoneNumbers.
class Person(models.Model):
address = models.OneToOneField(Address, blank=True, null=True)
phoneNumbers = models.ManyToManyField(PhoneNumber, blank=True)
class Address(models.Model):
address = models.CharField(max_length=50)
city = models.CharField(max_length=50)
state = models.CharField(max_length=2)
class PhoneNumber(models.Model):
number = models.CharField(max_length=15)
numberType = models.CharField(default='Mobile', max_length=15)
I'm using nested serializers in Django REST framework.
class PersonSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.Field('id')
address = AddressSerializer(source='address')
phoneNumbers = PhoneNumberSerializer(many=True)
This works for GET (I get address and phone numbers as nested json fields), but I need the same for PUT / POST / PATCH. Specifically, for each phone number, I want it to be updated if the id is specified, or created if there is no id in json. And the same for address, all in the same API call.
You need to implement your own create() and / or update() methods to support this.
It's explained in the Django Rest doc and it's available since Django Rest 3.0:
The following example demonstrates how you might handle creating a user with a nested profile object.
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ('username', 'email', 'profile')
def create(self, validated_data):
profile_data = validated_data.pop('profile')
user = User.objects.create(**validated_data)
Profile.objects.create(user=user, **profile_data)
return user
(...)
Because the behavior of nested creates and updates can be ambiguous, and may require complex dependancies between related models, REST framework 3 requires you to always write these methods explicitly. The default ModelSerializer .create() and .update() methods do not include support for writable nested representations.
I have a problem with the CustomUser Model. Here is the model:
class CustomUser(models.Model):
department = models.ManyToManyField(Department)
game = models.ManyToManyField(Game)
user = models.OneToOneField(User)
In my user registration form, I want to have fields of User plus the Game and Department fields. Here's my form as of now:
class MyRegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
first_name = forms.CharField(max_length = '200')
last_name = forms.CharField(max_length = '200')
class Meta:
model = User
fields = ('first_name', 'last_name', 'username', 'email')
I used User as my Model, so clearly it does not have the Game and Department fields as of now. At the same time, if I use CustomUser as a model in my form, I do have all the fields I need, but when I click the register button, an error: "CustomUser does not have a set_password attribute" appears.
Also, in Setting I have this:
CUSTOM_USER_MODEL = 'logins.CustomUser'
So how can I make the CustomUser Model have the attributes of User and have those fields appear in my form?
Thanks in advance!
When creating your own custom user model, it's usually best to subclass the AbstractBaseUser which includes those default fields and will generally make the process easier on you. OneToOne relationship to the default User model is more about expnding the built-in User model, which doesn't sound like what you're looking for. Read more about it here
As Daniel mentioned in the comments, this means you're going to have to explicitly include the username field (and you obviously have the freedom to define it as you'd like, it just has to be unique). In the example below I set up email to be the username
from django.contrib.auth.models import AbstractBaseUser
class CustomUser(AbstractBaseUser):
department = models.ManyToManyField(Department)
game = models.ManyToManyField(Game)
# user = models.OneToOneField(User)
email = models.EmailField(max_length=255, unique=True, db_index=True)
USERNAME_FIELD = 'email'
Now you need to include the password field in your form, and please look at the example given in the documentation, they do a better job explaining than I do.