Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I add a button into django admin change list view page

I would like to add a button next to "add" button in list view in model for my model and then create a view function where I will do my stuff and then redirect user back to list view.

I've checked how to overload admin template, but I still dont know, where should I put my view function where I will do my stuff, and how can I register that view into admin urls.

There is also question about security. I would like to have that action inside admin, so if u r not logged in, u cannot use it.

I've found this, but I don't know if it's the right way: http://www.stavros.io/posts/how-to-extend-the-django-admin-site-with-custom/

like image 360
n1_ Avatar asked Jul 29 '13 08:07

n1_


People also ask

How do I customize my Django dashboard?

To customize any default page we need to create our own template directory, create the file using the same name and position in the parent directory and inform Django to use it. To go deeper, we will customize the 404 error page and configure Django to use it.

What is admin Modeladmin in Django?

One of the most powerful parts of Django is the automatic admin interface. It reads metadata from your models to provide a quick, model-centric interface where trusted users can manage content on your site. The admin's recommended use is limited to an organization's internal management tool.


2 Answers

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.

like image 116
allcaps Avatar answered Oct 08 '22 14:10

allcaps


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 %}  
like image 26
Nikolai Saiko Avatar answered Oct 08 '22 13:10

Nikolai Saiko