How to add a custom button to Django admin - python

I want to add a custom button right next to the SAVE button in my admin change form. I tried to add a button in submit_line.html
<input type="submit" action = "admin/{{id??}}" value="show PDF" name="show PDF{{ onclick_attrib }}/>
But, it doesn't redirect to my given page and I don't know how to pass the current id.
Actually, I dont think it's a good idea to change submit_line.html anyways, because I only want it in one model. Is there a better solution for that?

I think you can add your own logic. Here you check app verbose name, to show that button only in your app:
{% if opts.verbose_name == 'Your app verbose name' %}<input type="submit" value="{% trans 'Show PDF' %}" name="_show_pdf" {{ onclick_attrib }}/>{% endif %}
and in your admin.py:
class YourAdmin(admin.ModelAdmin):
def response_change(self, request, obj):
if '_continue' in request.POST:
return HttpResponseRedirect(obj.get_admin_url())
elif '_show_pdf' in request.POST:
# Do some stuf here( show pdf in your case)

Django1.10:
I would do it as follows, to avoid the verbosity and extra template processing of the accepted answer
1) Extend admin/change_form.html in your app's template directory (you are namespacing your app's templates, I hope) and override block submit_row:
{% extends "admin/change_form.html" %}
{% block submit_row %}
<div class="submit-row">
{% if extra_buttons %}
{% for button in extra_buttons %}
{{ button }}
{% endfor %}
{% endif %}
{% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" name="_save" />{% endif %}
{% if show_delete_link %}
{% url opts|admin_urlname:'delete' original.pk|admin_urlquote as delete_url %}
<p class="deletelink-box">{% trans "Delete" %}</p>
{% endif %}
{% if show_save_as_new %}<input type="submit" value="{% trans 'Save as new' %}" name="_saveasnew" />{% endif %}
{% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" />{% endif %}
{% if show_save_and_continue %}<input type="submit" value="{% trans 'Save and continue editing' %}" name="_continue" />{% endif %}
</div>
{% endblock %}
This assumes, of course, that button's string representation is an appropriate browser input or button element, and is marked safe with django.utils.safestring.mark_safe. Alternatively, you could use the safe template filter or access the attributes of button directly to construct the <input>. In my opinion, it's better to isolate such things to the python level.
2) Override MyModelAdmin.change_view and MyModelAdmin.change_form_template:
change_form_template = "my_app/admin/change_form.html"
def change_view(self, request, object_id, form_url='', extra_context=None):
extra_context = extra_context or self.extra_context()
return super(PollAdmin, self).change_view(
request, object_id, form_url, extra_context=extra_context,
)

In my case I only needed for users to do a custom action only after they saved, like "Add another", other buttons such as "export to pdf" I put in the change list as there it's super easy to add buttons.
If you are here for a case like mine the built-in messages framework will do the trick.
adding a response message is simple and you will need to override the
admin class save_model, goes like this:
from django.utils.safestring import mark_safe
def save_model(self, request, obj, form, change):
# if new model
if not change:
message_html = "<a href='http://yourSITE/admin/appname/appmodel/add/'>Add another</a>"
messages.add_message(request, messages.INFO, mark_safe(message_html))
super(YourAdminClass, self).save_model(request, obj, form, change)
there are 4 different types of messages (I changed the css of INFO) and they are displayed in the screenshot

You can add a custom button right next to "SAVE" button on "Add" form and "Change" form for a specifc admin.
First, in the root django project directory, create "templates/admin/custom_change_form.html" and "templates/admin/submit_line.html" as shown below:
Next, copy & paste all the code of "change_form.html" under django library whose path is "django/contrib/admin/templates/admin/change_form.html" to "templates/admin/custom_change_form.html" as shown below:
# "templates/admin/custom_change_form.html"
{% extends "admin/base_site.html" %}
{% load i18n admin_urls static admin_modify %}
{% block extrahead %}{{ block.super }}
<script src="{% url 'admin:jsi18n' %}"></script>
{{ media }}
{% endblock %}
... Much more code below
Next, copy & paste all the code of "submit_line.html" under django library whose path is "django/contrib/admin/templates/admin/submit_line.html" to "templates/admin/submit_line.html" as shown below:
# "templates/admin/submit_line.html"
{% load i18n admin_urls %}
<div class="submit-row">
{% block submit-row %}
{% if show_save %}<input type="submit" value="{% translate 'Save' %}" class="default" name="_save">{% endif %}
... Much more code below
Next, add the code below between "{% block submit-row %}" and "{% if show_save %}" on "templates/admin/submit_line.html". *style="float:right;margin-left:8px;" is important for this code below to put a custom button right next to "SAVE" button:
{% if custom_button %}
<input type="submit" style="float:right;margin-left:8px;" value="{% translate 'Custom button' %}" name="_custom_button">
{% endif %}
So, this is the full code as shown below:
# "templates/admin/submit_line.html"
{% load i18n admin_urls %}
<div class="submit-row">
{% block submit-row %}
{% if custom_button %}
<input type="submit" style="float:right;margin-left:8px;" value="{% translate 'Custom button' %}" name="_custom_button">
{% endif %}
{% if show_save %}<input type="submit" value="{% translate 'Save' %}" class="default" name="_save">{% endif %}
... Much more code below
Next, this is the settings for templates in "settings.py":
# "settings.py"
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
Then, add "os.path.join(BASE_DIR, 'templates')" to "DIRS" as shown below:
# "settings.py"
import os # Here
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')], # Here
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
Now, this is "Person" model as shown below:
# "models.py"
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
Then, this is "Person" admin as shown below:
# "admin.py"
from django.contrib import admin
from .models import Person
#admin.register(Person) # Here
class PersonAdmin(admin.ModelAdmin):
pass
So next, for "Person" admin, set "admin/custom_change_form.html" to "change_form_template", set "True" to "extra_context['custom_button']" in "changeform_view()" and set "response_add()" and "response_change()" to define the action after pressing "Custom button" on "Add" form and "Change" form respectively as shown below. *Whether or not setting "response_add()" and "response_change()", inputted data to fields is saved after pressing "Custom button" on "Add" form and "Change" form respectively:
# "admin.py"
from django.contrib import admin
from .models import Person
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
change_form_template = "admin/custom_change_form.html" # Here
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context['custom_button'] = True # Here
return super().changeform_view(request, object_id, form_url, extra_context)
def response_add(self, request, obj, post_url_continue=None): # Here
if "_custom_button" in request.POST:
# Do something
return super().response_add(request, obj, post_url_continue)
else:
# Do something
return super().response_add(request, obj, post_url_continue)
def response_change(self, request, obj): # Here
if "_custom_button" in request.POST:
# Do something
return super().response_change(request, obj)
else:
# Do something
return super().response_change(request, obj)
Finally, "Custom button" is added at the bottom of "Add" form and "Change" form for "Person" admin as shown below:
In addition, for "Person" admin, you can replace "changeform_view()" with "render_change_form()" set "context.update({"custom_button": True})" as shown below:
# "admin.py"
from django.contrib import admin
from .models import Person
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
change_form_template = "admin/custom_change_form.html"
def render_change_form(self, request, context, add=False, change=False, form_url="", obj=None):
context.update({"custom_button": True}) # Here
return super().render_change_form(request, context, add, change, form_url, obj)
def response_add(self, request, obj, post_url_continue=None):
if "_custom_button" in request.POST:
# Do something
return super().response_add(request, obj, post_url_continue)
else:
# Do something
return super().response_add(request, obj, post_url_continue)
def response_change(self, request, obj):
if "_custom_button" in request.POST:
# Do something
return super().response_change(request, obj)
else:
# Do something
return super().response_change(request, obj)
Then, "Custom button" is added at the bottom of "Add" form and "Change" form for "Person" admin as well as shown below:

Related

Adding item into database without inputting in the text box

I am trying to figure out how to insert a default item which is "Item Code" into my 'List' database instantaneously after clicking the add button. I only managed to do is insert the string/char that I have entered in the text box. Though, when I click the 'add' button without typing in anything inside the text box, I get this error:
ValueError at /
The view home.views.index didn't return an HttpResponse object. It returned None instead.
Here are my codes so far:
models.py
from django.db import models
from django.utils import timezone
class List(models.Model):
item = models.CharField(max_length=200)
date = models.DateTimeField(default = timezone.now)
def __str__(self):
return self.item
views.py
from django.shortcuts import render
from .models import List
from .forms import ListForm
from django.contrib import messages
def index(request):
context = {
'items': List.objects.all()
}
if request.method == 'POST':
form = ListForm(request.POST or None)
if form.is_valid():
form.save()
messages.success(request, ('Item has been added to the list.'))
return render(request, 'home/index.html', context)
else:
return render(request, 'home/index.html', context)
forms.py
from django import forms
from .models import List
class ListForm(forms.ModelForm):
class Meta:
model = List
fields = [
'item',
'date'
]
index.html
<h1>To do list</h1>
{% if messages %}
{% for message in messages %}
<p> {{ message }} </p>
{% endfor %}
{% endif %}
<ul>
{% if items %}
{% for item in items %}
<li>{{ item.item }}</li>
<li>{{ item.date }}</li>
<br>
{% endfor %}
{% endif %}
</ul>
<form method="POST">{% csrf_token %}
<input type="text" name="item">
<input type="submit" value="Add">
</form>
Remove the else statement and unindent the return. You always want to return that rendered template, except when you redirect after a successful post.

How to remove the save and continue editing button in django-admin in django 1.9?

I have attempted all the solutions listed in In Django admin, how can I hide Save and Continue and Save and Add Another buttons on a model admin? and that post was from a few years ago, and I can't find anything about how to disable those buttons. Due to custom saving, the Save And Continue Editing button causes an error. Is there any way to disable it in the current version of django?
Constraints:
- Cannot put app before django.contrib.admin
- I need to only disable it for one form.
- I have a custom form for creation, and it has a custom save method (it's an account creation)
You can just hide the button (the underlying functionality will still be there, but the button will not be visible).
This should work
from django.contrib import admin
class MyModelAdmin(admin.ModelAdmin):
...
class Media:
css = {
'all': ('some/path/to/css/disable_save_and_continue_editing_button.css')
}
disable_save_and_continue_editing_button.css
input[name="_continue"] {
display: none;
}
SO I have figured it out. If you need to play with these buttons then copy the code of submit_line.html and then override it to your templates/admin/submit_line.html and go to your setting.py and then go to
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))),"Project name or app name depends where you put your templates folder","templates")],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
And in yours submit_line.html code
{% load i18n admin_urls %}
<div class="submit-row">
{% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" name="_save">{% endif %}
{% if show_delete_link %}<p class="deletelink-box">{% trans "Delete" %}</p>{% endif %}
{% if show_save_as_new %}<input type="submit" value="{% trans 'Save as new' %}" name="_saveasnew" {{ onclick_attrib }}/>{%endif%}
{% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" {{ onclick_attrib }}/>{% endif %}
{% if show_save_and_continue %}<input type="submit" value="{% trans 'Save and continue editing' %}" name="_continue" {{ onclick_attrib }}/>{% endif %}
</div>
Just remove that save and continue buttons or you can simply comment it.
Hope this will help.
Mark it correct if it helps.
To remove "Save and continue editing" button, set "False" to "extra_context['show_save_and_continue']" in "changeform_view()" as shown below:
# "admin.py"
from django.contrib import admin
from .models import MyModel
#admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context['show_save_and_continue'] = False # Here
# extra_context['show_save'] = False
# extra_context['show_delete'] = False
return super().changeform_view(request, object_id, form_url, extra_context)
You can also remove "Save and continue editing" button by setting "False" to "show_save_and_continue" in "context.update()" in "render_change_form()" as shown below:
# "admin.py"
from django.contrib import admin
from .models import MyModel
#admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
context.update({
'show_save_and_continue': False, # Here
# 'show_save': False,
# 'show_delete': False,
})
return super().render_change_form(request, context, add, change, form_url, obj)

How do I add custom actions to a change model form in Django Admin?

I'm working with Django admin, and I'd like to be able to use an existing instance of a model as a template for making a new object, so people using django admin don't need to re-key all the same properties on a object, when making new objects.
I'm picturing it a bit like this at the bottom of the Django admin form for updating a single object:
The django docs explain how to add bulk actions, by adding to the actions on a model, like so:
class ArticleAdmin(admin.ModelAdmin):
actions = ['make_published']
def make_published(self, request, queryset):
queryset.update(status='p')
make_published.short_description = "Mark selected stories as published"
However, it wasn't so clear to me how to do this for a single change model form on an object, for actions I only want to apply to model at a time.
How would I do this?
I'm guessing I probably need to hack around with the change_model form, but beyond that, I'm not so sure.
Is there a fast way to do this without overriding loads of templates ?
Django Admin does not provide a way to add custom actions for change forms.
However, you can get what you want with a few hacking.
First you will have to override the submit row.
your_app/templates/admin/submit_line.html
{% load i18n admin_urls %}
<div class="submit-row">
{% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" name="_save" />{% endif %}
{% if show_delete_link %}
{% url opts|admin_urlname:'delete' original.pk|admin_urlquote as delete_url %}
<p class="deletelink-box">{% trans "Delete" %}</p>
{% endif %}
{% if show_save_and_copy %}<input type="submit" value="{% trans 'Create a new item based on this one' %}" name="_save_and_copy" />{% endif %}
{% if show_save_as_new %}<input type="submit" value="{% trans 'Save as new' %}" name="_saveasnew" />{% endif %}
{% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" />{% endif %}
{% if show_save_and_continue %}<input type="submit" value="{% trans 'Save and continue editing' %}" name="_continue" />{% endif %}
</div>
In the above template, I just added the line {% if show_save_and_copy %}<input type="submit" value="{% trans 'Create a new item based on this one' %}" name="_save_and_copy" />{% endif %}. All other line are from default django implementation.
Then you will have to handle your button '_save_and_copy'
your_app/admin.py
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
class ArticleAdmin(admin.ModelAdmin):
def render_change_form(self, request, context, *args, **kwargs):
"""We need to update the context to show the button."""
context.update({'show_save_and_copy': True})
return super().render_change_form(request, context, *args, **kwargs)
def response_post_save_change(self, request, obj):
"""This method is called by `self.changeform_view()` when the form
was submitted successfully and should return an HttpResponse.
"""
# Check that you clicked the button `_save_and_copy`
if '_save_and_copy' in request.POST:
# Create a copy of your object
# Assuming you have a method `create_from_existing()` in your manager
new_obj = self.model.objects.create_from_existing(obj)
# Get its admin url
opts = self.model._meta
info = self.admin_site, opts.app_label, opts.model_name
route = '{}:{}_{}_change'.format(*info)
post_url = reverse(route, args=(new_obj.pk,))
# And redirect
return HttpResponseRedirect(post_url)
else:
# Otherwise, use default behavior
return super().response_post_save_change(request, obj)
This example is for your specific case, it's up to you to make it more generic if you need to.
That being said, for your specific case you can also just click "Save and continue" to save your work, and then click "Save as new" to make a copy of it. Don't you ?
As pointed out, there is not a way and needs to be hacked. Here's I think an elegant hack for adding custom actions to both the list and change form views. It doesn't actually save the form just execute whatever custom action you want against the current object and return you back to the same change form page.
from django.db.models import Model
from django.contrib import admin, messages
from django.contrib.admin.options import (
unquote,
csrf_protect_m,
HttpResponseRedirect,
)
class ArticleAdmin(admin.ModelAdmin):
change_form_template = 'book/admin_change_form_book.html'
actions = ['make_published']
def make_published(self, request, queryset):
if isinstance(queryset, Model):
obj = queryset
obj.status = 'p'
obj.save()
updated_count = 1
else:
updated_count = queryset.update(status='p')
msg = "Marked {} new objects from existing".format(updated_count)
self.message_user(request, msg, messages.SUCCESS)
make_published.short_description = "Mark selected stories as published"
#csrf_protect_m
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
if request.method == 'POST' and '_make_published' in request.POST:
obj = self.get_object(request, unquote(object_id))
self.make_published(request, obj)
return HttpResponseRedirect(request.get_full_path())
return admin.ModelAdmin.changeform_view(
self, request,
object_id=object_id,
form_url=form_url,
extra_context=extra_context,
)
Now you can add an <input> for the action to the custom template view (this example uses book/admin_change_form_book.html in change_form_template)
{% extends 'admin/change_form.html' %}
{% block form_top %}
<input
type="submit"
name="_make_published"
value="Mark Published"
class="grp-button grp-default"
>
{% endblock %}
If you look at the admin/change_form.html (i.e. "django/contrib/admin/templates/admin/change_form.html") you can insert this custom <input> action anywhere between the <form...> </form> tags on the page. Including these blocks:
{% block form_top %}
{% block after_related_objects %}
{% block submit_buttons_bottom %}

django - What does this line achieve here?

I'm following a tutorial on effectivedjango.com, and this is the code they have:
views.py:
class CreateContactView(CreateView):
model = Contact
template_name = 'edit_contact.html'
fields = '__all__' #this is needed for error msg Using ModelFormMixin (base class of CreateContactView) without the 'fields' attribute is prohibited.
def get_success_url(self):
return reverse('contacts-list')
def get_context_data(self, **kwargs):
context = super(CreateContactView, self).get_context_data(**kwargs)
context['action'] = reverse('contacts-new')
return context
class UpdateContactView(UpdateView):
model = Contact
template_name = 'edit_contact.html'
fields = '__all__'
def get_success_url(self):
return reverse('contacts-list')
def get_context_data(self, **kwargs):
context = super(UpdateContactView, self).get_context_data(**kwargs)
context['action'] = reverse('contacts-edit', kwargs={'pk' : self.get_object().id})
return context
urls.py:
url(r'^$', contacts.views.ListContactView.as_view(),
name='contacts-list',),
url(r'^new$', contacts.views.CreateContactView.as_view(),
name='contacts-new',),
url(r'^edit/(?P<pk>\d+)/$', contacts.views.UpdateContactView.as_view(),
name='contacts-edit',),
contact_list.html:
{% block content %}
<h1>Contacts</h1>
<ul>
{% for contact in object_list %}
<li class="contact">
{{ contact }}
(edit)
</li>
{% endfor %}
</ul>
Add contact
{% endblock %}
edit_contact.html:
{% block content %}
{% if contact.id %}
<h1>Edit Contact</h1>
{% else %}
<h1>Add Contact</h1>
{% endif %}
<form action="{{ action }}" method="POST">
{% csrf_token %}
<ul>
{{ form.as_ul }}
</ul>
<input id="save_contact" type="submit" value="Save" />
</form>
Back to list
{% if contact.id %}
Delete
{% endif %}
{% endblock %}
Why does the line context['action'] = reverse('contacts-edit', kwargs={'pk' : self.get_object().id}) in views.py look like its calling itself?
What I mean is, this action is called when the submit button is pressed in the contact-edit template, correct? So it starts there, and it is reverse-calling contact-edit which is itself, right?
What am I not seeing here?
Thank you for all your help.
Yes, the line context['action'] = reverse('contacts-edit', kwargs={'pk' : self.get_object().id}) in views.py is calling itself. This line generates the proper url for contacts-edit view.
This is done so that POST requests come to the same view i.e. UpdateContactView which is an UpdateView. There, proper handling will be done i.e. form validation will occur with the sent data. If the form is valid, object will be updated. Otherwise, the form will be displayed again with errors.
Django docs on UpdateView:
A view that displays a form for editing an existing object,
redisplaying the form with validation errors (if there are any) and
saving changes to the object.

Create custom buttons in admin change_form in Django

I want to add custom buttons to the add/change form at the administration interface. By default, there are only three:
Save and add another
Save and continue editing
Save
I have created some custom methods in my forms.py file, and I want to create buttons to call these methods. I have used the snippet http://djangosnippets.org/snippets/1842/, but it's not exactly what I want. This one allows to create buttons and call methods from the admin.py file and not forms.py.
Is there a way to do that?
This is my admin.py code:
class CategoryAdmin(admin.ModelAdmin):
prepopulated_fields = { "alias": ("title",) }
form = CategoryForm
admin.site.register(Category, CategoryAdmin)
And my forms.py code:
class CategoryForm(forms.ModelForm):
"""
My attributes
"""
def custom_method(self):
print("Hello, World!")
How do I create a button that calls "custom_method()"?
One simple way I found to add buttons is to add another row for the custom buttons. Create an admin directory in your template dir based on your needs. For example I usually add buttons for specific models in a custom template. Make a "templates/admin/app/model/" directory.
Then add a file change_form.html.
{% extends "admin/change_form.html" %}
{% load i18n %}
{% block submit_buttons_bottom %}
<div class="submit-row">
<input type="button" value="{% trans 'Another Button' %}" name="_anotherbutton" />
</div>
{{ block.super }}
{% endblock %}
The code before the {{ block.super }} is inspired by the submit_line.html template used by the template tag {% submit_row %}. I prefer this method because is straightforward but you must live with another row of buttons.
You can override admin/change_form.html. Copy the version in contrib.admin.templates into your project. Mine is myproject/templates/admin/change_form.html, but you could use /myproject/myapp/templates/admin/change_form.html.
Next, edit the copy and change the two references to the existing template tag, {% submit_row %}, to point to your own template tag, {% my_template_tag %}.
Base your template tag on the contrib.admin's {% submit_row %}, but edit the HTML template to contain any extra buttons you want to display.
The submit buttons in a change form are rendered by the submit_row template tag. This tag renders the template admin/submit_line.html. Since you want to add to the existing buttons, your best (and DRYest) approach is to override admin/submit_line.html.
For example, create a file my_project/templates/admin/submit_line.html with the following content:
{% load i18n admin_urls %}
<div class="submit-row">
{% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" name="_save" {{ onclick_attrib }}/>{% endif %}
{% if show_delete_link %}<p class="deletelink-box">{% trans "Delete" %}</p>{% endif %}
{% if show_save_as_new %}<input type="submit" value="{% trans 'Save as new' %}" name="_saveasnew" {{ onclick_attrib }}/>{%endif%}
{% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" {{ onclick_attrib }}/>{% endif %}
{% if show_save_and_continue %}<input type="submit" value="{% trans 'Save and continue editing' %}" name="_continue" {{ onclick_attrib }}/>{% endif %}
<input type="submit" value="{% trans 'New button 1' %}" name="_button1" {{ onclick_attrib }}/>
<input type="submit" value="{% trans 'New button 2' %}" name="_button2" {{ onclick_attrib }}/>
</div>
Most of what's above was copied from django/contrib/admin/templates/submit_line.html. You can also add additional if statements in the template if you only want to show those additional buttons in certain cases.
You can add a custom button at the bottom of "Add" form and "Change" form for a specifc admin.
First, in the root django project directory, create "templates/admin/custom_change_form.html" as shown below:
Next, under "django" library, there is "change_form.html" which is "django/contrib/admin/templates/admin/change_form.html" so copy & paste all the code of "change_form.html" to "custom_change_form.html" as shown below:
# "templates/admin/custom_change_form.html"
{% extends "admin/base_site.html" %}
{% load i18n admin_urls static admin_modify %}
{% block extrahead %}{{ block.super }}
<script src="{% url 'admin:jsi18n' %}"></script>
{{ media }}
{% endblock %}
... Much more code below
Next, there is the code in line 64 on "custom_change_form" as shown below:
# "templates/admin/custom_change_form.html"
{% block submit_buttons_bottom %}{% submit_row %}{% endblock %} # Line 64
Then, add the code below between "{% submit_row %}" and "{% endblock %}":
{% if custom_button %}
<div class="submit-row">
<input type="submit" value="{% translate 'Custom button' %}" name="_custom_button">
</div>
{% endif %}
So, this is the full code as shown below:
# "templates/admin/custom_change_form.html"
{% block submit_buttons_bottom %} # Line 64
{% submit_row %}
{% if custom_button %}
<div class="submit-row">
<input type="submit" value="{% translate 'Custom button' %}" name="_custom_button">
</div>
{% endif %}
{% endblock %}
Next, this is the settings for templates in "settings.py":
# "settings.py"
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
Then, add "os.path.join(BASE_DIR, 'templates')" to "DIRS" as shown below:
# "settings.py"
import os # Here
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')], # Here
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
Now, this is "Person" model as shown below:
# "models.py"
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
Then, this is "Person" admin as shown below:
# "admin.py"
from django.contrib import admin
from .models import Person
#admin.register(Person) # Here
class PersonAdmin(admin.ModelAdmin):
pass
So next, for "Person" admin, set "admin/custom_change_form.html" to "change_form_template", set "True" to "extra_context['custom_button']" in "changeform_view()" and set "response_add()" and "response_change()" to define the action after pressing "Custom button" on "Add" form and "Change" form respectively as shown below. *Whether or not setting "response_add()" and "response_change()", inputted data to fields is saved after pressing "Custom button" on "Add" form and "Change" form respectively:
# "admin.py"
from django.contrib import admin
from .models import Person
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
change_form_template = "admin/custom_change_form.html" # Here
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context['custom_button'] = True # Here
return super().changeform_view(request, object_id, form_url, extra_context)
def response_add(self, request, obj, post_url_continue=None): # Here
if "_custom_button" in request.POST:
# Do something
return super().response_add(request, obj, post_url_continue)
else:
# Do something
return super().response_add(request, obj, post_url_continue)
def response_change(self, request, obj): # Here
if "_custom_button" in request.POST:
# Do something
return super().response_change(request, obj)
else:
# Do something
return super().response_change(request, obj)
Finally, "Custom button" is added at the bottom of "Add" form and "Change" form for "Person" admin as shown below:
In addition, for "Person" admin, you can replace "changeform_view()" with "render_change_form()" set "context.update({"custom_button": True})" as shown below:
# "admin.py"
from django.contrib import admin
from .models import Person
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
change_form_template = "admin/custom_change_form.html"
def render_change_form(self, request, context, add=False, change=False, form_url="", obj=None):
context.update({"custom_button": True}) # Here
return super().render_change_form(request, context, add, change, form_url, obj)
def response_add(self, request, obj, post_url_continue=None):
if "_custom_button" in request.POST:
# Do something
return super().response_add(request, obj, post_url_continue)
else:
# Do something
return super().response_add(request, obj, post_url_continue)
def response_change(self, request, obj):
if "_custom_button" in request.POST:
# Do something
return super().response_change(request, obj)
else:
# Do something
return super().response_change(request, obj)
Then, "Custom button" is added at the bottom of "Add" form and "Change" form for "Person" admin as well as shown below:

Categories