Understanding the Django admin

Hello!

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

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 or unfold admin theme. Other basic tweaks can be done by following solutions described in Django Admin Docs 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 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:

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! :smiley:

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

    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.

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 :smiley: ) and I am really looking forward to hearing your thoughts and suggestions on how I can get better at customizing the Django admin! :slight_smile:

1 Like

There are very good reasons why the official Django Admin docs 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.

2 Likes

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.

2 Likes

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

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 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 in python) first.

  • While in django 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 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, inlines are always placed at the bottom of the page). Javascript example :

    const selected_div = document.querySelector('div.field-car_type');
    const selected_div_parent = selected_div.parentElement.parentElement;
    const field_to_move = document.getElementById('hello_value-group');
    selected_div_parent.insertAdjacentElement('afterend', field_to_move);
    

    Also, get good at using your browser console. Thanks @Eleuthar for javascript tip.

  • Get good at overriding and extending 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” template and getting rid of {% change_form_object_tools %} 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( First step :wink: ) .

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 :slight_smile:

Lets make this thread heaven for Django admin developers :slight_smile:

1 Like

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

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.

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.

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.

1 Like