Custom actions in Django Admin
Question:
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.
Answers:
Admin actions allow you to easily hook up custom actions which can be performed on selected items from the admin’s list pages.
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.
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()
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, …)
You can create custom django admin actions.
For example, I have Person
model below:
# "store/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:
# "store/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
# "store/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:
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.
Admin actions allow you to easily hook up custom actions which can be performed on selected items from the admin’s list pages.
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.
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()
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, …)
You can create custom django admin actions.
For example, I have Person
model below:
# "store/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:
# "store/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
# "store/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: