29

My django admin interface looks like this:

django-admin

Now I would like to add a view which does not correspond to a model.

I could overwrite the template of above page and add a custom link. But I think this would look ugly.

Example for overwriting admin/index.html:

{% extends "admin/index.html" %}

{% block content %}

{{ block.super }}
<div class="app-sonstiges module">
   ....
</div>
{% endblock %}

But maybe there is an official way to do add a custom view to the admin interface?

In my case I want to provide a form which can execute tcptraceroute to a remote server. The admin of my app needs this.

I used the same html tags. Now the link "tcptraceroute" looks nice, but unfortunately the messages moved down:

newest-actions-moved-down

Is there a way to get a custom part like "Sontiges ... tcptraceroute" like in the screenshot, without moving the latest actions down?

Here is how the html structure looks like. My <div class="app-sonstiges"> is below content-main:

my-part-after-content-main

3
  • That shouldn't be the case. Are you sure you didn't forget to close some tag in the HTML fragment added to index.html ? Commented Jun 4, 2019 at 8:24
  • @MarioOrlandi my part (<div class="app-sonstiges module">...<div>) is after, not inside "content-main". I don't think that there is a typo in my html Commented Jun 4, 2019 at 8:45
  • 1
    mmhh I see ... you might try to add some css styling to ".app-sonstiges" to float it on the left Commented Jun 4, 2019 at 11:47

5 Answers 5

32
+200

You have 3 options here:

Using third-party packages which provide menu

This is pretty straight forward, there are some good packages out there which support menu for admin, and some way to add your item to the menu.

An example of a 3rd party package would be django-admin-tools (last updated Dec 2021, checked April 2023). The drawback is that it is a bit hard to learn. Another option would be to use django-adminplus (last updated Oct 2019, checked April 2023) but at the time of writing this, it does not support Django 2.2.

Hacking with django admin templates

You can always extend admin templates and override the parts you want, as mentioned in @Sardorbek answer, you can copy some parts from admin template and change them the way you want.

Using custom admin site

If your views are supposed to only action on admin site, then you can extend adminsite (and maybe change the default), beside from adding links to template, you should use this method to define your admin-only views as it's easier to check for permissions this way.

Considering you already extended adminsite, now you can use the previous methods to add your link to template, or even extend get_app_list method, example:

class MyAdminSite(admin.AdminSite):
    def get_app_list(self, request):
        app_list = super().get_app_list(request)
        app_list += [
            {
                "name": "My Custom App",
                "app_label": "my_test_app",
                # "app_url": "/admin/test_view",
                "models": [
                    {
                        "name": "tcptraceroute",
                        "object_name": "tcptraceroute",
                        "admin_url": "/admin/test_view",
                        "view_only": True,
                    }
                ],
            }
        ]
        return app_list

You can also check if current staff user can access to module before you show them the links. It will look like this at then end: enter image description here

Sign up to request clarification or add additional context in comments.

4 Comments

Nice answer, thank you! Which third party package for the django admin do you recommend?
@guettli that depends on what you expect from that package, django-admin-tools is good and it keeps default django's look and feel, but it's a bid hard to learn, it also needs a little trick to work with django 2.2 right now(one solution mentioned in issues), django-suit is easier to config but its theme is a bit old looking(new version is under development), you can also use django-admin-bootstrap which supports sidebar menu, but you need to use third option from my answer to add a new menu item(I didn't test but it should work)
@PooyaMobasherBehrooz I would add those details to your answer, it will make it more complete, for sure.
the django-adminplus project is outdated and no longer compatible with +2.x versions of Django
10

Problem here that your div is not inside main-content. I suggest extending admin/index.html

Put in app/templates/admin/index.html

{% extends "admin/index.html" %}
{% load i18n static %}

{% block content %}
<div id="content-main">

{% if app_list %}
    {% for app in app_list %}
        <div class="app-{{ app.app_label }} module">
        <table>
        <caption>
            <a href="{{ app.app_url }}" class="section" title="{% blocktrans with name=app.name %}Models in the {{ name }} application{% endblocktrans %}">{{ app.name }}</a>
        </caption>
        {% for model in app.models %}
            <tr class="model-{{ model.object_name|lower }}">
            {% if model.admin_url %}
                <th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
            {% else %}
                <th scope="row">{{ model.name }}</th>
            {% endif %}

            {% if model.add_url %}
                <td><a href="{{ model.add_url }}" class="addlink">{% trans 'Add' %}</a></td>
            {% else %}
                <td>&nbsp;</td>
            {% endif %}

            {% if model.admin_url %}
                {% if model.view_only %}
                <td><a href="{{ model.admin_url }}" class="viewlink">{% trans 'View' %}</a></td>
                {% else %}
                <td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
                {% endif %}
            {% else %}
                <td>&nbsp;</td>
            {% endif %}
            </tr>
        {% endfor %}
        </table>
        </div>
    {% endfor %}

    <!-- here you could put your div -->
    <div class="app-sonstiges module">
    ....
    </div>
    <!-- here you could put your div -->
{% else %}
    <p>{% trans "You don't have permission to view or edit anything." %}</p>
{% endif %}
</div>
{% endblock %}

The another approach is to add custom app to app_list. But it is far more ugly. So I suggest overriding template.

I assume you are overriding get_urls in AdminSite.

Comments

2

If you need a custom page to handle user input (from form), you may want to give django-etc 1.3.0+ a try:

    from etc.admin import CustomModelPage

    class MyPage(CustomModelPage):
    
        title = 'My custom page'  # set page title

        # Define some fields you want to proccess data from.
        my_field = models.CharField('some title', max_length=10)

        def save(self):
            # Here implement data handling.
            super().save()

    # Register the page within Django admin.
    MyPage.register()

Comments

1

Note: I combined instructions in this answer and this answer:

  • Implemented MyAdminSite with get_urls and get_app_list
    def get_urls(self):
        urls = super().get_urls()
        my_urls = [
            path("my-func", self.my_func, name="myfunc"),
        ]
        return my_urls + urls

    def get_app_list(self, request, **kwargs):
        """Show some links in the admin UI.

        See also https://stackoverflow.com/a/56476261"""
        app_list = super().get_app_list(request, kwargs)
        app_list += [
            {
                "name": "Other actions",
                "app_label": "other_actions_app",
                "models": [
                    {
                        "name": "my name",
                        "object_name": "my object name"my_admin:myfunc"),
                        "view_only": True,
                    }
                ],
            }
        ]
        return app_list
  • Set admin_site = MyAdminSite(name="my_admin")
  • Used @admin.register(MyObject, site=admin_site) with the site parameter but using admin from django.contrib
  • Did not change settings (still uses django.contrib.admin)
  • Put this line in my overall urls.py to load my registered:
admin_site._registry.update(admin.site._registry)

I know this seems weird, but it's the only thing that really worked for me, so I wanted to write it down in case it's useful for others.

Comments

1

You can add custom URLs to the Django admin by creating a fictitious model, registering it in admin.py, and overriding the get_urls method. You can also create a separate view and add it to the standard urls.py.

  1. Create a dummy model (models.py)

Django Admin requires a model to be registered, but you can create a dummy one that won't be saved to the database:

from django.db import models

class DummyModel(models.Model):
    class Meta:
        managed = False
  1. Create custom views (views.py)

In this file, we define the functions that will process requests:

from django.http import HttpResponse
from django.contrib.admin.views.decorators import staff_member_required

@staff_member_required
def custom_admin_view(request):
    return HttpResponse("Это кастомная страница в Django Admin.")
  1. Add custom URLs to Django Admin (admin.py)

Register a dummy model and override the get_urls method:

from django.contrib import admin
from django.urls import path
from .models import DummyModel
from .views import custom_admin_view

@admin.register(DummyModel)
class CustomAdmin(admin.ModelAdmin):
    def get_urls(self):
        return [path('custom-view/', custom_admin_view, name="yourapp_dummymodel_changelist")]  # name="yourapp_yourmodel_changelist"

The (name="yourapp_yourmodel_changelist") should be specified as I indicated in the comment. This is necessary in order to replace the link on the main page so that View is used when klick on link.

Sorry for my English

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.