How to initialize an inline formset and do some custom validations

Django admin inline prefilled

April 2020

You can find the source code in this repository

In previous tutorial we have seen how to add inlines in our django admin for Shop and Product models. This tutorial explains how to go a step further. Let’s imagine our user has set the planning for his shop and now you like to add a new product. It could be nice to avoid him setting again the planning for the product and so to initialize it with the planning of the shop as default values.

When Django initialize an inline it calls the method

def get_formset(self, request, obj=None, **kwargs):

So we need to override it and then inialize it with the values we want. To do so you can initialize the formset with an initial dictionary value

formset = super(productplanning_inline, self).get_formset(request, obj, **kwargs)
formset.__init__ = curry(formset.__init__, initial=initial)
return formset

So what we will do is to check for the connected user if he has a shop with a planning set and if so, we will initialize the product planning with items from the shop planning.

class productplanning_inline(admin.TabularInline):
    model = ProductPlanning
    extra = 7
    max_num=7

    def get_formset(self, request, obj=None, **kwargs):
        initial = []
        if request.method == "GET":
            # get user
            user = User.objects.get(id=request.user.id)
            #check if current connected user has a shop
            shops = Shop.objects.filter(user_id=user.id)
            if shops:
                # we assume there is only one shop for a user
                shop = shops[0]
                # get planning shop
                plannings = ShopPlanning.objects.filter(refShop=shop.id)
                if obj == None:
                    if plannings:
                        #no planning set and we have planning for the shop
                        import datetime
                        for planning in plannings:
                            initial.append({
                                'day': planning.day,
                                'startHour': planning.startHour,
                                'endHour': planning.endHour

                            })
                else:
                    if plannings:
                        pass
                    else:
                        import datetime
                        for planning in plannings:
                            initial.append({
                                'day': planning.day,
                                'startHour': planning.startHour,
                                'endHour': planning.endHour

                            })
        formset = super(productplanning_inline, self).get_formset(request, obj, **kwargs)
        from functools import partialmethod
        formset.__init__ = partialmethod(formset.__init__, initial=initial)
        return formset

If you try to add a product, now you should see the planning form already set

Django admin inline prefilled

To do some custom validation, we will use our own form and implement our logic code in the method clean

class ProductPlanningFormSet(BaseInlineFormSet):

    def clean(self):
        super(ProductPlanningFormSet, self).clean()

        for form in self.forms:
            if not form.is_valid():
                return
            if form.cleaned_data and not form.cleaned_data.get('DELETE'):
                startHour = form.cleaned_data['startHour']
                endHour = form.cleaned_data['endHour']
                if endHour <= startHour:
                    raise ValidationError(
                        "The end hour should be after the startHour (for day %s)" % form.cleaned_data["day"])

                if startHour >= endHour:
                    raise ValidationError(
                        "The startHour should be before the endHour (for day %s)" % form.cleaned_data["day"])

and in our inline class just specify the new created form


class productplanning_inline(admin.TabularInline):
    model = ProductPlanning
    extra = 7
    max_num=7
    formset = ProductPlanningFormSet

This code is just checking that then startHour and endHour are coherent, otherwise it will display an error message on the admin interface.

Christophe Surbier