43👍
- Create a template in you template folder: admin/YOUR_APP/YOUR_MODEL/change_list.html
-
Put this into that template
{% extends "admin/change_list.html" %} {% block object-tools-items %} {{ block.super }} <li> <a href="export/" class="grp-state-focus addlink">Export</a> </li> {% endblock %}
-
Create a view function in
YOUR_APP/admin.py
and secure it with annotationfrom django.contrib.admin.views.decorators import staff_member_required @staff_member_required def export(self, request): ... do your stuff ... return HttpResponseRedirect(request.META["HTTP_REFERER"])
-
Add new url into
YOUR_APP/admin.py
to url config for admin modelfrom django.conf.urls import patterns, include, url class YOUR_MODELAdmin(admin.ModelAdmin): ... list def stuff ... def get_urls(self): urls = super(MenuOrderAdmin, self).get_urls() my_urls = patterns("", url(r"^export/$", export) ) return my_urls + urls
Enjoy 😉
0👍
The easy and accepted way is to override the template.
If you don’t want to mess with the Django templates, you could add a Media
class to your admin and add some javascript to create the button although I think creating elements with javascript is a bit nasty and should be avoided.
- [Django]-Initial populating on Django Forms
- [Django]-Per Field Permission in Django REST Framework
- [Django]-Django: How to pre-populate FormView with dynamic (non-model) data?
-1👍
Though other answers are entirely valid, I think it is important to note that it is absolutely not necessary to add a button to get such behavior. You can use admin actions, as you did for the make_published
action.
This as the advantage of not requiring to override any template, and thus prevent from potential troubles when upgrading django version (as admin templates may change, and changes might not be “compatible” with the way you overrode it).
import csv
from django.http import HttpResponse
from django.utils import timezone
def export_as_csv(modeladmin, request, queryset):
opts = modeladmin.model._meta
filename = format(timezone.now(), "{app}_{model}-%Y%m%d_%H%M.csv").format(
app=opts.app_label, model=opts.model_name)
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
writer = csv.writer(response)
field_names = [f.get_attname() for f in opts.concrete_fields]
writer.writerow(field_names)
for obj in queryset.only(*field_names):
writer.writerow([str(getattr(obj, f)) for f in field_names])
return response
Admin actions are made for this, adding a custom button is one step closer to “over-customization”, which means it’s probably time to write your own views.
The admin has many hooks for customization, but beware of trying to use those hooks exclusively. If you need to provide a more process-centric interface that abstracts away the implementation details of database tables and fields, then it’s probably time to write your own views.
Quote from the introduction paragraph of Django Admin’s documentation
- [Django]-How to pass url parameter to reverse_lazy in Django urls.py
- [Django]-Max image size on file upload
- [Django]-How can my django model DateField add 30 days to the provided value?