Custom actions in Django Admin
Asked Answered
G

5

24

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.

Grunt answered 6/4, 2009 at 22:1 Comment(0)
P
21

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

Pixie answered 6/4, 2009 at 22:30 Comment(3)
This seems to be a good solution. I don't think it would be a big problem to update my site, but how did people do this before Django 1.1?Grunt
This feature actually started out as a third-party app: code.google.com/p/django-batchadminPixie
Just an update; Django now includes Admin actions in all versions after 1.1: docs.djangoproject.com/en/1.2/ref/contrib/admin/actionsEindhoven
L
5

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:

enter image description here

I can make 2 persons uppercase as shown below:

enter image description here

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

enter image description here

I can make 2 persons lowercase as shown below:

enter image description here

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:

enter image description here

Lachrymose answered 1/1, 2023 at 11:12 Comment(0)
C
2

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, ...)
Cynde answered 5/9, 2022 at 11:33 Comment(1)
Interesting approach!Puffer
I
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.

Inspiratory answered 6/4, 2009 at 22:30 Comment(3)
What would be the difference between a ModelAdmin.save() and a Model.save()?Grunt
Meaning instead of saving in your Model in models.py you save in your admin.ModelAdmin class in admin.py.Inspiratory
See docs here: docs.djangoproject.com/en/dev/ref/contrib/admin/…Inspiratory
M
1

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()
Maddening answered 30/12, 2014 at 15:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.