Creating nested forms in Django Admin
I recently came across the need to let admins create/edit specific objects from Django admin with an inline formset (nested forms).
In Rails, this can be accomplished by simply doing this.
accepts_nested_attributes_for :model
Things are a little bit different in Django. As opposed to Rails, Django provides an admin interface by default but getting the same to work in a nested way isn’t very obvious.
class Provider(models.Model):
name = models.CharField(max_length=100)
slug = models.CharField(max_length=100, null=True, blank=True)
url = models.CharField(max_length=255, null=True, blank=True)
image_url = models.CharField(max_length=255, null=True, blank=True)
class ProviderConfig(models.Model):
provider = models.ForeignKey(Provider)
...# regular django model fields
The problem statement is simple, I need to show and create ProviderConfig
together with the Provider
. I boiled a fresh pot of brew and started digging. A couple of hours later I was finally able to wrap my head around this.
First step in achieving the desired behaviour is to define a form (one that is to be nested) for ProviderConfig
and add the following snippet in admin.py
.
class ProviderConfigInline(admin.TabularInline):
model = ProviderConfig
extra = 5 #This says the number of forms to be show as default, here it gives 5
ProviderConfig forms
Inline formsets in django forms are defined using inlines
which can be a TabularInline
and/or StackedInline
Once we have our Inline form defined, we need to hook it to the ProviderAdmin
form that is achieved by adding the following to admin.py
.
class ProviderAdmin(admin.ModelAdmin):
fieldsets = [('Provider', {'fields': ['name', 'slug', 'url', 'image_url']})]
inlines = [ProviderConfigInline]
In the snippet above, inlines
is where we define the form that is to be nested, in our case ProviderConfigInline
. The attribute fieldsets
lets you control the model fields to be displayed on that model’s admin form/view.
Tying this all together we have the following.
from django.contrib import admin
from apps.app1.models import *
class ProviderConfigInline(admin.TabularInline):
model = ProviderConfig
extra = 5 # This says the number of forms to be show as default, here it gives 5 ProviderConfig forms
class ProviderAdmin(admin.ModelAdmin):
fieldsets = [('Provider', {'fields': ['name', 'slug', 'url', 'image_url', 'type', 'weight', 'active']})]
inlines = [ProviderConfigInline]
admin.site.register(Provider, ProviderAdmin)
We now have Provider
and ProviderConfig
as nested forms in Django admin. More details at django-docs.