In this tutorial, we will learn how to add Inlines or inline models in Django admin. You can update a number of identical models on the same page using a Django inline model admin. In essence, it enables you to bulk edit a number of things at once.
What is Django Inline Admin?
The Django admin interface has the ability to edit models on the same page as a parent model. These are called inlines.
Django offers two InlineModelAdmin subclasses, and they are:
- TabularInline
- StackedInline
The only distinction between these two is the template that was utilized to render them.
Why we need Django inlines:
To allow a user to create and update related via Foreign Key objects from the create/update views of a referenced object, all on one page.
Let's move toward coding. We have 3 models. Product, Images, and Variants. Images and Variants have foreign relations with the Product model.
# models.py
from django.db import models
from django.utils.safestring import mark_safe
class Product(models.Model):
title = models.CharField(max_length=150)
short_description = models.TextField(max_length=100)
def __str__(self):
return self.title
class Image(models.Model):
product = models.ForeignKey(
Product, on_delete=models.CASCADE, null=True
)
image = models.ImageField(blank=True, upload_to='images')
def __str__(self):
return self.product.title
def image_tag(self):
if self.image.url is not None:
return mark_safe('<img src="{}" height="50"/>'.format(self.image.url))
else:
return ""
class Variant(models.Model):
product = models.ForeignKey(
Product, on_delete=models.CASCADE
)
size = models.CharField(max_length=100)
quantity = models.PositiveIntegerField(default=1)
price = models.DecimalField(max_digits=12, decimal_places=2)
def __str__(self):
return self.product.title
After running migrations, register your model in admin panel.
# admin.py
from django.contrib import admin
from .models import (
Product, Image, Variant
)
admin.site.register(Product)
admin.site.register(Image)
admin.site.register(Variant)
Now when you go to the admin panel you will see 3 models. And our product model will look like this:
But what do I want? I want to edit the Product, Images, and Variants model all on one page (Change Product page) and we can do this by adding Images and Variant Inlines to Product Admin model.
Now let's add the Image and Variant Inlines. Go to admin.py and add the following code.
# admin.py
from django.contrib import admin
from .models import (
Product, Image, Variant
)
admin.site.register(Image)
admin.site.register(Variant)
class ProductImageInline(admin.TabularInline):
model = Image
readonly_fields = ('id', 'image_tag',)
extra = 1
class ProductVariantInline(admin.TabularInline):
model = Variant
readonly_fields = ('id',)
extra = 1
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
inlines = [ProductVariantInline, ProductImageInline]
The code is very simple. We have ProductImageInline and ProductVariantInline classes and then we are registering these two inlines in our parent model i.e ProductAdmin.
Now go to the admin panel. And you will see image inlines and variant inlines are added to our product model admin.
That's great. Now we can edit the product and all images and variants that are linked (via foreign key) to the product model on one page.
We have added inlines using simple options but there are many advance options we can add to our inlines.
InlineModelAdmin Options:
The InlineModelAdmin
class adds or customizes the following options:
*Note: We are focusing on Variant Inline admin (ProductVariantInline) and will implement all these options in ProductVariantInline class.
InlineModelAdmin.model:
The model which the inline is using. This is required. In our case, it is model = Variant
class ProductVariantInline(admin.TabularInline):
model = Variant
InlineModelAdmin.fk_name:
The name for the foreign key on the model. Most of the time, this will be taken care of automatically, but if there are many foreign keys to the same parent model, fk name is required.
class ProductVariantInline(admin.TabularInline):
model = Variant
fk_name = "product" #optional
InlineModelAdmin.form:
The value for form
defaults to ModelForm.
But you can also add your own form by doing some customization like adding placeholders, different CSS classes, and form validations.
Explanation: How to add custom form with validations in django inline admin
InlineModelAdmin.classes:
A list or tuple containing extra CSS classes to apply to the fieldset that is rendered for the inlines. Defaults to None
. As with classes configured in fieldset, inlines with a collapse
class will be initially collapsed and their header will have a small “show” link.
class ProductVariantInline(admin.TabularInline):
model = Variant
extra = 1
classes = ('collapse', )
InlineModelAdmin.
extra:
This regulates how many more forms, in addition to the initial forms, the formset displays. to 3 by default.
class ProductVariantInline(admin.TabularInline):
model = Variant
extra = 1
InlineModelAdmin.can_delete:
specifies whether inline items may be deleted within the inline. Default to True. If you don't want to delete inlines you can set it to False.
class ProductVariantInline(admin.TabularInline):
model = Variant
can_delete = False
InlineModelAdmin.
show_change_link:
Specifies whether or not inline objects that can be changed in the admin have a link to the change form. Defaults to False.
class ProductVariantInline(admin.TabularInline):
model = Variant
show_change_link = True
readonly_fields: You can also specify readonly fields.
class ProductVariantInline(admin.TabularInline):
model = Variant
readonly_fields = ('id', 'price',)
InlineModelAdmin.
has_add_permission
(request, obj): Should return True
if adding an inline object is permitted, False
otherwise. obj
is the parent object being edited or None
when adding a new parent.
Returning this to False will disable the add inlines option.
class ProductVariantInline(admin.TabularInline):
model = Variant
extra = 1
def has_add_permission(self, request, obj):
return False
InlineModelAdmin.
has_change_permission
(request, obj=None): Should return True
if editing an inline object is permitted, False
otherwise. obj
is the parent object being edited.
Returning this function to False will not allow to change the inlines.
class ProductVariantInline(admin.TabularInline):
model = Variant
extra = 1
def has_change_permission(self, request, obj=None):
return False
InlineModelAdmin.
has_delete_permission
(request, obj=None): Should return True
if deleting an inline object is permitted, False
otherwise. obj
is the parent object being edited.
Returning this functions to False will disable the delete inline checkboxes.
class ProductVariantInline(admin.TabularInline):
model = Variant
extra = 1
def has_delete_permission(self, request, obj=None):
return False
I can't cover all options in one article. You can find more options in documentation. like max_num, min_num etc.
StackedInlines.
The difference between Tabular and stacked inline is only the template.
class ProductImageInline(admin.StackedInline):
model = Image
readonly_fields = ('id', 'image_tag',)
extra = 1
StackedInline will give inlines in this format.
If this article is helpful then you can support us by buying me a coffee.
Related Article:
Django Inline Formset Factory with Examples.
I would greatly appreciate it if you subscribe to my YouTube channel, Let's Code More