Custom Actions in Django Admin

In my Django app, I have a Newsletter model. Now I'd like to be able to send the newsletter (and even resend it) from Django Admin.

I could do this with a hook on the Model.save() method but is there another way that is not tied to the Model?

Thanks.

5 Answers

Admin actions allow you to easily hook up custom actions which can be performed on selected items from the admin's list pages.

3

You can create custom django admin actions.

For example, I have Person model below:

# "my_app1/models.py"

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=30)
    
    def __str__(self):
        return self.name

Now, I created uppercase() and lowercase() admin actions in Person admin as shown below. *actions = ("uppercase", "lowercase") is necessary to display uppercase() and lowercase() admin actions:

# "my_app1/admin.py"

from django.contrib import admin, messages
from .models import Person

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    
    actions = ("uppercase", "lowercase") # Necessary 
    
    @admin.action(description='Make selected persons uppercase')
    def uppercase(modeladmin, request, queryset):
        for obj in queryset:
            obj.name = obj.name.upper()
            obj.save()
            messages.success(request, "Successfully made uppercase!")

    @admin.action(description='Make selected persons lowercase')
    def lowercase(modeladmin, request, queryset):
        for obj in queryset:
            obj.name = obj.name.lower()
            obj.save()
            messages.success(request, "Successfully made lowercase!")

Then, if I select 2 persons and Make selected persons uppercase then click on Go as shown below:

I can make 2 persons uppercase as shown below:

Then, if I select 2 persons and Make selected persons lowercase then click on Go as shown below:

I can make 2 persons lowercase as shown below:

In addition, if I remove @admin.action() as shown below

# "my_app1/admin.py"
    
    # ...
    
    # @admin.action(description='Make selected persons uppercase')
    def uppercase(modeladmin, request, queryset):
        for obj in queryset:
            obj.name = obj.name.upper()
            obj.save()
            messages.success(request, "Successfully made uppercase!")

    # @admin.action(description='Make selected persons lowercase')
    def lowercase(modeladmin, request, queryset):
        for obj in queryset:
            obj.name = obj.name.lower()
            obj.save()
            messages.success(request, "Successfully made lowercase!")

Actual function names are displayed as shown below:

2022 Update:

django.__version__ == 4.1 and maybe ealier.

You can add a custom action at /admin/.../change by overriding django.contrib.admin.options.ModelAdmin._changeform_view:

from django.contrib import admin 

class MyModelAdmin(admin.ModelAdmin): 

    def _changeform_view(self, request, object_id, form_url, extra_context):

        if '<your-action>' in request: 
            # 1. check permissions
            # 2. do your thing 
            print(request)
        
        return super()._changeform_view(request, object_id, form_url, extra_context)

You can now add simple sumbit button to your form with your custom action:

<input type="submit" name="<your-action>" value="<button-title>">

Advantage

  • Zero javascript.
  • Authentication (CSRF) protected.
  • All stock admin actions will work with your form (add, save and continue, ...)
1

If you are doing it from the admin then you'll need to override the save() method, but it can be the AdminModel save... doesn't need to be the full Model save.

However, if you are emailing a lot of emails, a better approach would be to install django-mailer which puts emails into a queue for later processing and then provides you with a new management command: send_mail.

So once you're ready to send the newsletter out you can manually run python manage.py send_mail. Any emails with errors will be moved to a deferred queue where you can retry sending them later.

You can automate this by running manage.py send_mail from cron.

If you really want to get fancy and do it from admin site, install django-chronograph and set up your send_mail schedule from there.

3

You can try this YouTube tutorial. Just change:

def available(modeladmin, request, queryset):
    queryset.update(status='ava')

def not_available(modeladmin, request, queryset):
    queryset.update(status='not')

to something like this:

def send(modeladmin, request, queryset):
    for data in queryset:
        subject=data.title
        message=data.mesage
           
        for d in Users.objects.filter(newsletter=True):
            email=d.email
            sendemail = EmailMessage(
                subject, message + unsubscribe,
                '[email protected]',
                [email], [],
                headers = {'Reply-To': '[email protected]'}
            )
            sendemail.content_subtype = "html" 
              
            sendemail.send()
Elena Rostova

Elena Rostova

Lead Health, Wellness & Medical Journalist

Elena Rostova holds a Master's degree in Public Health Journalism. She covers groundbreaking medical research, holistic wellness trends, mental health awareness, and nutritional science.

Share this article