I have a ModelForm to allow the creation of new User objects via a subclass of CreateView, and I also have a UserProfile model with a "client" field, and connected to the User model.
This:
# models.py
class UserProfile(TimeStampedModel):
user = models.OneToOneField(User, unique=True)
client = models.ForeignKey(Client)
# forms.py
class UserForm(ModelForm):
def create_userprofile(self, user, client):
profile = UserProfile()
profile.user = user
profile.client = client
profile.save()
class Meta:
model = User
fields = ('email', 'username', 'password', 'first_name', 'last_name', 'groups')
# views.py
class UserCreate(LoginRequiredMixin, CreateView):
model = User
template_name = 'usermanager/user_form.html'
form_class = UserForm
success_url = reverse_lazy('usermanager:list')
def form_valid(self, form):
### Make sure a newly created user has a UserProfile.
# some pseudo-code thrown in
# First save the user
result = super(UserCreate, self).form_valid(form)
# Now that we have a user, let's create the UserProfile
form.create_userprofile(created_user, current_user.userprofile.client)
# Finally return the result of the parent method.
return result
I want to be able to create a new UserProfile when the form is submitted (and is valid, of course), so I was doing it on the CreateView.form_valid() method, but I need the ID of the just created user, which at that time I don't think I have - do I?
At the same time, I need to assign to the new UserProfile the same client as the current (not the new) user has in his profile.
Any thoughts on how to achieve this?
try checking if
self.object.pk
has what you want after calling
super(UserCreate, self).form_valid(form)
in your form_valid method.
Related
I am trying to check if the user id not equal to 1 then he should not be able to update few fields. I tried something similar to the following code but it did not work because of the following issues
self.user.id don't actually return the user I need to get the authenticated user in different why?
the def function maybe should have a different name like update?
also the general way maybe wrong?
class ForAdmins(serializers.ModelSerializer)):
class Meta:
model = User
fields = '__all__'
class ForUsers(serializers.ModelSerializer)):
class Meta:
read_only_fields = ['email','is_role_veryfied','is_email_veryfied']
model = User
fields = '__all__'
class UsersSerializer(QueryFieldsMixin, serializers.ModelSerializer):
def customize_read_only(self, instance, validated_data):
if (self.user.id==1):
return ForAdmins
else:
return ForUsers
class Meta:
# read_only_fields = ['username']
model = User
fields = '__all__'
You can make the decision which serializer you want to pass from your views
or
you can do it inside modelSerializer update method.
for getting user from Serializer class Try:
request = self.context.get('request', None)
if request:
user = request.user
for getting user from View class Try:
user = self.request.user
Here I am trying to update user and user_profile model.This updates the user but the one problem with this is: If I don't provide the address or any other field then it becomes blank after updating.How can I solve this ?
If i update only one field then it makes other field null while updating.I want to store the user's previous data if user doesn't update the field
models.py
class Profile(models.Model):
user = models.OneToOneField(get_user_model(),on_delete=models.CASCADE,related_name='profile')
address = models.CharField(max_length=250,blank=True,null=True)
serializer.py
class UpdateUserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = get_user_model()
fields = ['first_name', 'last_name', 'profile']
def update(self, instance, validated_data):
instance.username = validated_data.get('username', instance.username)
instance.email = validated_data.get('email', instance.email)
instance.first_name = validated_data.get('first_name', instance.first_name)
instance.last_name = validated_data.get('last_name', instance.last_name)
instance.save()
profile_data = validated_data.pop('profile')
instance.profile.address = profile_data.get('address', instance.profile.address)
instance.profile.save()
return instance
views.py
class UpdateUser(generics.UpdateAPIView):
serializer_class = UpdateUserSerializer
queryset = get_user_model().objects.all()
You are overriding model instance fields on update with values from params. If there are no corresponding params, you will get empty strings as values.
DRF comes with this logic already implemented. You only have to process profile data. Change serializers.py to:
class UpdateUserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = get_user_model()
fields = ['first_name', 'last_name', 'profile']
def update(self, instance, validated_data):
# We try to get profile data
profile_data = validated_data.pop('profile', None)
# If we have one
if profile_data is not None:
# We set address, assuming that you always set address
# if you provide profile
instance.profile.address = profile_data['address']
# And save profile
instance.profile.save()
# Rest will be handled by DRF
return super().update(instance, validated_data)
Make sure you use PATCH request, as PUT is for whole instance update. PATCH is for partial instance update.
I have seen similar questions but they are from 2012. Django Rest Framework has changed since then.
So, I have a Profile model that references the django User model. I have a ProfileSerializer that looks like this:
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerializer
class Meta:
model = Profile
fields = ['user', 'image', 'decription', 'city', 'phone_number']
The UserSerializer:
class UserSerializer(serializers.ModelSerializer):
def create(self, validated_data):
user_obj = User.objects.create(first_name=validated_data['first_name'], last_name=validated_data['last_name'],
email=validated_data['email'])
user_obj.set_password(validated_data['password'])
user_obj.save()
return user_obj
class Meta:
model = User
write_only_fields = ['password']
read_only_fields = ['id']
The Browsable Api shows the user field as a dropdown with the registered users, I need it to show the UserSerializer fields so that the view could create both, the Profile object and the User object at once, the view:
class RegisterUser(generics.CreateAPIView):
serializer_class = ProfileSerializer
Is it possible?
Edit, add the model:
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User)
image = models.UrlField()
....
So, is it possible to create both, the nested User object and the Profile object with one CreateAPIView? Or should I do it in two steps. All suggestions are welcome.
I created a model (UserSettings) to extend django's User model through a OneToOneField (as recommended by the documentation):
class UserSettings(models.Model):
user = models.OneToOneField(User, primary_key=True)
subscribeToMails = models.BooleanField(default=True)
[...]
I wish to offer my users a way to edit some of their profile data, some of which is stored in the User model (the email address), and the rest in the UserSettings model. How may I do that?
I thought of two ways: adding another OneToOneField in the UserSettings model for the email address field; or overriding the UpdateView get_queryset() method (but I'm not sure how). Is there a best or recommended way to do it? So far here's how my view look:
class EditUser(UpdateView):
model = UserSettings
fields = ('emailVisible', 'subscribeToMails', 'mpPopupNotif',
'mpEmailNotif', 'avatar', 'quote', 'website')
template_name = 'user/edit.html'
def get_object(self):
return UserSettings.objects.get(user_id=self.request.user)
def get_success_url(self):
return reverse_lazy('user:edit')
Thanks for the replies! However, since I couldn't figure out how to make this work and thought using two tables eventually resulted in too much clutter to my taste, I finally went with the easier route and subclassed AbstractUser:
# models.py
class ForumUser(AbstractUser):
subscribeToMails = models.BooleanField(default=True)
[...]
# views.py
class EditUser(LoginRequiredMixin, UpdateView):
model = ForumUser
fields = ('email', 'emailVisible', 'subscribeToMails', 'mpPopupNotif',
'mpEmailNotif', 'avatar', 'quote', 'website')
template_name = 'user/edit.html'
success_url = reverse_lazy('forum:welcome')
def get_object(self):
return ForumUser.objects.get(username=self.request.user)
I only had to change my registration form:
# forms.py
class RegisterForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = ForumUser
fields = ('username', 'email', 'password1', 'password2')
def clean_email(self):
"Ensure registered emails are unique."
email = self.cleaned_data.get('email')
username = self.cleaned_data.get('username')
if email and ForumUser.objects.filter(email=email).exclude(
username=username).count():
raise forms.ValidationError('Email address already in use.')
return email
def clean_username(self):
"""
UserCreationForm method where mentions of the User model are replaced
by the custom AbstractUser model (here, ForumUser).
https://code.djangoproject.com/ticket/19353#no1
and https://docs.djangoproject.com/en/1.7/_modules/django/contrib/
auth/forms/#UserCreationForm
"""
username = self.cleaned_data["username"]
try:
ForumUser.objects.get(username=username)
except ForumUser.DoesNotExist:
return username
raise forms.ValidationError(
self.error_messages['duplicate_username'],
code='duplicate_username',
)
Use this solution:
mix both User and UserSettings in a form like this:
class EmployeeEditForm(forms.ModelForm):
#fields from User model that you want to edit
first_name = forms.CharField(required=False, label=_('First Name'))
last_name = forms.CharField(required=False, label=_('Last Name'))
class Meta:
model = UserSettings
fields = ('first_name', 'last_name', 'subscribeToMails')
You can access to User and UserSettings object in views.py like this:
user = request.user
usersettings = user.usersettings
Now you can edit User object like this:
user.first_name = request.POST['first_name']
user.last_name = request.POST['last_name']
user.save()
And edit UserSettings like this:
usersettings.subscribeToMails = request.POST['subscribeToMails']
usersettings.save()
Formsets is the best way to go about it.
https://docs.djangoproject.com/en/dev/topics/forms/formsets/
EDIT I reformulated the question here
I want to create several custom user models extending django.contrib.auth.models.User, with the following features:
some specific fields
a nice admin, i.e., for each model, an admin form that I can customize easily (eg. show/hide some fields, both from the parent django.contrib.auth.models.User and the child model).
I almost managed to do it with the code below, but I still have an issue: the password is cleared every time I want to modify an instance of MyUser1 or MyUser2 from the admin.
Is it the best way to do it? If so, how can I fix this cleared password issue?
models.py
from django.db import models
from django.contrib.auth.models import User
class MyUser1(User):
#add more fields specific to MyUser1
class MyUser2(User):
#add more fields specific to MyUser2
admin.py
class MyUser1AdminForm(forms.ModelForm):
class Meta:
model = MyUser1
def __init__(self, *args, **kwargs):
super(MyUser1AdminForm, self).__init__(*args, **kwargs)
self.fields['password'].widget = forms.PasswordInput()
def save(self, commit=True):
user = super(MyUser1AdminForm, self).save(commit=False)
user.set_password(self.cleaned_data["password"])
if commit:
user.save()
return user
class MyUser1Admin(admin.ModelAdmin):
form = MyUser1AdminForm
admin.site.register(MyUser1, MyUser1Admin)
# same for MyUser2
if you whant for user to enter pass - add new field for pass and check it before commit
class MyUser1AdminForm(forms.ModelForm):
check_pass = forms.CharField(label="Password",widget = forms.PasswordInput(), required=True)
class Meta:
model = MyUser1
exclude = ('password',)
def save(self, commit=True):
user = super(MyUser1AdminForm, self).save(commit=False)
if commit and user.check_password(self.cleaned_data["check_pass"]):
user.save()
return user