PageView

How to Create Django Admin List Actions

How to Create Django Admin List Actions

Django Admin list actions are meant to be used to perform operations in bulk. All Django Admin list views already come with a default action “Delete selected <ModelName>s”. In this short tutorial I will guide you through the steps to create your own list actions.


Creating the Action Function

Each action in the list is a regular Python function that takes three parameters: the current ModelAdmin, a HttpRequest object (just like a view function) and a QuerySet, which is the list of selected model instances.

Those Action Functions can live inside the admin.py module of your app. But if they start to get really big, you can define them outside the admin.py.

Following is the skeleton for a Action Function:

def my_admin_action(modeladmin, request, queryset):
    # do something with the queryset

my_admin_action.short_description = 'My admin action'

Simple Example

Consider the following model and model admin:

models.py

from django.db import models

class Book(models.Model):
    HARDCOVER = 1
    PAPERBACK = 2
    EBOOK = 3
    BOOK_TYPES = (
        (HARDCOVER, 'Hardcover'),
        (PAPERBACK, 'Paperback'),
        (EBOOK, 'E-book'),
    )
    title = models.CharField(max_length=50)
    publication_date = models.DateField(null=True)
    author = models.CharField(max_length=30, blank=True)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    pages = models.IntegerField(blank=True, null=True)
    book_type = models.PositiveSmallIntegerField(choices=BOOK_TYPES)

    class Meta:
        verbose_name = 'book'
        verbose_name_plural = 'books'

admin.py

from django.contrib import admin
from .models import Book

class BookAdmin(admin.ModelAdmin):
    list_display = ['title', 'publication_date', 'author', 'price', 'book_type']

admin.site.register(Book, BookAdmin)

Let’s say we want to create a list action to apply a 10% discount to the selected books. It would be as simple as:

admin.py

import decimal
from django.contrib import admin
from .models import Book

def apply_discount(modeladmin, request, queryset):
    for book in queryset:
        book.price = book.price * decimal.Decimal('0.9')
        book.save()
apply_discount.short_description = 'Apply 10%% discount'

class BookAdmin(admin.ModelAdmin):
    list_display = ['title', 'publication_date', 'author', 'price', 'book_type']
    actions = [apply_discount, ]  # <-- Add the list action function here

admin.site.register(Book, BookAdmin)

Don’t forget to add the name of the function to the actions list, and the result will be something like this:

Custom List Action

Tip!

You can optimize the apply_discount function using a F() expression:

from django.db.models import F

def apply_discount(modeladmin, request, queryset):
    queryset.update(price=F('price') * decimal.Decimal('0.9'))

If you want to learn more about F() expressions, I have a post dedicated to that subject: Django Tips #13 Using F() Expressions


Export to CSV Example

You can also use the list action to return a HttpResponse. A simple export to CSV example:

admin.py

import decimal, csv
from django.contrib import admin
from django.http import HttpResponse
from django.db.models import F
from .models import Book

def apply_discount(modeladmin, request, queryset):
    queryset.update(price=F('price') * decimal.Decimal('0.9'))
apply_discount.short_description = 'Apply 10%% discount'

def export_books(modeladmin, request, queryset):
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="books.csv"'
    writer = csv.writer(response)
    writer.writerow(['Title', 'Publication Date', 'Author', 'Price', 'Pages', 'Book Type'])
    books = queryset.values_list('title', 'publication_date', 'author', 'price', 'pages', 'book_type')
    for book in books:
        writer.writerow(book)
    return response
export_books.short_description = 'Export to csv'

class BookAdmin(admin.ModelAdmin):
    list_display = ['title', 'publication_date', 'author', 'price', 'book_type']
    actions = [apply_discount, export_books, ]

admin.site.register(Book, BookAdmin)

You can read more about data export here in the blog:


List Action as Model Admin Method

An alternative way to implement it is by creating the list action function as a method of the admin class:

class BookAdmin(admin.ModelAdmin):
    list_display = ['title', 'publication_date', 'author', 'price', 'book_type']
    actions = ['apply_discount', export_books]

    def apply_discount(self, request, queryset):
        queryset.update(price=F('price') * decimal.Decimal('0.9'))
    apply_discount.short_description = 'Apply 10%% discount'

Pass the method name as a string to the actions list, and rename the modeladmin keyword argument to self.


Conclusions

There is much more you can do with the admin list actions. Read more in the official documentation.

The example used in this tutorial is available on GitHub: sibtc/django-admin-list-actions



Sponsored Links