Django admin dynamic Inline positioning
Tags: django, python
Published: 2022-04-05 09:16:15

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](https://linevi.ch/en/django-inline-in-fieldset.html) 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](https://habr.com/ru/post/651179/) 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 `ProductModel `which have O2M relation to `ImageModel`.

 {% embed https://gist.github.com/danilovmy/a32777c08f37dedf408ea533ba00e7cb.js %}

You also need `ModelAdmins` for given models.

{% embed https://gist.github.com/danilovmy/a9050961c747340482adf7717b6db182.js %}

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

{% embed https://gist.github.com/danilovmy/df4a3cb6dadc419e1292d57032f99c2f.js %}

So far we have two simple models and basic `ModelAdmins` with an `InlineModel`. 
![Normal ModelAdmin form with InlineModel](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d13w1ijzja145owlg4ho.JPG)
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](https://docs.djangoproject.com/en/4.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.readonly_fields) 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.

{% embed https://gist.github.com/danilovmy/bd45f8e3b262f3c27d74e1a87259ec74.js %}

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8t35ss63olnlytib03q7.JPG)
When we rearrange the fields this way the inline is rendered between the fields:

{% embed https://gist.github.com/danilovmy/ff5baf296c97ea110c9e69108367d2f4.js %}

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bsjiohjczg4gks47esbd.JPG)

**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.

{% embed https://gist.github.com/danilovmy/0cdc8c33c839d45f4e5c1965244518c6.js %}

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.