[Django]-How do I access the request object or any other variable in a form's clean() method?

182đź‘Ť

âś…

The answer by Ber – storing it in threadlocals – is a very bad idea. There’s absolutely no reason to do it this way.

A much better way is to override the form’s __init__ method to take an extra keyword argument, request. This stores the request in the form, where it’s required, and from where you can access it in your clean method.

class MyForm(forms.Form):

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


    def clean(self):
        ... access the request object via self.request ...

and in your view:

myform = MyForm(request.POST, request=request)

47đź‘Ť

For what it’s worth, if you’re using Class Based Views, instead of function based views, override get_form_kwargs in your editing view. Example code for a custom CreateView:

from braces.views import LoginRequiredMixin

class MyModelCreateView(LoginRequiredMixin, CreateView):
    template_name = 'example/create.html'
    model = MyModel
    form_class = MyModelForm
    success_message = "%(my_object)s added to your site."

    def get_form_kwargs(self):
        kw = super(MyModelCreateView, self).get_form_kwargs()
        kw['request'] = self.request # the trick!
        return kw

    def form_valid(self):
        # do something

The above view code will make request available as one of the keyword arguments to the form’s __init__ constructor function. Therefore in your ModelForm do:

class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel

    def __init__(self, *args, **kwargs):
        # important to "pop" added kwarg before call to parent's constructor
        self.request = kwargs.pop('request')
        super(MyModelForm, self).__init__(*args, **kwargs)

36đź‘Ť

UPDATED 10/25/2011: I’m now using this with a dynamically created class instead of method, as Django 1.3 displays some weirdness otherwise.

class MyModelAdmin(admin.ModelAdmin):
    form = MyCustomForm
    def get_form(self, request, obj=None, **kwargs):
        ModelForm = super(MyModelAdmin, self).get_form(request, obj, **kwargs)
        class ModelFormWithRequest(ModelForm):
            def __new__(cls, *args, **kwargs):
                kwargs['request'] = request
                return ModelForm(*args, **kwargs)
        return ModelFormWithRequest

Then override MyCustomForm.__init__ as follows:

class MyCustomForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        super(MyCustomForm, self).__init__(*args, **kwargs)

You can then access the request object from any method of ModelForm with self.request.

👤Chris Pratt

18đź‘Ť

The usual aproach is to store the request object in a thread-local reference using a middleware. Then you can access this from anywhere in you app, including the Form.clean() method.

Changing the signature of the Form.clean() method means you have you own, modified version of Django, which may not be what you want.

Thank middleware count look something like this:

import threading
_thread_locals = threading.local()

def get_current_request():
    return getattr(_thread_locals, 'request', None)

class ThreadLocals(object):
    """
    Middleware that gets various objects from the
    request object and saves them in thread local storage.
    """
    def process_request(self, request):
        _thread_locals.request = request

Register this middleware as described in the Django docs

👤Ber

14đź‘Ť

For Django admin, in Django 1.8

class MyModelAdmin(admin.ModelAdmin):
    ...
    form = RedirectForm

    def get_form(self, request, obj=None, **kwargs):
        form = super(MyModelAdmin, self).get_form(request, obj=obj, **kwargs)
        form.request = request
        return form

10đź‘Ť

I ran into this particular problem when customizing the admin. I wanted a certain field to be validated based on the particular admin’s credentials.

Since I did not want to modify the view to pass the request as an argument to the form, the following is what I did:

class MyCustomForm(forms.ModelForm):
    class Meta:
        model = MyModel

    def clean(self):
        # make use of self.request here

class MyModelAdmin(admin.ModelAdmin):
    form = MyCustomForm
    def get_form(self, request, obj=None, **kwargs):
        ModelForm = super(MyModelAdmin, self).get_form(request, obj=obj, **kwargs)
        def form_wrapper(*args, **kwargs):
            a = ModelForm(*args, **kwargs)
            a.request = request
            return a
    return form_wrapper
👤entropy

6đź‘Ť

The answer by Daniel Roseman is still the best. However, I would use the first positional argument for the request instead of the keyword argument for a few reasons:

  1. You don’t run the risk of overriding a kwarg with the same name
  2. The request is optional which is not right. The request attribute should never be None in this context.
  3. You can cleanly pass the args and kwargs to the parent class without having to modify them.

Lastly, I would use a more unique name to avoid overriding an existing variable. Thus, My modified answer looks like:

class MyForm(forms.Form):

  def __init__(self, request, *args, **kwargs):
      self._my_request = request
      super(MyForm, self).__init__(*args, **kwargs)


  def clean(self):
      ... access the request object via self._my_request ...

5đź‘Ť

You can’t always use this method (and its probably bad practice), but if you are only using the form in one view you could scope it inside the view method itself.

def my_view(request):

    class ResetForm(forms.Form):
        password = forms.CharField(required=True, widget=forms.PasswordInput())

        def clean_password(self):
            data = self.cleaned_data['password']
            if not request.user.check_password(data):
                raise forms.ValidationError("The password entered does not match your account password.")
            return data

    if request.method == 'POST':
        form = ResetForm(request.POST, request.FILES)
        if form.is_valid():

            return HttpResponseRedirect("/")
    else:
        form = ResetForm()

    return render_to_response(request, "reset.html")
👤Chris

3đź‘Ť

fresh cheese from cheesebaker@pypi: django-requestprovider

👤user237419

3đź‘Ť

I have another answer to this question as per your requirement you want to access the user into the clean method of the form.
You can Try this.
View.py

person=User.objects.get(id=person_id)
form=MyForm(request.POST,instance=person)

forms.py

def __init__(self,*arg,**kwargs):
    self.instance=kwargs.get('instance',None)
    if kwargs['instance'] is not None:
        del kwargs['instance']
    super(Myform, self).__init__(*args, **kwargs)

Now you can access the self.instance in any clean method in form.py

1đź‘Ť

When you want to access it through “prepared” Django class views like CreateView there’s a small trick to know (= the official solution doesn’t work out of the box). In your own CreateView you’ll have to add code like this:

class MyCreateView(LoginRequiredMixin, CreateView):
    form_class = MyOwnForm
    template_name = 'my_sample_create.html'

    def get_form_kwargs(self):
        result = super().get_form_kwargs()
        result['request'] = self.request
        return result

= in short this is the solution to pass request to your form with Django’s Create/Update views.

👤Olivier Pons

Leave a comment