Optional, replaceable extras¶
In the installation and basic usage we covered the bare minimum required to display plain text links to the admin. We can, of course, do better than that, and django-adminlinks comes with a few bits and pieces to do so. None of them are required, or even enabled by default, and all of them are replaceable.
Styling the links¶
If you’re lucky, they’ll already look as nice as your regular links, because that’s all they are. Don’t believe me? Here’s the default template for the edit link:
{% if link %}
{% load i18n %}
<a href="{{ link }}" class="django-adminlinks--btn django-adminlinks--edit" data-adminlinks="autoclose" data-no-turbolink>
{% blocktrans with verbose_name as name %}Edit this <span class="django-adminlinks--btn--important">{{ name }}</span>{% endblocktrans %}
</a>
{% endif %}
As you can see, the links can be styled in a composite way because they have multiple classes.
Using the provided styles¶
There’s a template tag we’ve not mentioned until now, because it’s probably not suitable for use with things like django-sekizai or django-compressor.
Behold render_adminlinks_css:
{% load adminlinks_assets %}
<!doctype html>
<html>
<head>
{% render_adminlinks_css %}
</head>
<body>
[...]
</body>
</html>
That’s all there is to it. Infact, it’s not even a complex tag, it just handles rendering this on your behalf and does so if the request.user can potentially use the AdminSite:
{% if should_load_assets %}
{% load static %}
<link rel="stylesheet" media="screen" href="{% static 'adminlinks/css/widgets.css' %}{% if debug %}?cachebusting={% now "u" %}{% endif %}" type="text/css">
<link rel="stylesheet" media="screen" href="{% static 'adminlinks/css/fancyiframe-custom.css' %}{% if debug %}?cachebusting={% now "u" %}{% endif %}" type="text/css">
{% endif %}
The styles in widgets.css are designed to emulate the visuals of the default Django admin, without using any images. You can override them by providing your own widgets.css, or override the adminlinks/templates/adminlinks/css.html file in your own templates, wherever you’ve specified them under TEMPLATE_DIRS.
As you can see, the {% render_adminlinks_css %} also renders another stylesheet, fancyiframe-custom.css, which is used in conjunction with the provided JavaScript.
Including the CSS should get you links like this GIF, which is just the output of:
{% load adminlinks_buttons %}
{% render_admin_buttons blogpost %}
Note
If you’re using either django-sekizai or django-compressor, you’ll need to handle whether or not the stylesheets should be displayed yourself. The simplest test would be something like {% if request.user.is_authenticated and request.user.is_staff %}. Or you can just always include them, I suppose.
Making things modal¶
Though it is inevitably not perfect, we can make the UX a little better by allowing users to edit things in-place, via the included modal iframe.
Using the provided JavaScript¶
Exactly like the bundled Stylesheets template tag, there’s a template tag for rendering the bundled JavaScript. The same caveats about django-sekizai and django-compressor apply:
{% load adminlinks_assets %}
<!doctype html>
<html>
<head>
[...]
</head>
<body>
[...]
{% render_adminlinks_js %}
</body>
</html>
Which will output:
{% if should_load_assets %}
{% load static %}
<script type="text/javascript" src="{% static 'admin/js/jquery.min.js' %}{% if debug %}?cachebusting={% now "u" %}{% endif %}"></script>
<script type="text/javascript" src="{% static 'adminlinks/js/jquery.fancyiframe.js' %}{% if debug %}?cachebusting={% now "u" %}{% endif %}"></script>
<script type="text/javascript" src="{% static 'adminlinks/js/adminlinks.js' %}{% if debug %}?cachebusting={% now "u" %}{% endif %}"></script>
{% endif %}
As you can see, the JavaScript is a little bit more involved. It uses the jQuery which comes with Django, and a script of my own wrangling, to display an <iframe> in a modal box. It hooks up all classes of django-adminlinks--btn to this modal box – this CSS class is applied as a namespace to all the links Here’s an example of it being used with the default CSS to do per-field editing of the title.
Note
The modal window has been sped up here to keep the animated GIF small, and the admin is in popup mode thanks to fix_admin_popups().
Patching the standard ModelAdmin¶
If you’re making use of the bundled JavaScript, through the template tag or otherwise, you’ll probably want to alter the behaviour of the Django ModelAdmin instances in an effort to better support the modal-editing. We can extend the behaviour like so:
from django.contrib import admin
from adminlinks.admin import AdminlinksMixin
from myapp.models import MyModel
# At it's most simple, mixing in with the default modeladmin.
class MyModelAdmin(AdminlinksMixin, admin.ModelAdmin):
list_display = ['my_field', 'my_other_field']
admin.site.register(MyModel, MyModelAdmin)
Or, for the slightly more complex usage of replacing a third-party admin:
from django.contrib import admin
from adminlinks.admin import AdminlinksMixin
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
# Replacing an existing Modeladmin
try:
admin.site.unregister(User)
except admin.NotRegistered:
pass
class MyUserAdmin(AdminlinksMixin, UserAdmin):
pass
admin.site.register(User, MyUserAdmin)
For more complex admins, or different ways of handling displaying things (such as using jQuery ajaxForm, or one of the many other modal boxes) you’ll have to go off the beaten track and drop some/most of the provided stuff. Any suggestions for how to make it more flexible, do get in contact and explain.
Editing field subsets¶
If your intention is to use the edit field template tag:
{% render_edit_field_button object 'title' %}
You’ll need to amend your ModelAdmin to support the dynamic generation of that form, using the AdminlinksMixin, which updates the standard get_urls() to expose the change_field_view()
Success responses¶
To allow our bundled JavaScript’s modal box to automatically close after a non-idempotent action (eg: add/change/delete), we need to override the existing modeladmin methods response_add(), response_change() and delete_view() to handle sending a message to the modal window. That too is covered by including AdminlinksMixin.
Simplifying the AdminSite visual clutter¶
If you’re aiming for doing everything via the front-end, using the template tags to their fullest potential, you may want to get rid of some of the visual noise the admin provides (header, breadcrumbs, etc). Add the following to your TEMPLATE_CONTEXT_PROCESSORS to make it behave as if it were in a popup, reducing the visual context appropriately:
TEMPLATE_CONTEXT_PROCESSORS = (
# These are other context processors we probably already have ...
"django.contrib.auth.context_processors.auth",
"django.core.context_processors.media",
"django.core.context_processors.static",
"django.core.context_processors.request",
# This is our new context processor!
"adminlinks.context_processors.fix_admin_popups",
)
See fix_admin_popups() for more.