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:
Related
I don't understand how widgets work.
I tried this minimum example :
in my forms.py
class PartialResetForm(forms.Form):
date = forms.DateField(
label="Starting date",
widget=AdminDateWidget()
)
in my admin/intermediary_reset_page.html
{% extends "admin/base_site.html" %}
<!--Loading necessary css and js -->
{{ form.media }}
{% block content %}
<form action="" method="post">{% csrf_token %}
<!-- The code of the form with all input fields will be automatically generated by Django -->
{{ form }}
<!-- Link the action name in hidden params -->
<input type="hidden" name="action" value="custom_action" />
<!-- Submit! Apply! -->
<input type="submit" name="apply" value="Submit" />
</form>
{% endblock %}
in my admin.py as the definition of an action
def custom_action(self, request, queryset):
form = PartialResetForm()
return render(request, "admin/intermediary_reset_page.html", {
"items": queryset, "form": form
})
For now I don't care about the queryset, it will be my next topic. With this simple example, I wanted to have a calendar in order to help pick a date, but only a TextInput appeared. I believe it is due to the fact that AdminDateWidget inheritates from TextInput.
My question is why isn't it appearing as a calendar ? I imported the media and declared my widget, I don't understand what else I'm supposed to do.
you should declare type
AdminDateWidget(attrs={'type': 'date'})
And it should be enough ;)
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)
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 %}
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:
I want to add some html element (button, "a" tag, etc ) to a django admin page. How can i do it? Please help.
Not sure where you want to add your stuff but this is a solution I found somewhere else on SO to change the HTML of a FileField (in my case i wanted to display the current image in an ImageField).
In other words you can make a widget that modifies the html of the field you want to customize:
# Widget that modifies the output of a FileField
class OutputWidget(AdminFileWidget):
# Overloaded django magic
def render(self, name, value, attrs=None):
output = []
# This is the place where we edit the output
if value and getattr(value, "url", None):
image_url = value.url
output.append(u' <img src="%s" alt="%s" />' % (image_url, image_url, image_url))
output.append(super(AdminFileWidget, self).render(name, value, attrs))
return mark_safe(u''.join(output))
# ModelAdmin class that is applied to the model
class MyModelSettings(admin.ModelAdmin):
# Overloaded django magic
def formfield_for_dbfield(self, db_field, **kwargs):
# Look for the field we want to edit and register the widget with it
if db_field.name == 'nameOfFieldIWantToEdit':
request = kwargs.pop("request", None)
kwargs['widget'] = OutputWidget
return db_field.formfield(**kwargs)
return super(MyModelSettings,self).formfield_for_dbfield(db_field, **kwargs)
# Register my overloaded settings with the model
admin.site.register(MyModel, MyModelSettings)
The code goes into admin.py where you register your models.
From the docs:
The Django admin site
Customizing the Django admin interface
I used Omokoli's solution from above but to make the field use my custom widget I did:
class MyModelAdminForm(forms.ModelForm):
class Meta:
model = get_model('myapp', 'mymodel')
widgets = {
'original_link': OutputWidget,
}
You can create a file under templates/admin called base_site.html
(create the "admin" folder in your app).
add this code:
{% extends "admin/base_site.html" %}
{% load i18n %}
{% block userlinks %}
{% if site_url %}
{% trans 'View site' %} /
{% endif %}
{% if user.is_active and user.is_staff %}
{% url 'django-admindocs-docroot' as docsroot %}
{% if docsroot %}
{% trans 'Documentation' %} /
{% endif %}
{% endif %}
{% if user.has_usable_password %}
{% trans 'Change password' %} /
{% endif %}
{% trans 'Log out' %}
<!-- YOUR CUSTOM CODE HERE -->
<div class="your_custom_class">
{% trans 'your link 1' %} /
{% trans 'your link 2' %}
</div>
{% endblock %}
You can overwrite a lot of the admin html files. Make sure you extends the html you are overwriting, not to loose basic functionality.
See customize-django-admin-python for full admin templates tree you can overwrite.
You can also look at the base_site.html in Django project in Github. You'll find all of the "base_site" blocks you can plant your custom code in.