1

How to add or edit models and related model on same page ?

Product selected

April 2020

Sometimes when editing or adding a model in the admin, we would like to add values for related models at the same time. That’s the purpose of this tutorial

You can find the source code in this repository

In previous tutorials we used the following models

# *coding: utf-8*
from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import User

class Shop(models.Model):
    user = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE)
    name = models.TextField()
    email = models.CharField(max_length=200)
    address = models.CharField(max_length=1024,null=True, blank=True)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)
    def __str__(self):
        return u'%s' % (self.name)

class Product(models.Model):
    user = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE)
    refShop = models.ForeignKey(Shop, db_index=True,on_delete=models.CASCADE)
    title = models.TextField(max_length=65)
    price = models.FloatField(default=0)
    withEndDate = models.BooleanField(default=False)
    endDate = models.DateField(blank=True,null=True)
    description = models.CharField(max_length=65, default='', null=True, blank=True)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)
    available = models.BooleanField(default=True)
    def __str__(self):
        return u'%s - %s' % (self.refShop,self.title)

Now we will create two more models related to these Shop and Product models. Let’s imagine that our shop have an opening day planning and our product is available per day with a specific planning too. Let’s create the new models to express that.

# Insert after Shop model

WEEKDAYS = [
    (1, ("Monday")),
    (2, ("Tuesday")),
    (3, ("Wednesday")),
    (4, ("Thursday")),
    (5, ("Friday")),
    (6, ("Saturday")),
    (0, ("Sunday")),
]

class ShopPlanning(models.Model):
    class Meta:
        ordering = ['refShop', 'day', 'startHour']
    refShop = models.ForeignKey(Shop,on_delete=models.CASCADE,related_name='shopplanning')
    day = models.IntegerField(choices=WEEKDAYS)
    startHour = models.TimeField()
    endHour = models.TimeField()

    def __str__(self):
          return ("%(premises)s %(day)s (%(from)s - %(to)s)") % {
                'premises': self.refShop,
                'day': self.day,
                'from': self.startHour,
                'to': self.endHour
          }

# Insert after Product model 

class ProductPlanning(models.Model):
    class Meta:
        ordering = ['refProduct', 'day', 'startHour']
    refProduct = models.ForeignKey(Product,on_delete=models.CASCADE,related_name='productplanning')
    day = models.IntegerField(choices=WEEKDAYS)
    startHour = models.TimeField()
    endHour = models.TimeField()

    def __str__(self):
          return ("%(premises)s %(day)s (%(from)s - %(to)s)") % {
                'premises': self.refProduct,
                'day': self.day,
                'from': self.startHour,
                'to': self.endHour
          }

Ok so now while creating a new shop/product, it would be nice to create the planning at the same time on the same screen. That is the purpose of inlines instruction. First we create an inline class in the admin

class shopplanning_inline(admin.TabularInline):
    model = ShopPlanning
    extra = 7
    max_num=7

And then we modify our ShopAdmin class to declare our inline

class ShopAdmin(admin.ModelAdmin):
    inlines = [shopplanning_inline]
    fieldsets = [
        (None, {'fields': ['user','name','email','address']}),
    ]
    list_display = ('user','name', 'email',  )
    ordering = ('user',)

    def get_queryset(self, request):
        qs = super(ShopAdmin, self).get_queryset(request)
        if request.user.is_superuser:
            return qs
        else:
            return qs.filter(user=request.user)

With that in place, we tell the admin to display shop planning items on the shop creation/editing page.

Django admin inlines

While creating the shopplanning_inline class, i inherit it from the admin.TabularInline class which produce a display like the image below. But there is another display mode which can be used by inheriting from the admin.StackedInline class. The extra=7 instruction means that we want 7 items displayed on the interface and the max=7 means that only 7 items can be added. Try to set extra to a value below 7 and you will see a nice “Add another shop planning” button to the interface to add more items until you reach the max number.

Notice: if you have done previous tutorials and connected to the admin with your usera or userb you will not see the inlines. This is because you need to set the permission to the usera/userb for our new models.

Christophe Surbier