I am trying to update UserDetailsSerializer and the problem is when I run my code in my test model it works but when I use it in my actual app, it throws this error: 'User' object has no attribute 'userprofile'
model.py
class userProfileModel(models.Model):
GENDER = [
('', ""),
('M', "Male"),
('F', "Female")
]
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='userprofile', default='')
avatar = models.ImageField(upload_to='avatar/', default='avatar/no-avatar.png')
age = models.DateField(auto_now_add=True)
gender = models.CharField(max_length=10, choices=GENDER, default='')
address = models.CharField(max_length=500, default='')
longitude = models.FloatField(default=0.0)
latitude = models.FloatField(default=0.0)
phone = models.IntegerField(default=0)
user_is_active = models.BooleanField(default=False)
def __str__(self):
return self.user.username
serializers.py
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = userProfileModel
fields = (
'id',
'avatar',
'age',
'gender',
'address',
'longitude',
'latitude',
'phone',
)
class UserDetailsSerializer(UserDetailsSerializer):
profile = UserProfileSerializer(source='userprofile')
class Meta(UserDetailsSerializer.Meta):
fields = UserDetailsSerializer.Meta.fields + ('profile',)
read_only_fields = ('',)
def update(self, instance, validated_data):
# User data
nested_serializer = self.fields['profile']
nested_instance = instance.userprofile
nested_data = validated_data.pop('userprofile')
nested_serializer.update(nested_instance, nested_data)
return super(UserDetailsSerializer, self).update(instance, validated_data)
The error:
RelatedObjectDoesNotExist at /rest-auth/user/
User has no userprofile.
It's because UserProfile instance for user is not created yet. You can use a signal on post_save of User model, so that whenever user is saved, check if UserProfile instance for it is created, if not then create one. like below:
def create_profile(sender,**kwargs ):
if kwargs['created']:
user_profile=UserProfile.objects.create(user=kwargs['instance'])
post_save.connect(create_profile,sender=User)
Also like my friend suggested in comments, Always check if userProfile instance is exist with hasattr(instance, 'profile'), if not then create a default one for that user.
Reference: This is a cool topic Extending user model
When you are creating user profile, make sure you save them, you can refer this snippet.
class Profile(models.Model):
# ...
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
And also don't forget to import post_save.
Related
I am trying to create a signal to create a profile for a newly created user. However, I need to have two different types of profiles: A customer profile and an employee profile. The type of profile which will be created is decided inside my User model via the "user_type" field:
user/models.py:
class User(AbstractBaseUser):
USER_TYPES = (
('Employee', 'employee'),
('Customer', 'customer'),
('Vendor', 'vendor')
)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
phone_number = models.CharField(max_length=20)
user_type = models.CharField(max_length=8, choices=USER_TYPES)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = MyUserManager()
USERNAME_FIELD = 'email'
def __str__(self):
return f'{self.first_name} {self.last_name} : {self.email}'
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True
def has_module_perms(self, app_label):
"Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True
#property
def is_staff(self):
"Is the user a member of staff?"
# Simplest possible answer: All admins are staff
return self.is_admin
# if user.is_employee == True
class EmployeeProfile(models.Model):
EMPLOYEE_ROLES = (
('Driver', 'driver'),
('Production', 'production'),
('Manager', 'manger')
)
user = models.OneToOneField(User, on_delete=models.CASCADE)
role = models.CharField(max_length=12, choices=EMPLOYEE_ROLES)
def __str__(self):
return str(self.user)
# if user.is_customer == True
class CustomerProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
company = models.CharField(max_length=100, null=True)
address = models.CharField(max_length=100, null=True)
address_2 = models.CharField(max_length=100, null=True)
city = models.CharField(max_length=50, null=True)
state = models.CharField(max_length=2, help_text="State Abbreviation (ex: OH)", null=True)
zipcode = models.CharField(max_length=5, null=True)
def __str__(self):
return str(self.user)
and then in my user/signals.py file:
#receiver(post_save, sender=User)
def create_customer_profile(sender, instance, created, **kwargs):
if created:
if User.user_type == 'customer':
CustomerProfile.objects.create(user=instance)
#receiver(post_save, sender=User)
def create_employee_profile(sender, instance, created, **kwargs):
if created:
if User.user_type == 'employee':
EmployeeProfile.objects.create(user=instance)
#receiver(post_save, sender=User, dispatch_uid='save_new_user_profile')
def save_profile(sender, instance, created, **kwargs):
user = instance
if created:
if User.user_type == 'customer':
customer_profile = CustomerProfile(user=user)
customer_profile.save()
if created:
if User.user_type == 'employee':
employee_profile = EmployeeProfile(user=user)
employee_profile.save()
When I create a user, no profiles are created. I was able to get a profile created by just using one Profile model and the standard signal:
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
... but ideally I want to have two separate profiles depending on the type of user that is created.
You can do this by overriding the save method from the User model.
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.user_type == 'customer':
CustomerProfile.objects.create(user=self)
elif self.user_type == 'employee':
EmployeeProfile.objects.create(user=self)
I trying to update the user profile but I can't get that what mistake I do here.
#forms.py
class AddStudentUserForm(forms.ModelForm):
class Meta:
model = User
fields = ["username", "first_name", "last_name"]
class AddStudentStaffForm(forms.ModelForm):
class Meta:
model = Staff
fields = ["gender", "dob", "mob", "img"]
#views.py
def editProfile(request, user, id):
if request.POST:
staffform = AddStudentStaffForm(request.POST or None, request.FILES or None, instance=request.user.staff_profile)
userform = AddStudentUserForm(data=request.POST, instance=request.user)
if staffform.is_valid():
staffform.save()
if userform.is_valid():
userform.save()
return redirect(profile,user)
return render(request, 'edit.html')
#models.py
class Staff(models.Model):
Male = 'Male'
Female = 'Female'
Other = 'Other'
GENDER_CHOICE = (
(Male, 'Male'),
(Female, 'Female'),
(Other, 'Other')
)
id = models.AutoField(primary_key=True)
admin = models.OneToOneField(User, related_name='staff_profile', on_delete=models.CASCADE)
gender = models.CharField(max_length=10, choices=GENDER_CHOICE, default=Male)
dob = models.DateField(auto_now_add=False, blank=True, null=True, default=None)
mob = models.IntegerField(blank=True, null=True, default=None)
img = models.ImageField(upload_to='staff/', blank="True", null="True")
objects = models.Manager()
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Staff.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.staff_profile.save()
def __str__(self):
return self.gender
I started to create a Student Management System project. But I faced some difficulties to develop it. I trying lot of to solve these problem but since I can't solve that.
Here if I want to update the Staff model fields like gender, dob, mob it will updated successfully but when I trying to update the User model fields like username, first_name, last_name it will not update. I think the if userform.is_valid(): condition couldn't run, but I can't get what mistake I do here.
You can debug why the form is not valid, try code below :
if userform.is_valid():
userform.save()
return redirect(profile,user)
else:
print userform.errors #To see the form errors in the console.
I am trying to update the user profile in django rest framework
So i am able to create the user profile simultaneously with user creation using signals
Now i am trying to update the profile created:-
//models.py
class User(AbstractBaseUser,PermissionsMixin):
phone_regex=RegexValidator(regex = r'^[6-9]\d{9}$',message='please enter the correct phonenumber')
#name_regex=RegexValidator(regex=r'/^[A-Za-z]+$/',message='Please enter the correct name')
phone=models.CharField(validators=[phone_regex],max_length=15,unique=True)
date_joined=models.DateTimeField(verbose_name='date joined',auto_now_add=True)
last_login=models.DateTimeField(verbose_name='last login',auto_now=True)
active = models.BooleanField(default=True)
staff = models.BooleanField(default=False) # a admin user; non super-user
admin = models.BooleanField(default=False)
first_login=models.BooleanField(default=True)
USERNAME_FIELD='phone'
REQUIRED_FIELDS = []
class UserProfile(models.Model):
# There is an inherent relationship between the Profile and
# User models. By creating a one-to-one relationship between the two, we
# are formalizing this relationship. Every user will have one -- and only
# one -- related Profile model.
GENDER = (
('M', 'Homme'),
('F', 'Femme'),
)
user = models.OneToOneField(
User,related_name="profile", on_delete=models.CASCADE
)
# Each user profile will have a field where they can tell other users
# something about themselves. This field will be empty when the user
# creates their account, so we specify blank=True.
email=models.EmailField(unique=True,validators=[validate_email],max_length=254,blank=True,null=True)
name=models.CharField(max_length=15,blank=True)
dateofbirth=models.DateField(auto_now=False, null=True, blank=True)
Gender=models.CharField(max_length=1, choices=GENDER,blank=True,null=True)
address1 = models.CharField(
"Address",
max_length=1024,
blank=True,
null=True
)
address2 = models.CharField(
"Society",
max_length=1024,
blank=True,
null=True
)
address3 = models.CharField(
"Landmark",
max_length=1024,
blank=True,
null=True
)
zip_code = models.CharField(
"ZIP / Postal code",
max_length=12,
blank=True,
null=True
)
city = models.CharField(
"City",
max_length=1024,
default ="Gurugram",
blank=True,
null=True
)
country = models.CharField(
"Country",
max_length=10,
default="India",
blank=True,
null=True
)
# In addition to the `bio` field, each user may have a profile image or
# avatar. This field is not required and it may be blank.
# A timestamp representing when this object was created.
created_at = models.DateTimeField(auto_now_add=True,blank=True)
# A timestamp representing when this object was last updated.
updated_at = models.DateTimeField(auto_now=True,blank=True)
def __str__(self):
return self.user.phone
#receiver(post_save, sender=User)
def create_profile_for_user(sender, instance=None, created=False, **kargs):
if created:
UserProfile.objects.get_or_create(user=instance)
// Serializer.py
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields=("name",)
class UserSerializer(serializers.ModelSerializer):
user = UserProfileSerializer(many=True)
class Meta:
model = User
fields = ('user', 'phone',)
def create(self, validated_data):
user_data = validated_data.pop('user')
user = User.objects.create(**user_data)
profile = user_data.objects.create(user=user, **validated_data)
return profile
def update(self, instance, validated_data):
print("update function")
# profile_data = validated_data.pop('profile')
# print(profile_data)
profile_data = validated_data.pop('user')
profile = instance.profile
# print("profile is ",profile)
print(profile_data.name)
print("name in validation ",validated_data['name'], " type is ",print(type(validated_data)))
instance.username = validated_data.get('name', instance.username)
instance.email = validated_data.get('email', instance.email)
instance.save()
print("name is ",instance.profile.name )
#profile.Gender = validated_data['Gender'],
#profile.email = validated_data['email'],
#profile.dateofbirth=validated_data['dateofbirth'],
#profile.address1=validated_data['address1'],
##profile.address2=validated_data['address2'],
#profile.address3=validated_data['address3']
# print("1",profile)
#print(profile.Gender)
#print(profile.email)
#print(profile.dateofbirth)
profile.save()
print("2",profile)
print(profile.name)
print("save done")
return instance
//views.py
class ProfileCreateAPIView(CreateAPIView):
def put(self, request, *args, **kwargs):
print("my print 0",request)
header_token = request.META.get('HTTP_AUTHORIZATION', None)
print(header_token)
access_token=header_token.split(' ')[1]
status,user =validate_token(access_token)
print(request.data)
user_profile_serializer = UserProfileSerializer(
instance=user,
data=request.data
)
user_serializer = UserSerializer(
instance=user,
data=request.data)
print("my print 1",user_profile_serializer.is_valid())
print("my print 2",user_serializer.is_valid())
if user_profile_serializer.is_valid() and user_serializer.is_valid():
user_profile_serializer.save()
print("user_profile save done")
user_serializer.save()
print("user save done")
return Response(
status=rt_status.HTTP_200_OK)
# Combine errors from both serializers.
# errors = dict()
# errors.update(user_profile_serializer.errors)
#errors.update(user_serializer.errors)
else:
return Response(status=rt_status.HTTP_400_BAD_REQUEST)
'''
return Response(status=rt_status.HTTP_400_BAD_REQUEST)
'''
Now the issue is when i am hitting the api I am getting user_serializer.is_valid() as TRUE but False from UserProfile.
I feel there is some issue with my getting the profile data.
But I am not able to understand how to resolve the issue .
Any help is much appreciated.
I would like to extend Django's user model using a OneToOneField in my Django api project, but I'm getting a weird error. I'm hoping someone can help me out. Below is my code and the error message.
models.py:
class Profile(models.Model):
yearOfExperience = models.PositiveIntegerField(default=1)
profession = models.CharField(max_length=250, blank=True, null=True)
dp = models.URLField(blank=True, null=True)
qualification = models.CharField(max_length=255, blank=True, null=True)
phoneNumber = models.CharField(max_length=255, blank=True, null=True)
#receiver(post_save, sender=CustomUser)
def create_user_profile(sender, instance=None, created=False, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
serializer:
class ProfileSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(source = 'pk', read_only = True)
username = serializers.CharField(source = 'user.username', read_only = True)
email = serializers.CharField(source = 'user.email', read_only=True)
class Meta:
model = Profile
fields = ('id', 'email', 'username', 'yearOfExperience',
'qualification', 'profession', 'phoneNumber'
)
def create(self, validated_data, instance=None):
if 'user' in validated_data:
user_data = validated_data.pop('user')
user = CustomUser.objects.create(**validated_data)
profile = Profile.objects.update_or_create(user=user, **validated_data)
return user
apiView:
class ProfileListView(generics.ListCreateAPIView):
permission_classes = (IsAuthenticatedOrReadOnly,)
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
def perform_create(self, serializer):
serializer.save(user=self.request.user)
Error message:
File "/home/olaneat/Desktop/filez/project/django/funzone/lib/python3.7/site-packages/django/db/models/base.py", line 500, in __init__
raise TypeError("%s() got an unexpected keyword argument '%s'" % (cls.__name__, kwarg))
TypeError: CustomUser() got an unexpected keyword argument 'yearOfExperience'
I believe you're missing the user one to one field to your profile model so
add it there
class Profile(models.Model):
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE)
...
Since you're using post_save for your profile, there's no need for the .create in your serializer. So something like this:
class ProfileSerializer(serializers.ModelSerializer):
username = serializers.SerializerMethodField(read_only = True)
email = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Profile
fields = ('id', 'email', 'username', 'yearOfExperience',
'qualification', 'profession', 'phoneNumber'
)
def get_username(self, obj):
return obj.user.username
def get_email(self, obj):
return obj.user.email
now just post from the API view and the profile will be created.
If however, you want to keep the .create for the option to add new users from the profile API then perhaps something like this:
class ProfileSerializer(serializers.ModelSerializer):
username = serializers.SerializerMethodField(read_only = True)
email = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Profile
fields = ('id', 'email', 'username', 'yearOfExperience',
'qualification', 'profession', 'phoneNumber'
)
def create(self, validated_data, instance=None):
if 'user' in validated_data:
user = validated_data.pop('user')
else:
user = CustomUser.objects.create(**validated_data)
profile, created_profile = Profile.objects.update_or_create(user=user, **validated_data)
return profile
def get_username(self, obj):
return obj.user.username
def get_email(self, obj):
return obj.user.email
I have the following models.
class User(models.Model):
...
class Group(models.Model):
name = models.CharField(max_length=255)
creater = models.ForeignKey(User)
users = models.ManyToManyField(User, through='GroupUser', through_fields=('group', 'user'))
class GroupUser(models.Model):
group = models.ForeignKey(Group)
user = models.ForeignKey(User)
date_joined = models.DateField(auto_now_add=True, editable=False)
and for admin.py, I defined these.
class GroupAdminForm(forms.ModelForm):
users = forms.ModelMultipleChoiceField(
queryset=User.objects.all(),
required=False,
widget=FilteredSelectMultiple(
is_stacked=False,
),
)
class Meta:
model = Group
def __init__(self, *args, **kwargs):
super(GroupAdminForm, self).__init__(*args, **kwargs)
if self.instance.id:
self.fields['users'].initial = self.instance.users.all()
# Version 1 save()
def save(self, commit=True):
group = super(GroupAdminForm, self).save(commit=False)
if group.id:
group.users = self.cleaned_data['users']
if commit:
group.save()
group.save_m2m()
return group
# Version 2 save()
def save(self, commit=True):
group = super(GroupAdminForm, self).save(commit=False)
if group.id:
for user in self.cleaned_data['users']:
u = GroupUser(group=group, user=user)
u.save()
if commit:
group.save()
return group
class GroupAdmin(models.ModelAdmin):
...
form = GroupAdminForm
filter_horizontal = ['users']
...
The problem is no matter I use version 1 and 2 save() function, it returns an error:
Cannot set values on a ManyToManyField which specifies an intermediary model.
I found some similar questions on web but I still cannot get the ideas of how the to override the save() function. Any suggestions?
I know it may works if I do not define the intermediary model, but can do it in such way but still have the way to get the date_join field for each records?