Troubles making a user update view - django

I have a custom user model, subclass of AbstractBaseUser.
I'm currently having troubles making an update view, so user could change his profile. Changing user through admin interface works fine.
This is a form that I use to change user objects in both admin and app`s interface.
class UserChangeForm(forms.ModelForm):
password = ReadOnlyPasswordHashField(
label=_("Password"),
help_text=_(
"Raw passwords are not stored, so there is no way to see this "
"user's password, but you can change the password using "
"this form."
),
)
class Meta:
model = User
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
password = self.fields.get('password')
if password:
password.help_text = password.help_text.format('../password/')
user_permissions = self.fields.get('user_permissions')
if user_permissions:
user_permissions.queryset = user_permissions.queryset.select_related('content_type')
def clean_password(self):
return self.initial["password"]
I'm using fields = '__all__' to be able to change all the fields through admin interface. But in app's intreface I want user to change only some fields.
This in my view:
def update_user(request):
form = UserChangeForm(request.POST or None, instance=request.user, fields=('email', 'first_name'))
if request.method == 'POST':
if form.is_valid():
form.save()
return redirect('home')
return render(request, 'update_user.html', {'form': form})
return render(request, 'update_user.html', {'form': form})
If I pass fields parameter like that UserChangeForm(request.POST or None, request.user, fields=('email', 'first_name')) I get __init__() got an unexpected keyword argument 'fields' error.
If I don't pass it I get exacty same form with all the fileds as in the admin inface.
How can I get this form show only fields I want?

One solution would be to create a subclass of the UserChangeForm so that you can choose fields you want in the Meta class:
class MyUserChangeForm(UserChangeForm):
class Meta(UserChangeForm.Meta):
model = User
fields = ['email', 'first_name]
And then in your view you use the new form you made:
def update_user(request):
form = MyUserChangeForm(request.POST or None, instance=request.user)
# and so on ...

Related

Setting user kwargs as field vales in Django Form with Class-based View

I have a Django application (1.11) to track referrals (referred by a user). I want to pass the id of the authenticated user to the ModelForm 'referrer' field (and since it's from the current logged in user the field shouldn't be editable).
class Referral(models.Model):
name = models.CharField(max_length=100)
referrer = models.ForeignKey('users.User', on_delete=models.SET_NULL, related_name='referrals', null=True, blank=True)
View:
class ReferralFormView(FormView):
form_class = ReferralForm
template_name = "refer.html"
success_url = reverse_lazy('thanks')
def get(self, request):
if request.user.is_authenticated():
return super(ReferralFormView, self).get(request)
else:
return redirect('login')
def get_form_kwargs(self):
user = self.request.user
form_kwargs = super(ReferralFormView, self).get_form_kwargs()
form_kwargs['referrer'] = user.id
return form_kwargs
def form_valid(self,form):
...
form.save()
return super(ReferralFormView, self).form_valid(form)
I override get_form_kwargs in the view, then modify form init
class ReferralForm(forms.ModelForm):
class Meta:
model = Referral
def __init__(self, *args, **kwargs):
referrer = kwargs.pop('referrer', None)
super(ReferralForm, self).__init__(*args, **kwargs)
self.fields['referrer'].disabled = True
self.fields['referrer'].queryset = User.objects.filter(id=referrer)
However all I see is a blank referrer field, what am I missing to make the user the value of that field (which can't be edited)? I also tried self.fields['referrer'].initial = User.objects.filter(id=referrer). I don't want the user to have to select their own username from a queryset of one.
I can print a <QuerySet [<User: username>]> for user = User.objects.filter(id=referrer), so why isn't it setting that user as the field value?
Update: I can assign the user value with
self.fields['referrer'].initial = User.objects.filter(id=referrer).first()
self.fields['referrer'].disabled = True
However, on form submit Referral obj is not saving with the referrer field value (that value's still blank)
thanks
what I needed to do was select the user obj in the queryset:
self.fields['referrer'].initial = User.objects.filter(id=referrer).first()
but using self.fields['referrer'].disabled = True disabled the field so I was still getting a blank value on submit (disabled doesn't do what the docs say it does). Using self.fields['referrer'].initial = User.objects.filter(id=referrer).first() with that field set as readonly allows the form to submit with the initial value

Django Send Form By Authenticated User

i stuck when trying to send data or save data with django form by user it self (logged).
When i test why form "From" user must be selectable not automatic selected by user it self.
class ValidationCreate(forms.ModelForm):
class Meta:
model = About
fields = '__all__'
def upload(request):
upload = ValidationCreate()
if request.method == 'POST':
upload = ValidationCreate(request.POST, request.FILES)
if upload.is_valid():
upload.save()
return redirect('validation')
else:
return HttpResponse("""your form is wrong, reload on reload""")
else:
return render(request, 'upload_form.html', {'about_form': upload})
sample
this way you can assign the request.user
if upload.is_valid():
instance = upload.save(commit=False)
instance.profile = Profile.objects.get(user=request.user) # you can change the user if field name is different
instance.save()
return redirect('validation')
else:
in forms
fields = ['field_1', 'field_2',] # except user field

How to transfer some variable to django form?

I want to make a custom form field validation to check if entered string is an email of user variable. Smth like this:
class FullEmailOrPhoneForm(forms.Form):
entered_string = forms.CharField()
class Meta:
model = User
fields = ('entered_string',)
def clean_entered_string(self):
email = self.cleaned_data['entered_string']
if email == user.email: # I need user variable for this comprasion
ans = email
else:
raise ValidationError('Incorrect email')
return ans
My view:
def reset_password_with_username(request, user):
if request.method == 'POST':
form = FullEmailOrPhoneForm(request.POST)
if form.is_valid():
pass
else:
form = FullEmailOrPhoneForm()
return render(request, 'registration/password_reset_with_username.html')
So how can I transfer user variable from view to form validation function?
You can override the __init__() method of your form so it can receive an extra argument, the user:
# inside FullEmailOrPhoneForm
def __init__(self, user, *args, **kwargs):
self.user = user # now you can use self.user anywhere in your form
super().__init__(*args, **kwargs)
def clean_entered_string(self):
...
if self.user and email == self.user.email:
...
# inside your view you have to specify `data=` since the first init arg is now the user.
form = FullEmailOrPhoneForm(user=request.user, data=request.POST)
# or with no data
form = FullEmailOrPhoneForm(user=request.user)
Note that you created a Form, not a ModelForm, so your Meta class is completely useless. If in fact, you wanted to have a ModelForm that models the user being edited (and the user you want to pass to the form is the same user as the one being edited), you should do this using the instance of the form:
class FullEmailOrPhoneForm(forms.ModelForm): # <-- note the ModelForm here
...
class Meta:
model = User
fields = ...
def clean_entered_string(self):
...
if self.instance and email == self.instance.email:
...
# then in your view:
form = FullEmailOrPhoneForm(request.POST, instance=user)

Remove option from ModelChoiceField

I just got my hands on an user account create view that looks like this:
#login_required
def user_create(request):
template_name = 'user/User_Create.html'
if request.method == 'POST':
#this part is not important
pass
else:
form = UserCreateForm()
user_error = ''
context = {'form': form, 'user_error': user_error}
return render(request, template_name, context)
with the UserCreateForm written like this:
class UserCreateForm(forms.ModelForm):
def save(self, commit=True):
user = super(UserCreateForm,self).save(commit=False)
username = self.cleaned_data['username']
username = username.replace(".", "")
username = username.replace("-", "")
user.username = username
if commit:
user.save()
return user
class Meta:
model = User
fields = ['username', 'name', 'profile', 'redefine_password', 'name_created']
widgets = {
'username': forms.TextInput(),
'name': forms.TextInput(),
'profile': forms.Select(),
'redefine_password': forms.CheckboxInput(),
'name_created': forms.TextInput(),
}
My problem is that we have different types of users(Admin, Supervisor, Support, Normal) and currently, Supervisors are able to create Admin accounts...
My initial approach was to pass the user from the view to the form, like this:
form = UserCreateForm(user=request.user)
and in the form, I'm trying to delete the option if the user is not an Admin, like this:
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super(UserCreateForm, self).__init__(*args, **kwargs)
if not user.is_superuser:
del self.fields['profile'][1, 'Administrador']
but that failed miserably, I got a TypeError: 'ModelChoiceField' object does not support item deletion.
I tried assigning it to None but that didn't work as well since it doesn't support item assignment neither.
Lastly, I tried assisgning it to a forms.ModelChoiceField() using the queryset attribute but I couldn't make it work.
Could someone shed a light?
Edit:
What I am trying to do is to remove the option to create an admin account in case the current logged user is not an admin, the option is defined in the profile choices.

Django Form problems with UserProfile

I'd like to create a "update user's profile" page to let users modify their profiles, so I come up with the following models:
class Profile(models.Model):
user = models.OneToOneField(User)
nick_name = models.CharField(blank=True,max_length=100)
school = models.CharField(blank=True,max_length=100)
motto = models.CharField(blank=True,max_length=100)
class ProfileForm(ModelForm):
class Meta:
model = Profile
And my view is designed as:
#login_required
def update_profile_view(request):
if request.method == 'POST':
user = request.user
try:
profile = user.get_profile()
except Exception:
profile = Profile.objects.create(user=user)
form = ProfileForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
profile.nick_name = cd['nick_name']
profile.school = cd['school']
profile.motto = cd['motto']
profile.save()
return HttpResponseRedirect('/main_page/')
else:
form = ProfileForm()
return render(request, 'update_profile.html', {'form':form})
The relationship between an user and a profile is apparently 1to1, and with request I can determine the current user. So the form's user field needn't to be filled. Unfortunately, this couldn't pass "the form.is_valid()" test. And it seems hard to modify a form before "is_valid" invoked. For simplicity, I don't want to create my own Form Class, neither do I want to write customized form validation. Is there any other way to solve the problem?
Your view can be greatly simplified:
#login_required
def update_profile_view(request):
try:
profile = Profile.objects.get(user=request.user)
except Profile.DoesNotExist:
profile = None
form = ProfileForm(request.POST or None, instance=profile)
if request.method == 'POST':
if form.is_valid():
form.save()
return HttpResponseRedirect('/main_page/')
return render(request, 'update_profile.html', {'form':form})
There's no need to manually assign the fields like you're doing. Django ORM knows how to do an insert versus an update automatically. So if you simply pass the ProfileForm an instance of a Profile, it knows to do an update. If there's no instance of a profile, it's going to do an insert.
Now, if you want to make the assignment of the user transparent in the UI, you'll need to exclude the user field from the form and assign it yourself. There are a couple of different ways to do that.
I would also recommend leveraging reverse in your redirect so you don't have a hard-coded path.
You have basicly two choices:
1 Modification of ProfileForm:
class ProfileForm(ModelForm):
class Meta:
model = Profileclass
exclude = ('user',)
2 Change this lines as follows:
form = ProfileForm(request.POST, instance=profile)
if form.is_valid():
updated_profile = form.save()
You can either set the user field's value to not required in the init method (self.fields['user'].required = False) of the form or set the user not editable in the model (editable=False).
In your view method, call profile = form.save(commit=False), then do profile.user = your_user and profile.save()
You don't have to apply the cleaned data manually to the profile since the ModelForm does this.