9

As you know we can define a ModelForm and then replace add and change forms by setting form attribute in modelAdmin class. for example:

class FooAdminForm(django.forms.ModelForm):
     class Meta:
         model = Foo

     def __init__(self, *args, **kwargs):
         super(FooAdminForm, self).__init__(self, *args, **kwargs)


class FooAdmin(admin.ModelAdmin):
    form = FooAdminForm

in a simple view we can initialize a form object and pass extra arguments to init function of the form. something like this:

def my_view(request):
    form = FooAdminForm(p1='aaa', p2='bbb')

and then in the init function we can access these parameter.

self.p1 = kwargs.pop('p1')
self.p2 = kwargs.pop('p2')

but how can I pass arguments to modelAdmin form? I can only pass form class to modelAdmin form attribute, and I can't initialize it like in views.

I found a solution on stackoverflow here:

Use a form with a custom __init__ in Django Admin

but it adds a dynamic attribute to form object, which I think is a little hackish. Is there a better or formal way for doing this?

Community
  • 1
  • 1
Jahan
  • 363
  • 6
  • 16

2 Answers2

3

I was looking around for a solution to this, and found a kinda-elegant solution in Django's issue tracker:

def get_form(self, request, obj=None, **kwargs):
    Form = super().get_form(request, obj=None, **kwargs)
    return functools.partial(Form, user=request.user)

https://code.djangoproject.com/ticket/27240#comment:9

So if you're using a custom form, you can simply pass it to super().get_form() using kwargs

def get_form(self, request, obj=None, **kwargs):
    kwargs['form'] = FooAdminForm
    Form = super().get_form(request, obj=None, **kwargs)
    return functools.partial(Form, user=request.user)
radoh
  • 4,554
  • 5
  • 30
  • 45
  • on django 4.0.4 the method receives 4 params: `get_form(self, request, obj=None, change=False, **kwargs)`, but it worked :D – Manuel Lazo Aug 18 '22 at 23:53
-1

Because ModelAdmin.get_form does not quite behave the same as FormView.get_form, you need to be a bit sneaky:

https://docs.djangoproject.com/en/3.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_form

class FooAdmin(admin.ModelAdmin):
    form = FooAdminForm

    def get_form(self, request, obj=None, **kwargs):
        form_class = super().get_form(request, obj, **kwargs)
        class Form(form_class):
            def __init__(self, *args, **kwargs):
                kwargs.update(foo='bar')
                super().__init__(self, *args, **kwargs)
        return Form
Matthew Schinckel
  • 35,041
  • 6
  • 86
  • 121
  • 1
    The error is `modelform_factory() got an unexpected keyword argument 'foo'` because `get_form` initializes a [modelform_factory](https://github.com/django/django/blob/c459a4661b2e96b53d3784c76e1fd5651b8cdc4a/django/contrib/admin/options.py#L702) and provides a [`ModelForm` class](https://github.com/django/django/blob/c459a4661b2e96b53d3784c76e1fd5651b8cdc4a/django/contrib/admin/options.py#L1555), not instance. [docs](https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#modelform-factory-function) – raratiru Feb 27 '20 at 21:06
  • Ah, my bad. `ModelAdmin.get_form` behaves differently to `View.get_form`... – Matthew Schinckel Mar 11 '20 at 12:44
  • You might be able to use `functools.partial` to add the keyword argument to the result of `super().get_form(...)`, but that's getting a bit hacky... – Matthew Schinckel Mar 11 '20 at 12:47
  • Not working for me, I get AttributeError: Form' object has no attribute 'get'. On Django 2.2, but I don't think that makes the difference. – gabn88 Feb 22 '21 at 14:46
  • gabn88: Are you sure you typed it in correctly? There is no `.get()` call in my answer, only `.get_form`. – Matthew Schinckel Feb 24 '21 at 03:32