How can I create custom page for django admin?

Question:

I want to create custom page for admin panel without model. For first i copy index.html to project folder:

mysite/
    templates/
        admin/
            index.html

Then add to apps block my code:

<div class="module">
    <table summary="{% blocktrans with name="preferences" %}Models available in the preferences application.{% endblocktrans %}">
        <caption><a href="preferences" class="section">{% blocktrans with name="preferences" %}Preferences{% endblocktrans %}</a></caption>
            <tr>
                <th scope="row"><a href="preferences">Preferences</a></th>
                <td><a href="preferences" class="changelink">{% trans 'Change' %}</a></td>
            </tr>
    </table>
</div>

This works good, then I create new page /templates/admin/preferences/preferences.html and
add to urls.py:

url(r'^admin/preferences/$', TemplateView.as_view(template_name='admin/preferences/preferences.html')),

And add code to preferences.html:

{% extends "admin/base_site.html" %}
{% block title %}Test page{% endblock %}

Run it and see message with error “The requested admin page does not exist.”. What I do wrong?

Asked By: Gr1N

||

Answers:

You need to add your admin URL before the URL patterns of the admin itself:

urlpatterns = patterns('',
   url(r'^admin/preferences/$', TemplateView.as_view(template_name='admin/preferences/preferences.html')),
   url(r'^admin/', include('django.contrib.admin.urls')),
)

This way the URL won’t be processed by Django’s admin.

Answered By: Simeon Visser

You should be using admin’s get_urls.

Answered By: Mitar

Here’s an example of everything that should be needed (as of Django 1.6) for a custom admin page that is linked to from a button next to the “History” button in the top right of an object’s detail page:

https://gist.github.com/mattlong/4b64212e096766e058b7

Answered By: mateolargo

Full example:

from django.urls import path
from django.contrib import admin
from django.db import models

class DummyModel(models.Model):
    class Meta:
        verbose_name = 'Link to my shiny custom view'
        app_label = 'users'  # or another app to put your custom view

@admin.register(DummyModel)
class DummyModelAdmin(admin.ModelAdmin):
    def get_urls(self):
        view_name = '{}_{}_changelist'.format(
                DummyModel._meta.app_label, DummyModel._meta.model_name)
        return [
            path('my_view/', MyCustomView.as_view(), name=view_name)
        ]

With this approach Django’s makemigrations command will create DB migration to create table for DummyModel.

Answered By: Mark Mishyn

Years go by and still a relevant answer to this can be posted.

Using Django 1.10+ you can do:

security/admin.py (this is your app’s admin file)

from django.contrib import admin
from django.conf.urls import url
from django.template.response import TemplateResponse
from security.models import Security


@admin.register(Security)
class SecurityAdmin(admin.ModelAdmin):

    def get_urls(self):

        # get the default urls
        urls = super(SecurityAdmin, self).get_urls()

        # define security urls
        security_urls = [
            url(r'^configuration/$', self.admin_site.admin_view(self.security_configuration))
            # Add here more urls if you want following same logic
        ]

        # Make sure here you place your added urls first than the admin default urls
        return security_urls + urls

    # Your view definition fn
    def security_configuration(self, request):
        context = dict(
            self.admin_site.each_context(request), # Include common variables for rendering the admin template.
            something="test",
        )
        return TemplateResponse(request, "configuration.html", context)

security/templates/configuration.html

{% extends "admin/base_site.html" %}
{% block content %}
...
{% endblock %}

See Official ModelAdmin.get_urls description (make sure you select proper Django version, this code is valid for 1.10 above)

Answered By: Rui Carvalho

If you want to create a custom page just to place there an arbitrary form to handle user input, you may give django-etc a try. There’s etc.admin.CustomModelPage you can use:

    # admin.py
    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()
Answered By: idle sign

Extending the AdminSite class worked best for me, as per django’s documentation. This solution protects the page(s) under the admin site login mechanism, and setting it up is easier than it may look:

  1. Where you have other admin code (eg. myapp/admin.py), extend the default class:
from django.contrib.admin import AdminSite

class CustomAdminSite(AdminSite):

    def get_urls(self):
        custom_urls = [
            path('admin/preferences/', self.admin_view(views.my_view)),
        ]
        admin_urls = super().get_urls()
        return custom_urls + admin_urls  # custom urls must be at the beginning


site = CustomAdminSite()

# you can register your models on this site object as usual, if needed
site.register(Model, ModelAdmin)
  1. Implement the view
def my_view(request):
    return render(request, 'admin/preferences/preferences.html')
  1. Use that admin site in urls.py, instead of the default one
from myapp import admin

# now use admin.site as you would use the default django one
urlpatterns = [
    # ...
    path('admin/', admin.site.urls),
    # ...
]
Answered By: Arnaud P

If you want to hook a page into the existing admin site, then you can do the following, which is based on #arnaud-p’s answer above. Arnaud’s answer didn’t work for me, as subclassing adminsite’s get_url function lost access to existing admin pages until I added the registry as follows.
Using the following method, your additional pages will require staff access, and you don’t need to change your urls.py, so this is great for making admin pages for apps etc… You can pass each_context in the view in order to get permissions etc.
works for django 3.2.9

In admin.py

from django.contrib import admin
from django.urls import path

from . import views

class CustomAdminSite(admin.AdminSite):
    
    def get_urls(self):
        self._registry = admin.site._registry
        admin_urls = super().get_urls() 
        custom_urls = [
            path('preferences/', views.Preferences.as_view(admin=self), name="preferences"),
        ]
        return custom_urls + admin_urls # custom urls must be at the beginning

    def get(self):
        request.current_app == self.name
        return super().get(request)

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

site = CustomAdminSite()

the view…

class Preferences(views.generic.ListView):
    admin = {}
    def get(self, request):
        ctx = self.admin.each_context(request)
        return render(request, 'admin/preferences/preferences.html', ctx)

the template…

{% extends "admin/base_site.html" %}
{% block content %}
...HELLO WORLD!
{% endblock %}
Answered By: miller the gorilla
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.