60

Is it possible to get request.user data in a form class? I want to clean an email address to make sure that it's unique, but if it's the current users email address then it should pass.

This is what I currently have which works great for creating new users, but if I want to edit a user I run into the problem of their email not validating, because it comes up as being taken already. If I could check that it's their email using request.user.email then I would be able to solve my problem, but I'm not sure how to do that.

class editUserForm(forms.Form):
    email_address = forms.EmailField(widget=forms.TextInput(attrs={'class':'required'}))

    def clean_email_address(self):
        this_email = self.cleaned_data['email_address']
        test = UserProfiles.objects.filter(email = this_email)
        if len(test)>0:
            raise ValidationError("A user with that email already exists.")
        else:
            return this_email
blissini
  • 545
  • 1
  • 7
  • 18
Joe
  • 4,553
  • 9
  • 51
  • 57

6 Answers6

78

As ars and Diarmuid have pointed out, you can pass request.user into your form, and use it in validating the email. Diarmuid's code, however, is wrong. The code should actually read:

from django import forms

class UserForm(forms.Form):
    email_address = forms.EmailField(
        widget=forms.TextInput(
            attrs={
                'class': 'required'
            }
        )
    )

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user', None)
        super(UserForm, self).__init__(*args, **kwargs)

    def clean_email_address(self):
        email = self.cleaned_data.get('email_address')

        if self.user and self.user.email == email:
            return email

        if UserProfile.objects.filter(email=email).count():
            raise forms.ValidationError(
                u'That email address already exists.'
            )

        return email

Then, in your view, you can use it like so:

def someview(request):
    if request.method == 'POST':
        form = UserForm(request.POST, user=request.user)
        if form.is_valid():
            # Do something with the data
            pass
    else:
        form = UserForm(user=request.user)
    # Rest of your view follows

Note that you should pass request.POST as a keyword argument, since your constructor expects 'user' as the first positional argument.

Doing it this way, you need to pass user as a keyword argument. You can either pass request.POST as a positional argument, or a keyword argument (via data=request.POST).

avenet
  • 2,894
  • 1
  • 19
  • 26
elo80ka
  • 14,837
  • 3
  • 36
  • 43
  • 2
    >Note that you should pass request.POST as a keyword argument, since your constructor expects 'user' as the first positional argument. this is specifically why you shouldn't mix key-word (i.e., optional) arguments with **kwargs. Instead, the constructor should not have user and just put this as the first line: user = kwargs.pop('user') if 'user' in kwargs else None – jorelli Nov 17 '11 at 07:19
  • It's been a long time, so I am not sure if my question is proper. I use `kwargs.get('user', None)` to get the user, but in the next line, it raises `TypeError` as `__init__() got an unexpected keyword argument 'user'`, I know pop get and remove the `user`, but I don't know why I can not keep `user` in kwargs. – shellbye Nov 20 '15 at 03:56
  • 1
    @shellbye: the Form's default `__init__(...)` isn't expecting a `user` argument; that's why I use `kwargs.pop(...)`. Is there some reason why you want to keep the user in `kwargs`? – elo80ka Nov 21 '15 at 06:46
  • 1
    @elo80ka no, I did not want to keep user in `kwargs`, I just accidentally used `get` and met that error. I've switched to `pop` but don't know why. Based on what you said, I checked the source code, and find out that there is not `kwargs` in Form's default `__init__(...)`, so we have to `pop` out the customized `user`, right? – shellbye Nov 22 '15 at 03:16
42

Here's the way to get the user in your form when using generic views:

In the view, pass the request.user to the form using get_form_kwargs:

class SampleView(View):
    def get_form_kwargs(self):
        kwargs = super(SampleView, self).get_form_kwargs()
        kwargs['user'] = self.request.user
        return kwargs

In the form you will receive the user with the __init__ function:

class SampleForm(Form):
    def __init__(self, user, *args, **kwargs):
        super(SampleForm, self).__init__(*args, **kwargs)
        self.user = user
bbill
  • 2,264
  • 1
  • 22
  • 28
wuerg
  • 1,022
  • 12
  • 12
  • 1
    I actually prefer this way, but you should remove user from kwargs before calling constructor.. something like `self.user = kwargs.pop('user')` – intelis Dec 05 '17 at 23:41
4

Just so you know, with Django 1.4 and generic class based CreateView and UpdateView, a self.instance is populated on each model form, which allows you to compare the POSTed email vs the current user email.

Here is a code sample, using mixin

class EmailUniqueMixin(object):
    """
    Ensure each User's email is unique
    on the form level
    """
    def clean_email(self):
        email = self.cleaned_data['email']
        existing_email = User.objects.filter(email=email).exclude(pk=self.instance.id)
        if existing_email:
            raise forms.ValidationError('That email address already exists')
        else:
            return email
Jérémy F.
  • 583
  • 1
  • 4
  • 8
0

Not that I'm aware of. One way to handle this is have your Form class's __init__ take an optional email parameter, which it can store as an attribute. If supplied at form creation time, it can use that during validation for the comparison you're interested in.

ars
  • 120,335
  • 23
  • 147
  • 134
0

When you instantiate the form in the view, it is possible to pass an instance by parameter and use it inside the form.

form = YourModelForm(request.POST, instance=request.user)

In the form, you must access this way:

 if *self.instance.email* == email:
    return email
0

We can use get_form method:

class SampleView(View):
    def get_form(self, request, *args, **kwargs):
        form = super().get_form(request, *args, **kwargs)
        form.request_user = request.user
        return form

class SampleForm(Form):
    def clean_email_address(self):
        email = self.cleaned_data['email_address']

        if self.request_user and self.request_user.email==email: