# Understanding the Django admin

**URL:** <https://forum.djangoproject.com/t/understanding-the-django-admin/28151>\
**Category:** The Admin\
**Created:** [February 16, 2024, 5:24pm UTC](https://forum.djangoproject.com/t/understanding-the-django-admin/28151 "2024-02-16T17:24:34Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![aze](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/aze/32/19166_2.png) [@aze](https://forum.djangoproject.com/u/aze)\
**Post date:** [February 16, 2024, 5:24pm UTC](https://forum.djangoproject.com/t/understanding-the-django-admin/28151/1 "2024-02-16T17:24:34Z")

</div>

Hello!

My second post here, I am happy that I have found this community 🙂

I was told that not many Django developers tweak the admin that much. I myself have not done it in the past for my small personal Django projects. There was simply no need.

It’s quite understandable, usually you don’t send users to the **/admin** , you build the UI for them to use and that is what Django is all about(at least from my perspective), admin for the admins/developers, UI for the users.

But **what if** only the basic CRUD operations need to be performed by the user and instead of building a custom UI with custom list/detail views/urls/templates/etc, you have none of that and the users are allowed to use the **/admin** for CRUD tasks? Users are able to delete/modify/add entries in the DB directly from the **/admin** with the built in CRUD functionality of the admin. Of course such app is not to be exposed to the external internet, it could used internally in the company, the primary point of entry to the web app is over the **/admin**.

If there is a need to customize the admin interface a bit, you can pop a [jazzmin](https://github.com/farridav/django-jazzmin) or [unfold](https://github.com/unfoldadmin/django-unfold) admin theme. Other _basic tweaks_ can be done by following solutions described in [Django Admin Docs](https://docs.djangoproject.com/en/5.0/ref/contrib/admin/#module-django.contrib.admin) or googling your problem, someone most likely has solved in the past.

In this scenario, users are happy, but developer is even happier, since without much effort at all there is a fully functioning CRUD site.

But **DEVELOPER DOES NOT LAUGH THAT MUCH ANYMORE** when the users start using the site over the **/admin** , and requests start coming in… all of a sudden this field has to have this color, that field has to be moved to the left a bit more, this field must be replaced with another field, this input field should have autocomplete functionality, there must be a custom button here or there, and the save button should not only save data when pressed, but also should calculate certain values taken from the currently opened instance and display results over there…

Let’s remember that developer did not built the Django admin by himself, he/she simply stood on the shoulder of giants that have created the Django admin. So now to fulfill users needs, the developer has to modify/override parts of a quite large Django admin codebase by himself and create all these custom solutions.

Instead of giving up on this idea of letting the users use the **/admin** , developer sees this as a **great learning opportunity**. Starts researching:

- **youtube** - no one goes deep in customizing the django admin
- **google** - small snippets of code for custom solutions of other people, but no general “this is how Django admin works” document/book/podcast, paper or a course
- **Django docs** - it writes all the attributes/methods, basically source code with nice comments and links, but still, a lot of it is not really understandable for the beginner, no clear picture how everything interconnects
- **IDE debuggers** - can any information be taken from them about the django admin by setting breakpoints in the admin.py for example?
- **django debug toolbar** - shows the templates that are being rendered for the django admin, okay, now we know what template has to be overridden to change the layout

Or these days one can ask chatgpt for help… for example the other day, while trying to solve this - [Show all the fields in inline of the many-to-many model instead of a simple dropdown](https://forum.djangoproject.com/t/show-all-the-fields-in-inline-of-the-many-to-many-model-instead-of-a-simple-dropdown/28062) I have asked chatgpt for a solution and it gave me something I would not have figured out myself, for example this piece of code below, it helped me to override the built in dropdown field(impossible to use with thousands of entries to choose from) of the inline and implement autocomplete search:

```auto
class VesselInline(admin.TabularInline):

    model = Deal.vessels.through
    extra = 0

    def get_formset(self, request, obj=None, **kwargs):
        formset = super().get_formset(request, obj, **kwargs)
        formset.form.base_fields['vessel'].widget = autocomplete.ModelSelect2(
            url='vessel-autocomplete',
            attrs={
                'data-placeholder': 'Search for a vessel', 
                'data-minimum-input-length': 2
                }
        )
        return formset

```

How did it know that `get_formset` method has to be modified…?! And not `get_form` or `get_fieldsets`?

How did it know that `formset`, then `form`. then vessel field from `base_fields` has to be taken and the `widget` of it has to be overridden with the autocomplete functionality… Like the relationship between all these? Where is it written? Source code?

But if I look at `InlineModelAdmin` class in **contrib/admin/options.py** and `get_formset` method of it, it is not really clear(for me) that it has to be done this way… OOP is my weak spot for now, I should get better with it, perhaps things would get clearer. But do you see this as a solution by simply looking at `get_formset` method? If yes, you are the person I must talk to! 😃

Here is the source code of **contrib/admin/options.py** `get_formset` method of `InlineModelAdmin` class:

```auto
    def get_formset(self, request, obj=None, **kwargs):
        """Return a BaseInlineFormSet class for use in admin add/change views."""
        if "fields" in kwargs:
            fields = kwargs.pop("fields")
        else:
            fields = flatten_fieldsets(self.get_fieldsets(request, obj))
        excluded = self.get_exclude(request, obj)
        exclude = [] if excluded is None else list(excluded)
        exclude.extend(self.get_readonly_fields(request, obj))
        if excluded is None and hasattr(self.form, "_meta") and self.form._meta.exclude:
            # Take the custom ModelForm's Meta.exclude into account only if the
            # InlineModelAdmin doesn't define its own.
            exclude.extend(self.form._meta.exclude)
        # If exclude is an empty list we use None, since that's the actual
        # default.
        exclude = exclude or None
        can_delete = self.can_delete and self.has_delete_permission(request, obj)
        defaults = {
            "form": self.form,
            "formset": self.formset,
            "fk_name": self.fk_name,
            "fields": fields,
            "exclude": exclude,
            "formfield_callback": partial(self.formfield_for_dbfield, request=request),
            "extra": self.get_extra(request, obj, **kwargs),
            "min_num": self.get_min_num(request, obj, **kwargs),
            "max_num": self.get_max_num(request, obj, **kwargs),
            "can_delete": can_delete,
            **kwargs,
        }

        base_model_form = defaults["form"]
        can_change = self.has_change_permission(request, obj) if request else True
        can_add = self.has_add_permission(request, obj) if request else True

        class DeleteProtectedModelForm(base_model_form):
             # removed this part

        defaults["form"] = DeleteProtectedModelForm

        if defaults["fields"] is None and not modelform_defines_fields(
            defaults["form"]
        ):
            defaults["fields"] = forms.ALL_FIELDS

        return inlineformset_factory(self.parent_model, self.model, **defaults)

    def _get_form_for_get_fields(self, request, obj=None):
        return self.get_formset(request, obj, fields=None).form

```

I don’t want to depend on chatgpt spitting out these solutions for me and me now knowing how to get them myself. But I imagine this kind of solution would come to me only after reading Django docs three times, becoming an expert in OOP and building many custom admin projects by myself over many years to start seeing the patterns of how it all interconnects.

- There is such site to understand class based views better [https://ccbv.co.uk/](https://ccbv.co.uk/), anything similar for django admin?
- there is such attempt to try to understand the admin - [Diagram of django view lifecycle - #4 by Eleuthar](https://forum.djangoproject.com/t/diagram-of-django-view-lifecycle/14894/4), printing is a way to go?

Is **DOING** projects the only way to grasp the Django admin source code? Solving problems one by one with the help of chatgpt until you solve enough to know what is possible and how it is possible to customize the admin? Or there are some resources/tools/strategies you use to get a grasp of it and work on a daily basis?

As you might understood I am really interested in learning everything there is about Django(starting with the code that chatgpt has written above 😃 ) and I am really looking forward to hearing your thoughts and suggestions on how I can get better at customizing the Django admin! 🙂

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [February 16, 2024, 5:52pm UTC](https://forum.djangoproject.com/t/understanding-the-django-admin/28151/2 "2024-02-16T17:52:44Z")

</div>

There are very good reasons why the [official Django Admin docs](https://docs.djangoproject.com/en/5.0/ref/contrib/admin/) say this in the very first two paragraphs:

> The admin’s recommended use is limited to an organization’s internal management tool. It’s not intended for building your entire front end around.

> If you need to provide a more process-centric interface that abstracts away the implementation details of database tables and fields, then it’s probably time to write your own views.

Yes, you can spend a _lot_ of time learning about how to customize the admin.

**However** , what I have learned over the years is that in the time you’ve spent learning how to do this, you generally could have completed your project twice-over by ignoring it.

I’ve seen this mistake made in a number of cases, and I know others who have encountered the same problem.

Just like you describe - the admin is deceptively simple to get _something_ up and running. But as additional features and functionality are requested, you find yourself working more and more around the limitations of the admin. Soon, you reach a point where you’re spending far more time trying to figure out how to get the admin to do something than actually doing it.

What you end up with can quite frankly be described as a generally-unmaintainable mess.

So what I _really_ suggest is the same thing that the authors of Django recommend.

Don’t do this. If you’re lucky and stay with Django long enough, you’ll realize how much of a mistake this can be.

---

<div class="post-metadata">

**Author:** ![Eleuthar](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/eleuthar/32/15916_2.png) [@Eleuthar](https://forum.djangoproject.com/u/Eleuthar)\
**Post date:** [February 17, 2024, 7:10am UTC](https://forum.djangoproject.com/t/understanding-the-django-admin/28151/3 "2024-02-17T07:10:30Z")

</div>

Blessed be the Django admin developers!

Yes! Ken is so incredibly right, I wish I knew that sooner, but at least now I got a lot of experience on AdminSite.

Lucky for you, @aze, I am one of the select few developers that have worked and still work with Django admin and tweaked it to fit my every need.

I admit, every now and then I had some challenges which I overcame in a more elegant fashion than you could find on any forum on this God forsaken internet :).

With all modesty - ChatGPT would never be able to provide the solutions I came up with.

**ChatGPT is not designed to assist in programming, only in topics like literature, generating emails and stuff that requires little to no logic.**  
**Do not take for granted any response from ChatGPT !**  
**That output must be validated and if you don’t know a certain topic, you cannot tell for sure if that response is correct.**

I agree with your fair point, that if your specifications are minimal, Django admin is more than enough, the best tool one could ever have, to a certain extent.  
Beyond that, you should consider making your own views if the requirements get bigger and more complex.

I found myself working way longer and harder to overcome the limitations of AdminSite, luckily I know some Javascript and I passed a lot of the workload from Django to the front-end, which is ideal when possible.

That being said, you can reach out to me on Discord, to help you on your endeavour.  
My username is “eleuthar”.  
I know how hard it is to face alone difficult tasks, therefore I wish to be the help I once needed direly.

---

<div class="post-metadata">

**Author:** ![aze](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/aze/32/19166_2.png) [@aze](https://forum.djangoproject.com/u/aze)\
**Post date:** [February 22, 2024, 1:08pm UTC](https://forum.djangoproject.com/t/understanding-the-django-admin/28151/4 "2024-02-22T13:08:26Z")

</div>

Thank you for the insights @KenWhitesell @Eleuthar! It is good to know that I am not the only one struggling with Django Admin 🙂

If I could choose, I would attempt to rebuild the project from scratch(as the authors of Django recommend).

The problem is, Django Admin IS the UI for the current project and it will be. It is not for me to decide at this point, too much time in invested already. As someone who has recently been assigned to this project, I have to go with it.

So I **will** have to be making changes and I **will** need the tools for that.

Sadly currently I did not find a guide that provides you the tools. Perhaps we could turn this thread into one, just to start? It would be helpful for me and for others reading this thread.

**Tools to understand/grasp the Django admin:**

- When we install Django package into our env, we can navigate to it’s source code. Either over vscode by clicking [Ctrl+Click](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-definition) or by physically navigating to the location of the django admin source files in _django/contrib/admin_ and looking through the source code there. Best if you understand the OOP([Object-oriented programming](https://en.wikipedia.org/wiki/Object-oriented_programming) in python) first.

- While in [django shell](https://docs.djangoproject.com/en/5.0/ref/django-admin/#shell), do an import - `from yourapp.models import YourModelName`, then run `print(YourModelName._meta. __dict__ )`. This will print all the “opts” of the Model? Did not use this yet, but good to know. Have to look into opts(options?) and understand what they hold and how they can be useful. Thanks @Eleuthar for this tip.

- You can "rip off " the [inlines](https://docs.djangoproject.com/en/5.0/ref/contrib/admin/#inlinemodeladmin-objects) off the bottom of the change/add page by using a few lines of Javascript([there is no default built in django option for that](https://stackoverflow.com/questions/18390627/how-can-i-display-a-django-admin-inline-model-within-a-fieldset), inlines are always placed at the [bottom of the page](https://code.djangoproject.com/ticket/4848)). Javascript example :

- Get good at [overriding](https://docs.djangoproject.com/en/5.0/howto/overriding-templates/) and [extending](https://docs.djangoproject.com/en/5.0/ref/templates/language/) templates. You will want to do it if at some point you will want to delete/add desired elements. For example to hide the “history” button in Django admin, I found out that overriding the default “[change\_form.html](https://github.com/django/django/blob/main/django/contrib/admin/templates/admin/change_form.html)” template and getting rid of [{% change\_form\_object\_tools %}](https://github.com/django/django/blob/1fd57f2a2175bddabac01716716b2069ef8c8acd/django/contrib/admin/templates/admin/change_form.html#L31) was a clean way to get rid of the button. This removes history button for all the models. If I want to remove the button in a particular model, in `admin.py` I have to use `change_form_template = "your_template_name"`. Found this out by reading the [source code](https://github.com/django/django/blob/1fd57f2a2175bddabac01716716b2069ef8c8acd/django/contrib/admin/options.py#L666)( First step 😉 ) .

I will come to this place to add new things I learn. If you have some - feel free to write yours! (I am sure you have more tips, tricks and tools). However small or big, lets write them here 🙂

Lets make this thread heaven for Django admin developers 🙂

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [February 22, 2024, 5:47pm UTC](https://forum.djangoproject.com/t/understanding-the-django-admin/28151/5 "2024-02-22T17:47:25Z")

</div>

> [@aze](#):
>
> If I could choose, I would attempt to rebuild the project from scratch(as the authors of Django recommend).

Nothing says it needs to be rebuilt from scratch. Attempting to present the situation in this way is a false dichotomy.

> [@aze](#):
>
> The problem is, Django Admin IS the UI for the current project and it will be.

This does not prevent you from writing your own views that look like the Admin. The templates, CSS, and JavaScript widgets are available for use. Those components are not the problem.

> [@aze](#):
>
> It is not for me to decide at this point, too much time in invested already.

This is an example of a “sunk cost” fallacy. The fact that a bad decision was made in the past does not justify continuing to make that same bad decision today.

> [@aze](#):
>
> As someone who has recently been assigned to this project, I have to go with it.

As a professional, it’s also your responsibility to identify these mistakes and to demonstrate that there are better ways of handling this.

For example, if someone wants to use runserver as their production web server, I’m going to (and have) comment strongly against it. It’s a fundamental mistake that just shouldn’t be made. And if someone inherits a project where that _was_ done, I’m going to continue to recommend that it be fixed appropriately, regardless of what effort has been expended in making it work so far.

Now, fundamentally, the decision is yours. Heaven knows _I’ve_ got a number of Django deployments that violate any number of Django “best practices”. (For example, there _are_ situations where using runserver as the production web server is an appropriate choice) However, what I’m _not_ going to do is identify this as anything other than a unique set of circumstances and not something that is recommended as a routine option.

And that’s what I want to continue to make clear here.

Sure, you can do this. But I’m going to continue to express my opinion here that it’s fundamentally a poor idea. The “Technical Debt” you create by focusing everything through the admin can be amazingly substantial.

I don’t want anyone reading this thread to think “This is a great idea! I want to do this!” I want their impression to be “Yes, I can do this, but I really should find a different way first”, especially when the alternative takes less time, and creates a more maintainable and sustainable product. The “two-years-from-today-you” will thank you.
