How to pass request to django form?
I am creating a django update profile form where user could change profile email. I want to check if email in form belongs to logged user if not then I want to check if this email is used by others users before setting it as new users email.
Here is my code and this self.request.user.email doesn't work:
def clean_email(self):
email = self.cleaned_data.get("email")
owns_email = (email != self.request.user.email)
if User.objects.filter(email__icontains=email).exists() and owns_email:
raise forms.ValidationError("This email aldready registered.")
return email
So maybe there is better solution to solve my problem?
Since you are using a cbv, you can use the get_form_kwargs function from the FormMixin.
It could look something like this:
class UserProfileUpdateView(UpdateView):
...
def get_form_kwargs(self):
'''This goes in the Update view'''
kwargs = super(UserProfileUpdateView, self).get_form_kwargs() #put your view name in the super
user = self.request.user
if user:
kwargs['user'] = user
return kwargs
Then your form class would look something like this, based on your above code:
class UserProfileUpdateForm:
...
def __init__(self, *args, **kwargs):
if kwargs.get('user'):
self.user = kwargs.pop('user', None)
super(UserProfileUpdateForm, self).__init__(*args,**kwargs)
def clean_email(self):
email = self.cleaned_data.get("email")
owns_email = (email != self.user.email)
if User.objects.filter(email__icontains=email).exists() and owns_email:
raise forms.ValidationError("This email already registered.")
return email
The form doesn't have the Request object. You need to manually pass the currently logged in user in the constructor. Your form should look something like this:
class UserProfileForm(forms.Form):
user = None
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
super(UserProfileForm, self).__init__(*args, **kwargs)
...
def clean_email(self):
email = self.cleaned_data['email']
owns_email = (email != self.user.email)
if User.objects.filter(email__icontains=email).exists() and owns_email:
raise forms.ValidationError('This email already registered.')
return email
...
Instantiating the form in the view:
def edit_profile(request):
form = UserProfileForm(user=request.user)
...
Related
I got a strange bug. I am failing validation if I add an email field. If validation for only 1 username field, then everything works fine. Tell me please, what am I doing wrong?
file forms.py:
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField(required=False)
def __init__(self, user, *args, **kwargs):
self.user = user
super(UserUpdateForm, self).__init__(*args, **kwargs)
if 'label_suffix' not in kwargs:
kwargs['label_suffix'] = '*'
self.fields['username'].widget = forms.TextInput(attrs={'class':'input-text'})
self.fields['email'].widget = forms.EmailInput(attrs={'class':'input-text'})
class Meta:
model = User
fields = ("username","email",)
file views.py:
def get_context_data(self, request):
self.object = get_object_or_404(Profile,slug=self.kwargs['slug'])
ctx={}
ctx['UserUpdateForm']=UserUpdateForm(request.POST if "UserUpdateForm" in request.POST else None,instance=request.user)
сtx['comments']=Comments.objects.filter(profile=self.object)
return ctx
def post(self, request, *args, **kwargs):
if request.method=='POST':
if "UserUpdateForm" in request.POST:
form=UserUpdateForm(request.POST)
if form.is_valid():
user=User.objects.get(username=self.request.user)
user.username=form.cleaned_data.get('username')
user.email=form.cleaned_data.get('email')
user.save()
obj=Profile.objects.get(user__username=user.username)
return HttpResponseRedirect(reverse_lazy('profile',kwargs={'slug': obj.slug},))
return render(request,self.template_name,self.get_context_data(request))
You construct your form with an extra parameter user:
class UserUpdateForm(forms.ModelForm):
def __init__(self, user, *args, **kwargs):
# …
so that means that the first parameter when you construct the form is the user. You thus should pass a user:
form=UserUpdateForm(request.user, request.POST)
or if you want to edit the user object:
form=UserUpdateForm(request.user, request.POST, instance=request.user)
it however does not make much sense to pass the user, since as far as one can see, you never use the .user attribute in your form.
I am using a updateapiview to the update my user information. This is my view
class UserUpdateView(generics.UpdateAPIView):
serializer_class = UserUpdateSerializer
def get_queryset(self):
print(self.kwargs['pk'])
return User.objects.filter(pk=self.kwargs['pk'])
def partial_update(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data, partial=True)
if self.get_object() != request.user:
return Response({'error': 'permission denied'}, status=status.HTTP_400_BAD_REQUEST)
print(request.data['password'])
request.data['password'] = make_password(request.data['password'])
instance = super(UserUpdateView, self).partial_update(request, *args, **kwargs)
return instance
This is my serializer
class UserUpdateSerializer(serializers.ModelSerializer):
email = serializers.EmailField(required=True)
def validate_username(self, username):
if User.objects.filter(username=username):
raise serializers.ValidationError('Username already exists!!')
return username
def validate_email(self, email):
if User.objects.filter(email=email):
raise serializers.ValidationError('Email already exists!!')
return email
class Meta:
model = User
fields = ('pk', 'username', 'password', 'email')
read_only_fields = ('pk',)
I am getting the data updated and returned successfully. But I want to add some field like message with content 'successfully updated' on successful updation of the user profile. I searched if there is any way to perform this but can't find the appropriate way to do it (alike get_context_data method in django). So, is there any way to perform the above task ??
Question 2:
How to prevent the self user ( i.e if has a email sample#gmail.com and if user clicks udpate with the same email, it should not raise an error that username already exists, I guess this can be done with self.instance (but not sure how actually to implement it).
Thanks!
I'm not sure what version of DRF you are using but in the latest 3.9.0 UpdateAPIView uses UpdateModelMixin, which returns Response object instead of instance. So you can modify data before returning.
def partial_update(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data, partial=True)
if self.get_object() != request.user:
return Response({'error': 'permission denied'}, status=status.HTTP_400_BAD_REQUEST)
print(request.data['password'])
request.data['password'] = make_password(request.data['password'])
response = super(UserUpdateView, self).partial_update(request, *args, **kwargs)
response.data['message'] = 'Successfully updated'
return response
Regarding your second question you can exclude the current user using self.instance.
def validate_email(self, email):
if User.objects.filter(email=email).exclude(id=self.instance.id).exists():
raise serializers.ValidationError('Email already exists!!')
return email
User is Logged in and fills the form.
user enters the username in a form.
If the entered username doesn't match with username of logged in user raise error
forms.py
class iffc_one(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(iffc_one, self).__init__(*args, **kwargs)
def clean(self):
cleaned_data = super().clean()
fieldentered_username = self.cleaned_data['current_user']
if self.user != fieldentered_username:
raise forms.ValidationError('Invalid user')
class Meta:
model = bookings_modelform
models.py
class bookings_modelform(models.Model):
current_user = models.CharField(max_length=200)
What am I doing wrong here!
You don't need to validate it, You just need get logged user information from request.user and put it into yourcmodel.
bookings_model.current_user = request.user.username
I've been struggling to pass some extra data form my Form class to my views. For a password recovery an user has to fill in an username or e-mail address. When cleaning, the username and password are checked if one of them exists in the database:
def clean(self):
username = self.cleaned_data.get("username")
email = self.cleaned_data.get("email")
if username:
try:
user = User.objects.get(username__iexact=username, is_active=True) # <- to view
except User.DoesNotExist:
raise forms.ValidationError(
self.error_messages["invalid_username"],
code="invalid_username"
)
elif email:
try:
user = User.objects.get(email__iexact=email, is_active=True) # <- to view
except User.DoesNotExist:
raise forms.ValidationError(
self.error_messages["invalid_email"],
code="invalid_email"
)
else:
raise forms.ValidationError(
self.error_messages["empty_form"],
code="empty_form"
)
return self.cleaned_data
When the form has been validated, I want to send the user data to the view. This to separate the send_email logics away from the form and being able to add some information to the context, so it can be rendered in the template.
So in my FormView, if the form is valid, I want to be able to use the user object retrieved in the Form.
Currently I have attempted quite some 'answers' of other SO questions and examples of the web. But I keep getting AttributeErrors, KeyErrors, 'WSGIRequest' object does not support item assignment.
The last attempt I made was to overwrite the init in my Form, and the get_form_kwargs in my view:
Form
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
super(RecoverPasswordForm, self).__init__(*args, **kwargs)
def clean(self):
....
self.request["user"] = User.objects.get(username__iexact=username, is_active=True)
View
def get_form_kwargs(self, **kwargs):
kwargs = super(RecoverPassword, self).get_form_kwargs()
kwargs["request"] = self.request
return kwargs
Which leads to the following error
'WSGIRequest' object does not support item assignment
Can somebody give me an explanation of what I'm doing wrong, and push me in the right direction to solve this problem? Thanks in advance!
From what I understand, I think you are trying to send an email with reset password link to the user from your view after validating the username/email in your form. Next to add some info to the context, so it can be rendered in the template.
So this can done by overriding the form_valid method in your class like:
def form_valid(self, form):
username = form.cleaned_data['username']
email = form.cleaned_data['email']
if username: user = User.objects.get(username__iexact=username, is_active=True)
else: user = User.objects.get(email__iexact=email, is_active=True)
send_email(user)
data = {'info':'give your info here'}
render(request, 'success.html', data)
Learn more here
Update:
to can access the user object from the form do like:
def clean():
...
self.cleaned_data["user"] = User.objects.get(username__iexact=username, is_active=True)
In your form_valid
def form_valid(self, form):
user = form.cleaned_data['user']
send_email(user)
data = {'info':'give your info here'}
render(request, 'success.html', data)
I can't find the answer to this problem which I guessed was very easy for Django.
I simply want to define an author field in a model, like this:
class Article(models.Model):
author = models.ForeignKey(User)
It seems there is no easy way like author = models.ForeignKey(User, default=current_user) so what is the easiest way to store the currently logged in user in the database? (if not logged in, a pre-defined default user called "anonymous" can be used)
Thanks for any help!
Currently logged user is available in the view as the request.user attribute:
def create_article(request):
...
if request.user.is_active():
article.author = request.user
article.save()
...
Alternatively, you can pass request.user to your form class and override the save:
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
def __init__(self, *args, **kwargs):
# Allows you to pass the user in from the request, or just set the property
if not hasattr(self, 'user'):
self.user = kwargs.pop('user')
super(ArticleForm, self).__init__(*args, **kwargs)
def save(self, commit=True)
article = super(ArticleForm, self).save(commit=False)
article.user = self.user
if commit:
article.save()
return article
Slightly more code, but it's encapsulated in the form class and not in the view, so you can use it in more than one place.
Example usage:
# in a view
#login_required
def your_view(request):
form = ArticleForm(request.POST or None, user=request.user)
. . .
# in Django admin
class ArticleAdmin(admin.ModelAdmin):
form = ArticleForm
def get_form(self, request, obj=None, **kwargs):
form = super(ArticleAdmin, self).get_form(request, obj, **kwargs)
form.user = request.user
return form
In either use case, you can be assured you have an authenticated user.