Django Dynamic Admin Forms

Posted on Nov. 25, 2013, 11:30 a.m.

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