[Django]-How can I add a button into django admin change list view page

74👍

When several applications provide different versions of the same
resource (template, static file, management command, translation), the
application listed first in INSTALLED_APPS has precedence.
Django documentation on INSTALLED_APPS

Make sure your app is listed before 'django.contrib.admin' in INSTALLED_APPS.

Create a change_list.html template in one of the following directories:

# Template applies to all change lists.
myproject/myapp/templates/admin/change_list.html      

# Template applies to change lists in myapp.
myproject/myapp/templates/admin/myapp/change_list.html  

# Template applies to change list in myapp and only to the Foo model.
myproject/myapp/templates/admin/myapp/foo/change_list.html  

The template should be picked up automatically, but in case it is not on one of paths listed above, you can also point to it via an admin model attribute:

class MyModelAdmin(admin.ModelAdmin):

    #... 
    change_list_template = "path/to/change_list.html"

You can lookup the contents of the original change_list.html it lives in path/to/your/site-packages/django/contrib/admin/templates/admin/change_list.html. The other answer also shows you how to format the template. Nikolai Saiko shows you how to override the relevant parts using ‘extends’ and ‘super’. Summary:

{% extends "admin/change_list.html" %} {% load i18n %} 
{% block object-tools-items %}
    {{ block.super }}
    <li>
        <a class="historylink" href="...">My custom admin page</a>
    </li>
{% endblock %}

Let’s fill href="..." with an url. The admin url names are in the namespace ‘admin’ and can be looked up like this:

{% url 'admin:custom_view' %}

When you are adding a button to change_form.html you maybe want to pass in the current object id:

{% url 'admin:custom_view' original.pk %}

Now create a custom view. This can be a regular view (just like other pages on your website) or a custom admin view in admin.py. The get_urls method on a ModelAdmin returns the URLs to be used for that ModelAdmin in the same way as a URLconf. Therefore you can extend them as documented in URL dispatcher:

class MyModelAdmin(admin.ModelAdmin):
    def get_urls(self):
        urls = super(MyModelAdmin, self).get_urls()
        my_urls = patterns('',
            url(r'^my_view/$', self.my_view, name="custom_view")
        )
        return my_urls + urls

    def my_view(self, request):
        # custom view which should return an HttpResponse
        pass

    # In case your template resides in a non-standard location
    change_list_template = "path/to/change_list.html"

Read the docs on how to set permissions on a view in ModelAdmin: https://docs.djangoproject.com/en/1.5/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_urls

You can protect your view and only give access to users with staff status:

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

@staff_member_required
def my_view(request):
    ...

You might also want to check request.user.is_active and handle inactive users.

Update: Take advantage of the framework and customise as little as possible. Many times actions can be a good alternative: https://docs.djangoproject.com/en/1.5/ref/contrib/admin/actions/

Update 2: I removed a JS example to inject a button client side. If you need it, see the revisions.

17👍

Here is another solution , without using of jQuery (like one provided by allcaps). Also this solution provides object’s pk with more intuitive way 🙂

I’ll give my source code based on that link (follow link above for more info):

I have an app Products with model Product. This code adds button “Do Evil”, which executes ProductAdmin.do_evil_view()

File products/models.py:

class ProductAdmin(admin.ModelAdmin):   
    def get_urls(self):
        urls = super().get_urls()
        my_urls = patterns('',
            (r'^(?P<pk>\d+)/evilUrl/$', self.admin_site.admin_view(self.do_evil_view))
        )
        return my_urls + urls

    def do_evil_view(self, request, pk):
        print('doing evil with', Product.objects.get(pk=int(pk)))
        return redirect('/admin/products/product/%s/' % pk)

self.admin_site.admin_view is needed to ensure that user was logged as administrator.

And this is template extention of standard Django admin page for changing entry:
File: {template_dir}/admin/products/product/change_form.html

In Django >= 1.8 (thanks to @jenniwren for this info):

{% extends "admin/change_form.html" %}
{% load i18n %}
{% block object-tools-items %}
    {{ block.super }}
    <li><a class="historylink" href="evilUrl/">{% trans "Do Evil" %}</a></li>
{% endblock %}

If your Django version is lesser than 1.8, you have to write some more code:

{% extends "admin/change_form.html" %}
{% load i18n %}
{% block object-tools %}
{% if change %}{% if not is_popup %}
<ul class="object-tools">
    <li><a class="historylink" href="history/">{% trans "History" %}</a></li>
    <li><a class="historylink" href="evilUrl/">{% trans "Do Evil" %}</a></li>
{% if has_absolute_url %}
    <li><a class="viewsitelink" href="../../../r/{{ content_type_id }}/{{ object_id }}/">{% trans "View on site" %}</a></li>
{% endif%}</ul>
{% endif %}{% endif %}
{% endblock %}  

Leave a comment