Django - extended user model not saving - python

I'm having trouble attempting to save data from my extended user model.
model.py
class Person(models.Model):
"""
The 'user' field creates a link between the django-registration-redux's
default user and allows it to be extended through this model.
"""
user = models.OneToOneField(User)
# Person attributes
address = models.CharField(max_length=50)
town = models.CharField(max_length=50)
postcode = models.CharField(max_length=10)
phone_no = models.CharField(max_length=10, unique=True)
# Meta contains information about the class which is not a field.
class Meta:
abstract = True
class Customer(Person):
# No extra attributes required for Customer.
def __str__(self):
return self.user.username
class Staff(Person):
# Staff attributes
job_role = models.CharField(max_length=50)
medical_contact = models.CharField(max_length=50)
nation_insurance = models.CharField(max_length=50, unique=True)
def __str__(self):
return self.user.username
forms.py
class UserProfileForm(RegistrationFormUniqueEmail):
address = forms.CharField(max_length=50)
town = forms.CharField(max_length=50)
postcode = forms.CharField(max_length=7)
phone_no = forms.CharField(max_length=10)
class Meta:
model = User
fields = ['username', 'first_name', 'last_name', 'email', 'password1', 'password2']
regbackend.py
class CustomRegistrationView(RegistrationView):
form_class = UserProfileForm
def register(self, form_class):
new_user = super(CustomRegistrationView, self).register(form_class)
user_profile = Customer()
user_profile.user = new_user
user_profile.address = form_class.cleaned_data['address']
user_profile.town = form_class.cleaned_data['town']
user_profile.postcode = form_class.cleaned_data['postcode']
user_profile.phone_no = form_class.cleaned_data['phone_no']
user_profile.save()
return user_profile
So I need to save attributes of Customer through the registration form which is in the django-registration-redux app. However with my current code it will only save "User" attributes. When I attempted to change the forms model to "Customer" it won't save "User" attributes.

It's because User is a OnetoOnefield, so you need to create the user, save it and then add it to your Customer object and save it.
You need to do something like that in your forms.py, redefine the save method:
def save(self, commit=True):
user = user.super(UserProfileForm, self).save()
customer = Customer(user=user)
customer.save()
Don't juste copy paste, it's just you to know you have first to register the user, and then add it to your new object, add the other fields, and save it.

Related

Could I use 'set_password' method in DRF without AbstractUser in my model?

I'm newbiew with DRF (and programming) and a big problem is with me.
I have this Model:
class Player (models.Model):
user_id = models.UUIDField (primary_key=True, default=uuid.uuid4, editable=False, blank=True)
username = models.CharField (max_length=30, unique=True, null=False)
email = models.EmailField (max_length=50, unique=True, null=False)
password = models.CharField (max_length=100, null=True)
is_validated = models.BooleanField (default=False)
created = models.DateField (auto_now_add=True)
updated = models.DateField (auto_now=True)
class Meta:
verbose_name = 'player'
verbose_name_plural = 'players'
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = [
'username'
]
def __str__ (self):
return self.username
And this Serializer:
class PlayerSerializer(serializers.ModelSerializer):
class Meta:
model = Player
fields = ('username', 'email', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = Player(
email = validated_data["email"],
username = validated_data["username"]
)
user.set_password(validated_data["password"])
user.save()
return user
def validate_password (self, value):
if len (value) < 8 or len (value) > 30:
raise serializers.ValidationError('Password length must be between 8 and 30.')
return value
When I create a new player, always have the same issue:
'Player' object has no attribute 'set_password'
How could I use 'set_password' without import AbstractUser?
I've tried extending the User model with OnetoOnelink but it didn't work.
I think what you should do here is write a python function that saves a player's password in the models.py file, then instead of calling set_password you can call the function you've created.
Hello i think the issue here is that the Player model doesn't have a defined method called set_password, unlike the defualt django User model and AbstractUser that have that method inbuilt.
You have to define a custom set_passowrd method

Django Rest - Serializer: must be a instance on create

I'm trying to create create a nested serializer using the Django Rest framework. The relationship is Profile X User but when i use Profile.objects.create(user=profile, **user_data) i get ValueError: Cannot assign "<Profile: Profile object (7)>": "Profile.user" must be a "User" instance..
This should be some rookie misunderstanding of models relationship definitions or the serializer declaration itself but I can't find anything around the docs. If someone can point me a direction I'll be gracefull.
models.py
class User(models.Model):
name = models.CharField(max_length=100, blank=False)
email = models.CharField(max_length=100, blank=True, default='')
password = models.CharField(max_length=100, blank=True, default='')
timestamp = models.DateTimeField(default= timezone.now)
class Meta:
ordering = ['timestamp']
class Profile(models.Model):
# choices [...]
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
profile_type = models.CharField(max_length=2,choices=PROFILE_CHOICES,default=TEAMMEMBER)
authentication_token = models.CharField(max_length=100, null=True)
avatar_url = models.CharField(max_length=100, default='')
permissions = models.CharField(max_length=100, null=True)
timestamp = models.DateTimeField(default= timezone.now)
class Meta:
ordering = ['timestamp']
serializer.py
class UserSerlializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['name', 'email', 'password']
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerlializer()
class Meta:
model = Profile
fields = ['user', 'profile_type']
def create(self, validated_data):
user_data = validated_data.pop('user')
profile = Profile.objects.create(**validated_data)
Profile.objects.create(user=profile, **user_data)
return Profile
POST
{
"profile_type" : "ST",
"user": {
"name" : "test",
"email" : "test#test.com",
"password" : "123456"
}
}
You are creating instances in wrong way. Change your create(...) method as,
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerlializer()
class Meta:
model = Profile
fields = ['user', 'profile_type']
def create(self, validated_data):
user_data = validated_data.pop('user')
user_instance = User.objects.create(**user_data)
profile_instance = Profile.objects.create(user=user_instance, **validated_data)
return profile_instance
Profile.user should beUser instance, but you are assigning Profile instance.
Change your create method to this:
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerlializer()
class Meta:
model = Profile
fields = ['user', 'profile_type']
def create(self, validated_data):
user_data = validated_data.pop('user')
profile = Profile.objects.create(**validated_data)
user = User.objects.create(**user_data) # 1. creating user
profile.user = user # 2. assigning user
profile.save() # 3. saving profile after adding user
return profile # returning Profile instance.
inherit your user model from django contrib auth module also, and make a one to one relation with profile
from django.contrib.auth.models import User

radio buttons incorrectly written to the database in django

I've a registration form, where user must chose one of 2 options.
Django renders all correctly, django admin also have it ok, but db records all possible choices as value.
forms.py
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email','password1','password2']
class UserProfileForm(forms.ModelForm):
terms_compliance = forms.BooleanField(label=mark_safe('I agree with terms and conditions '))
class Meta:
model = UserProfile
widgets = {'role': forms.RadioSelect}
fields = ('role','terms_compliance')
def __init__(self):
self.fields['terms_compliance'].initial = True
models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
role_choices = [('publisher','Publisher'), ('advertiser','Advertiser')]
role = models.CharField(max_length=15, choices=role_choices, default=None)
terms_compliance = models.BooleanField()
def __str__(self):
return self.user.username
In new instance (which is user.userprofile.role_choices) I need advertiser or publisher, but all I have is: [('publisher','Publisher'), ('advertiser','Advertiser')]
If you want to provide choices in a Database Field. Do like this:
class UserProfile(models.Model):
class RoleChoice(ChoiceEnum):
PUBLISHER = 'Издатель'
ADVERTISER = 'Рекламодатель'
user = models.OneToOneField(User, on_delete=models.CASCADE)
role = models.CharField(max_length=15, choices=RoleChoice.choices(), default=None)
terms_compliance = models.BooleanField()
def __str__(self):
return self.user
In Views.py, populate the DB like this.
For example:
...
choice = request.query_params.get('choice') or UserProfile.RoleChoice.PUBLISHER.value
...
For more details read from here: https://django-mysql.readthedocs.io/en/latest/model_fields/enum_field.html

How to create multiple objects along with a foreignkey whose foreignkey field is provided?

class User(models.Model):
email = models.EmailField()
class Lawyer(models.Model):
user = models.OnetoOneField(User)
class Session(models.Model):
lawyer = models.ForeignKey(Lawyer)
name = models.TextField()
class SessionSerializer(serializers.ModelSerializer):
email = serializers.EmailField()
class Meta:
model = Session
fields=['name', 'lawyer','email']
I do not have the lawyer id in the request , my request contains only email and name of the session.
I need to create multiple objects with session serializer but how to save the lawyer by using the email that is passed?
all you need is to create the Lawyer, also you should set allow null to true for the laweyer field.
class SessionSerializer(serializers.ModelSerializer):
email = serializers.SerializerMethodField()
lawyer = serializers.IntegerField(allow_null=True, required=False)
fields=['name', 'lawyer','email']
def create(self, validated_data):
user = User.objects.create(email=validated_data.get('email'))
lawyer = Lawyer.objects.create(user=user)
name = validated_data.get('name')
instance = Session.objects.create(lawyer=lawyer, name=name)
return instance

Getting attribute error when trying to access the nested serializer in Django Rest Framework

I am getting following error while using the PostSerializer:
Got AttributeError when attempting to get a value for field
full_name on serializer UserSerializer. The serializer field might
be named incorrectly and not match any attribute or key on the long
instance. Original exception text was: 'long' object has no attribute
'full_name'.
Serializers are as follows:
class PostSerializer(serializers.ModelSerializer):
author = UserSerializer(required=False, allow_null=True)
class Meta:
model = Post
fields = ('id', 'author', 'message', 'rating', 'create_date', 'close_date',)
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'full_name',)
View:
class PostMixin(object):
model = Post
serializer_class = PostSerializer
permission_classes = [
PostAuthorCanEditPermission
]
queryset = model.objects.all()
def pre_save(self, obj):
"""Force author to the current user on save"""
obj.author = self.request.user
return super(PostMixin, self).pre_save(obj)
class PostList(PostMixin, generics.ListCreateAPIView):
pass
User model:
class User(AbstractBaseUser):
email = models.EmailField(unique=True)
username = models.CharField(max_length=40, unique=True, null=True)
full_name = models.CharField(max_length=50, blank=False)
phone = models.CharField(max_length=20, unique=True, null=True)
about = models.CharField(max_length=255, blank=True)
type = models.CharField(max_length=1, default='U')
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(auto_now_add=True)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['full_name']
def __unicode__(self):
return self.email
def get_full_name(self):
return self.full_name
def get_short_name(self):
return self.full_name
Problem
Got AttributeError when attempting to get a value for field full_name on serializer UserSerializer.
The model User in Django has no such field called full_name.
There is though a method get_full_name() that does what you want.
Solution
So try using it through a SerializerMethodField
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username') # no full_name here
full_name = serializers.SerializerMethodField('get_full_name')
This will add a field called full_name to your serialized object, with the value pulled from User.get_full_name()
Check you are using your custom model and not Django's User model
You've customized your own User model, but since that models has full_name, you shouldn't have gotten that error in the first place, so double check you are not referencing Django's default User model first.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User # <--- Make sure this is your app.models.User,
# and not Django's User model
fields = ('id', 'username', 'full_name',) # This is OK on your User model
or just
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'first_name', 'last_name')

Categories

Resources