Django Admin nested inline
I need a nested django admin inline, which I can include the date field inlines in an other inline like below.
I have the models below:
class Person(models.Model):
name = models.CharField(max_length=200)
id_no = models.IntegerField()
class Certificate(models.Model):
cerfificate_no = models.CharField(max_length=200)
certificate_date = models.DateField(max_length=100)
person = models.ForeignKey(Person)
training = models.CharField(max_length=200)
class Training_Date(models.Model):
date = models.DateField()
certificate = models.ForeignKey(Certificate)
and the admin below:
class CertificateInline(admin.StackedInline):
model = Certificate
class PersonAdmin(admin.ModelAdmin):
inlines = [CertificateInline,]
admin.site.register(Person,PersonAdmin)
but i need to include the Training_Date model as inline which is part of Certificate admin inline.
Any idea ?
Solution 1:
There has been some movement in https://code.djangoproject.com/ticket/9025 recently, but I wouldn't hold my breath.
One common way around this is to link to an admin between first and second (or second and third) level by having both a ModelAdmin
and an Inline for the same model:
Give Certificate
a ModelAdmin
with TrainingDate
as an inline. Give CertificateInline
an additional field "Details" which is a link to its ModelAdmin
change form.
models.py:
from django.core import urlresolvers
class Certificate(models.Model):
# ...
def changeform_link(self):
if self.id:
# Replace "myapp" with the name of the app containing
# your Certificate model:
changeform_url = urlresolvers.reverse(
'admin:myapp_certificate_change', args=(self.id,)
)
return u'<a href="%s" target="_blank">Details</a>' % changeform_url
return u''
changeform_link.allow_tags = True
changeform_link.short_description = '' # omit column header
admin.py:
# Certificate change form has training dates as inline
class TrainingDateInline(admin.StackedInline):
model = TrainingDate
class CertificateAdmin(admin.ModelAdmin):
inlines = [TrainingDateInline,]
admin.site.register(Certificate ,CertificateAdmin)
# Person has Certificates inline but rather
# than nesting inlines (not possible), shows a link to
# its own ModelAdmin's change form, for accessing TrainingDates:
class CertificateLinkInline(admin.TabularInline):
model = Certificate
# Whichever fields you want: (I usually use only a couple
# needed to identify the entry)
fields = ('cerfificate_no', 'certificate_date', 'changeform_link')
readonly_fields = ('changeform_link', )
class PersonAdmin(admin.ModelAdmin):
inlines = [CertificateLinkInline,]
admin.site.register(Person, PersonAdmin)
Solution 2:
More universal solution
from django.utils.safestring import mark_safe
from django.urls import reverse
class EditLinkToInlineObject(object):
def edit_link(self, instance):
url = reverse('admin:%s_%s_change' % (
instance._meta.app_label, instance._meta.model_name), args=[instance.pk] )
if instance.pk:
return mark_safe(u'<a href="{u}">edit</a>'.format(u=url))
else:
return ''
class MyModelInline(EditLinkToInlineObject, admin.TabularInline):
model = MyModel
readonly_fields = ('edit_link', )
class MySecondModelAdmin(admin.ModelAdmin):
inlines = (MyModelInline, )
admin.site.register(MyModel)
admin.site.register(MySecondModel, MySecondModelAdmin)
Solution 3:
pip install django-nested-inline
This package should do what you need.
Solution 4:
AFAIK, you can't have a second level of inlines in the default Django admin.
The Django admin is just a normal Django application, so nothing prevents you from implementing a second level of nested forms, but IMHO it would be a kind of convoluted design to implement. Perhaps that is why there is no provision for it.
Solution 5:
Nested inlines are provided at: https://github.com/BertrandBordage/django-super-inlines/
pip install django-super-inlines