Rocky Cipher
Rocky Cipher

Reputation: 129

How to add an extra button to "Change List" page in Django Admin for the particular model in particular app?

So, I want to add an extra button in my Fun model's changelist_view which when pressed, performs a function and redirects me to a new page. I don't want to have this function for every app nor do I want for every model, only the Fun model. And I will be using Django Admin to view this changelist, just a side note.

Can anyone suggest anything? Thanks.

Upvotes: 2

Views: 2980

Answers (2)

You can add extra buttons to Change List page only for a particular admin(model) in a particular app, then redirect to Change List(Current) page.

For example, there is Person model as shown below:

# "app1/models.py"

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=20)
    last_name = models.CharField(max_length=20)

And, there is Person admin as shown below:

# "app1/admin.py"

from django.contrib import admin
from .models import Person

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    list_display = ('first_name', 'last_name')
    list_editable = ('first_name', 'last_name')
    list_display_links  = None
    ordering = ('id',)

And, this below is Person Change List page:

enter image description here

Now, copy pagination.html from django/contrib/admin/templates/admin/pagination.html in your virtual environment to templates/admin/app1/person/ to add extra buttons to Change List page only for Person admin in app1 as shown below. *To add extra buttons to Change List page for all admins in all apps, copy pagination.html to templates/admin/ and for all admins in app1, copy pagination.html to templates/admin/app1/ and you can see the original pagination.html:

Django Project
 |-core
 |  └-settings.py
 |-app1
 |  |-models.py
 |  └-admin.py
 |-app2
 └-templates
    └-admin
       |-app1
       |  |-person
       |  |  └-pagination.html # Here
       |  |-animal
       |  └-fruits
       └-app2

Then, add <input ... value="{% translate 'Lowercase' %}"> and <input ... value="{% translate 'Uppercase' %}"> after <input ... value="{% translate 'Save' %}"> in pagination.html as shown below:

# "templates/admin/app1/person/pagination.html"

# ...

{% if cl.formset and cl.result_count %}
<input 
  type="submit" 
  name="_save" 
  class="default" 
  value="{% translate 'Save' %}"
>
{# ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ Here ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ #}
<input
  type="submit" 
  name="_lowercase" 
  style="background-color:yellowgreen;margin-right:5px;"
  class="default" 
  value="{% translate 'Lowercase' %}"
>
<input
  type="submit" 
  name="_uppercase" 
  style="background-color:yellowgreen;margin-right:5px;"
  class="default" 
  value="{% translate 'Uppercase' %}"
>
{# ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ Here ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ #}
{% endif %}
</p>

Then, override changelist_view() in Person admin as shown below. *You can see the original changelist_view():

# "app1/admin.py"

from django.contrib import admin
from .models import Person
from django.shortcuts import redirect
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_protect
csrf_protect_m = method_decorator(csrf_protect)

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    list_display = ('first_name', 'last_name')
    list_editable = ('first_name', 'last_name')
    list_display_links  = None
    ordering = ('id',)

    @csrf_protect_m # Here
    def changelist_view(self, request, extra_context=None):
        to = '/' + '/'.join(request.build_absolute_uri().split('/')[3:])

        if "_uppercase" in request.POST:
            for obj in self.get_queryset(request):
                obj.first_name = obj.first_name.upper()
                obj.last_name = obj.last_name.upper()
                obj.save()
            return redirect(to)

        if "_lowercase" in request.POST:
            for obj in self.get_queryset(request):
                obj.first_name = obj.first_name.lower()
                obj.last_name = obj.last_name.lower()
                obj.save()
            return redirect(to)
        return super().changelist_view(request, extra_context)

Then, you can make all first and last names uppercase or lowercase, then save by clicking on Uppercase or Lowercase buttons respectively then, redirect to Change List(Current) page as shown below:

enter image description here

enter image description here

Upvotes: 1

Pedram
Pedram

Reputation: 3920

Codes in fun/admins.py

from django.contrib import admin
from .models import Fun


@admin.register(Fun)
class FunAdmin(admin.ModelAdmin):
    change_list_template = 'fun/admin_changelist.html'

and for the fun/admin_changelist.html template:

{% extends "admin/change_list.html" %}
{% load i18n admin_static admin_list %}

{% block result_list %}
  <a href="{% url 'your-url-name here' %}">The custom button</a>
  {% if action_form and actions_on_top and cl.full_result_count %}{% admin_actions %}{% endif %}
  {% result_list cl %}
  {% if action_form and actions_on_bottom and cl.full_result_count %}{% admin_actions %}{% endif %}
{% endblock %}

Overriding the admin templates is perfectly fine, but replacing them (completely) is not recommended; So we will just override the part that we need (here I assume your button will be located in the top of the list, so I only override the result_list block)

Then you should have url with name="your-url-name here" that is connected to a view.

Check the docs for more details

Upvotes: 2

Related Questions