17

My question is very similar to this one: How do I access the request object or any other variable in a form's clean() method?

Except, I have the same problem with admin form. So I can't see a way to init the form myself, therefore - to pass a request to it.

Thanks beforehand.

Community
  • 1
  • 1
Wiseman
  • 1,059
  • 2
  • 13
  • 24

3 Answers3

50

Indeed, there is a way to solve your issue!

You will need to subclass form provided by ModelAdmin.get_form() and override it:

class BusinessDocumentCommentForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        # Voila, now you can access request anywhere in your form methods by using self.request!
        super(BusinessDocumentCommentForm, self).__init__(*args, **kwargs)
        if self.request.GET.get('document_pk', False):
            #Do something
    def clean(self):
        # Do something with self.request
        # etc.    
    class Meta:
        model = BusinessDocumentComment

class BusinessDocumentCommentAdmin(admin.ModelAdmin):

    form = BusinessDocumentCommentForm     

    def get_form(self, request, obj=None, **kwargs):

        AdminForm = super(BusinessDocumentCommentAdmin, self).get_form(request, obj, **kwargs)

        class AdminFormWithRequest(AdminForm):
            def __new__(cls, *args, **kwargs):
                kwargs['request'] = request
                return AdminForm(*args, **kwargs)

        return AdminFormWithRequest
Community
  • 1
  • 1
Ivan Kharlamov
  • 1,889
  • 2
  • 24
  • 33
  • This is a great way to solve this! However, I'm a little confused about your usage of the term 'MetaClass' here. As I see it, ModelFormMetaClass is just a superclass of your form. What am I missing here? – mkoistinen Nov 11 '14 at 16:02
  • 1
    @mkoistinen, you are absolutely right. This thought occurred to me some time in the past, but I was too lazy to correct the answer. Since people are still using the snippet, I have responsibility to correct it. Thanks for feedback. :-) – Ivan Kharlamov Nov 11 '14 at 20:46
  • Thanks for updating, and, I got it wrong in my comment above. Its a *subclass*. Oops! – mkoistinen Nov 11 '14 at 21:37
  • So this one creates a new class for every call of get_form? ISTR that class objects are never freed during a Python process lifetime. If true, then this solution is a memory leak… – tzot Nov 16 '17 at 12:44
  • The method get_form does not exist anymore in django >= 1.11 – melbic Feb 28 '20 at 07:48
  • 2
    @melbic I see it here in 3.0: https://docs.djangoproject.com/en/3.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_form – mkoistinen May 18 '20 at 16:59
  • 1
    This is great!. I hope Django support this requirement natively. – AndyC Jul 08 '22 at 16:52
2

This solution works for me. You can use self.request anywhere in the form to use it, including def clean(self)

class MyModelAdmin(admin.ModelAdmin):
    form = MyForm

    def get_form(self, request, *args, **kwargs):
        form = super(MyModelAdmin, self).get_form(request, *args, **kwargs)
        form.request = request
        return form
Dharman
  • 30,962
  • 25
  • 85
  • 135
Marcel Canu
  • 27
  • 1
  • 3
1

There are a number of hooks in the ModelAdmin class to allow you to do things this - look at the code in django.contrib.admin.options.

Two methods that might help you are ModelAdmin.save_form and ModelAdmin.save_model, both of which are passed the request object. So you can override these methods in your Admin subclass and do any extra processing you need.

Edited after comment

You're quite right that this won't let you validate the form dependent on the user's privileges. Unfortunately the form instantiation is buried deep within the add_view and change_view methods of ModelAdmin.

There aren't many possibilities without duplicating a lot of existing code. You could override the *_view methods; or you could try and override the modelform_factory function to return a new class with the request object baked in already; or you could try fiddling with the form class __new__ method to do the same thing, but that's tricky because of the form metaclass.

Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • Daniel, thanks for your answer! Actually, I've tried overriding these methods, berfore implementing forms... But - us I understood (from one of your answers, if I'm not mistaking) there is no way to perform validation from these methods. By "validation", I mean ability not to save some content if I don't like something and at the same time ability to give some valuable feedback. My task is - validation of admin input depending on user rights. May be I'm digging in a wrong place? – Wiseman Apr 22 '10 at 06:00
  • See my extended answer above. – Daniel Roseman Apr 22 '10 at 13:03