55

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?

4
  • To which urls.py did you add the URL? It may be that a more general regex in Django's admin is capturing the /admin/preferences so it never reaches your URL regex. Commented Apr 7, 2012 at 11:21
  • I have only one urls.py at /mysite/mysite/urls.py, I think that this is not error in urls because I haven't error with urls patterns. Commented Apr 7, 2012 at 11:27
  • You won't get an error message if this is the case. Have you tried changing the URL to something else to see if you do get the admin page in that case? For example, ^testadmin/preferences/$ ? Commented Apr 7, 2012 at 11:30
  • With this url works good. I can see my preferencs page, but I want use ^admin/preferences/$ url. Commented Apr 7, 2012 at 11:35

8 Answers 8

39

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.

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

3 Comments

Will the page be protected or is it open to the public?
@DwayneCrooks I am years late, but it requires login by a super user created with python manage.py createsuperuser.
@ICanKindOfCode no it doesn't
29

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)

2 Comments

It's working without the security. I get an error while importing: ModuleNotFoundError: No module named 'security'
Can anyone explain to me why this solution uses both decorator and extends ModelAdmin?
13

You should be using admin's get_urls.

3 Comments

get_urls is a method of ModelAdmin which in turn needs a Model but the OP specifically wants "to create custom page for admin panel without model". (Emphasis added.)
He means the get_urls of AdminSite
6

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()

Comments

5

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

1 Comment

key for this question is admin panel *without* model, this solution is tied to a model.
5

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.

5 Comments

@decadenza is your app with dummy model listed in INSTALLED_APPS?
Yes, I'm still trying to understand what's the problem. Thanks.
Try to restart the server.
django wil look for users_dummy_model in DB
Very simple and kind of thinking inside the box while thinking outside the box. Good idea.
2

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),
    # ...
]

Comments

2

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 %}

1 Comment

I edited this, as my original, which had admin_urls = admin.site.urlslost the get_app_list override from the super class, so it was never called. In order to get get_app_list to work, and obtain the normal app models registered, I had to set the customadmin._registry to admin.site._registry.

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.