DEV Community

Cover image for Django admin dynamic Inline positioning
Maxim Danilov
Maxim Danilov

Posted on

Django admin dynamic Inline positioning

Recently I've received an interesting request from a client about one of our Django projects.
He asked if it would be possible to show an inline component above other fields in the Django admin panel.

At the beginning I thought, that there shouldn't be any issue with that.
Though there was no easy solution other then installing another battery to the project. My gut feeling told me, there were another way around that problem.

The first solution I found was from 2017. It had too much code for such a simple task.

Our executive lead programmer, Maxim Danilov found quite a short solution. He published his work online in russian about a month ago.

I´d like to share these ideas with englisch speaking Django community in order to help others simplify their code. It might come handy for such a "simple" at first glance issues.

Long story short, let's dive into the code:

Imagine you are building an E-commerce project. You have a ProductModelwhich have O2M relation to ImageModel.

You also need ModelAdmins for given models.

Now let's create a simple inline to put into our ProductModelAdmin.

So far we have two simple models and basic ModelAdmins with an InlineModel.
Normal ModelAdmin form with InlineModel
How should we squeeze that Inline above or in between the two fields of the ProductModelAdmin?

I suppose, you are familiar with the added field concept in ModelAdminForm. You can create a method in the ModelAdmin to display the response of the method in the form as a readonly field.
Keep in mind, that the rendering sequence of the ModelAdmin will create the InlineModels first, then render AdminForm and after that render the InlineForms.

We can use that to rearrange the order of Inlines and fields.

We use the render_change_form to get the objects request and response.
We use those objects in the image_inline method to take one inline_formset from the list of inline_admin_formsets that have not been processed yet, and render InlineFormset.

After the change_form rendering the remaining inline_admin_formsets will be rendered, in case if the ModelAdmin still has some.

Now we can use the method image_inline to determine the position of our InlineFormset.
With the code-snippet above the inline element will be placed above all other fields.

Inline on top
When we rearrange the fields this way the inline is rendered between the fields:

Of course Django admin adds a lable infront of the Inline with the name of the method, but that can be easily removed by some simple CSS in Media attribute of ProductModelAdmin.

Inline in between

This solution has one fatal error! Every Django ModelAdmin is singelton, that is why we can not use ModelAdmin.self as a container in render_change_form!

It is possible to change the ModelAdmins singleton´s behavior with a Mixin, staying inline with the concept of Djangos GCBV. We will take a closer look at it in my next article.

It simply means, that we can't use the instance of the ModelAdmin as a container to save our request and response.
The solution is to save those objects in the AdminForm instance.

The argument obj is not always given in render_change_form (i.e. add new object). That is why we have to get it from the ModelForm, which is wrapped into the AdminForm.

Now we can set our request and response as attributes of the ModelForm instance and use those in image_inline.

Summing up the above: you don't have to install another battery to your project to solve a simple problem. Sometimes you need to dig deep enough into the framework that you use, and find a simple, short and quick solution.

I´d like to thank Martin Achenrainer the intern of wPsoft for contributing to this article and translating it.

Top comments (0)