Django Dynamic Admin Forms

Posted on Nov. 25, 2013

You've learned how to create dynamic forms and are using them all over the place. But when you try to use them in the admin create and change forms, things don't work quite right. Let's find out how to use dynamic forms in the Django admin.

First, be sure to quickly review the previous post. It'll get you up to speed. Once you're all caught up, let's define our model in models.py:

1
2
3
4
from django.db import models

class Widget(models.Model):
    name = models.CharField(max_length=200)

Then you define your model form in forms.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from django import forms
from models import Widget

class WidgetForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(WidgetForm, self).__init__(*args, **kwargs)
        self.fields['my_extra_field'] = forms.CharField()

    class Meta:
        model = Widget

And you go to admin.py to indicate you want WidgetForm used:

1
2
3
4
5
6
7
from django.contrib import admin
from forms import WidgetForm
from models import Widget

class WidgetFormAdmin(admin.ModelAdmin):
    form = WidgetForm
admin.site.register(Widget, WidgetAdmin)

But your extra field doesn't appear. What's going on?

Learning Django? Subscribe to my Django articles, tips and tutorials.

Due to the internals of Django's admin site, the introspection actually looks at the fields defined by the model, not the form. That means that your additional fields won't actually show up.

So what can we do? You might be tempted to explicitly define it as a field, like so:

1
2
3
class WidgetFormAdmin(admin.ModelAdmin):
    form = WidgetForm
    fields = ['name', 'my_extra_field']

However, this option is validated when starting the application. And since your field is dynamic, you'll get an error. Django thinks you're referencing a field that doesn't exist on the form.

If only there were a way to do this at runtime... Wait, you're in luck, there is. Using get_fieldsets you can do just such a thing1. So we do the following:

1
2
3
4
5
6
7
8
9
class WidgetFormAdmin(admin.ModelAdmin):
    form = WidgetForm

    def get_fieldsets(self, *args, **kwargs):
        return  (
            (None, {
                'fields': ('name', 'my_extra_field'),
            }),
        )

And just like that, your additional field will show up. You may we wondering, what's the use case for something like this? The above example is contrived, essentially hard coding the additional field. But using ModelAdmin and its get_form method, you can return a truly dynamic form that varies based on the request or object being edited. You then reuse this logic to populate the fieldsets as needed.


  1. Up until Django 1.6, there is no get_fields. The latest development version does have this method. Once Django 1.7 is released, you'll be able to use this method instead of get_fieldsets if you want a flat form. 

Tags:Django

Comments

phille2Aug. 12, 2015, 10:39 a.m.

I can not get this to work in Django 1.7. Are you aware of any changes in 1.7 that would make this stop working? I still get the "

Unknown field(s) (<field>) specified for <class>. Check fields/fieldsets/exclude attributes of class <class>.".</class></class></field>

Reply
silviogutierrezAug. 12, 2015, 11:27 a.m.

I'm not sure, maybe something rewritten in the internals? I haven't had a chance to try this with Django 1.7+.

Reply
Sumit RoyJan. 18, 2017, 3:39 p.m.

so i can dynamically add fields on the basis of the value of a boolean field?

Reply
silviogutierrezJan. 19, 2017, 3:43 a.m.

That's right!

Reply

Post New Comment