Django Tips Avilpage Com en Latest
Django Tips Avilpage Com en Latest
ChillarAnand
1 Preface 1
1.1 Why this book? . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Pre requisites . . . . . . . . . . . . . . . . . . . . . . . 1
1.3 Who should read this book? . . . . . . . . . . . . . . . 2
1.4 Acknowledgements . . . . . . . . . . . . . . . . . . . 2
3 Better Defaults 5
3.1 Use ModelAdmin . . . . . . . . . . . . . . . . . . . . 5
3.2 Use Better Widgets . . . . . . . . . . . . . . . . . . . . 7
3.3 Better Defaults For Models . . . . . . . . . . . . . . . 9
3.4 Navigation Menu Bar . . . . . . . . . . . . . . . . . . 10
i
6 Filtering In Admin 25
6.1 Search Fields . . . . . . . . . . . . . . . . . . . . . . . 25
6.2 List Filters . . . . . . . . . . . . . . . . . . . . . . . . 26
6.3 Custom List Filters . . . . . . . . . . . . . . . . . . . . 27
6.4 Custom Text Filter . . . . . . . . . . . . . . . . . . . . 28
6.5 Advanced Filters . . . . . . . . . . . . . . . . . . . . . 30
9 Final Words 45
ii
CHAPTER
ONE
PREFACE
In this data driven world, internal tools are often overlooked parts in
companies. Without efficient tools for analytics and dashboards, cross
department communications & customer communications become a bot-
tleneck as people spend more time everyday to get some data.
There are several tools built specifically for analytics and dashboards.
But if we are already using Django framework, we can just use Django
Admin for most of these tasks.
In this book, we will learn how to customize Django admin for these
tasks.
1
Mastering Django Admin
Anyone who wants to learn how to customize and improve the perfor-
mance of django admin.
1.4 Acknowledgements
2 Chapter 1. Preface
CHAPTER
TWO
Django admin was first released in 2005 and it has gone through a lot of
changes since then. Still the admin interface looks clunky compared to
most modern web interfaces.
Jacob Kaplan-Moss, one of the core-developers of Django estimated that
it will cost 1 million dollars1 to hire a team to rebuild admin interface
from scratch. Until we get 1 million dollars to revamp the admin inter-
face, let’s look into alternate solutions.
1. Use django admin with modern themes/skins. Packages like
django-grappelli2 , django-suit3 extend the existing admin interface
and provide new skin,options to customize the UI etc.
2. Use drop-in replacements for django admin. Packages like xad-
min4 , django-admin25 are a complete rewrite of django admin.
Even though these packages come with lot of goodies and better
defaults, they are no longer actively maintained.
3. Use seperate django packages per task.
4. Write our own custom admin interface.
1
https://jacobian.org/2016/may/26/so-you-want-a-new-admin/
2
https://pypi.org/project/django-grappelli/
3
https://pypi.org/project/django-suit/
4
https://pypi.org/project/xadmin/
5
https://pypi.org/project/django-admin2/
3
Mastering Django Admin
We can start default admin interface or use any drop-in replacements for
the admin. Even with this admin interface, we need to write custom
views/reports based on business requirements.
In the next chapter, lets start with customizing admin interface.
THREE
BETTER DEFAULTS
When a model is registered with admin, it just shows the string represen-
tation of the model object in changelist page.
admin.site.register(Book)
5
Mastering Django Admin
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'author',
˓→'published_date', 'cover', 'is_available')
list_filter = ('is_available',)
list_per_page = 10
search_fields = ('name',)
date_hierarchy = 'published_date'
readonly_fields = ('created_at', 'updated_at')
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
list_display = ('id', 'name_colored', 'author',
˓→'published_date', 'cover', 'is_available')
return format_html(html)
name_colored.admin_order_field = 'name'
name_colored.short_description = 'name'
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
(continues on next page)
return format_html(
html.format(url=obj.cover.url, width=width,
˓→height=height)
Viewing and editing JSON field in admin interface will be very difficult
in the textbox. Instead, we can use JSON Editor widget provided any
third-party packages like django-json-widget, with which viewing and
editing JSON data becomes much intuitive.
CSV and Excel imports and exports
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
formfield_overrides = {
fields.JSONField: {
'widget': JSONEditorWidget
},
}
We can set user friendly names instead of default names for django mod-
els in admin. We can override this in model meta options.
class Category(models.Model):
class Meta:
(continues on next page)
class Book(TimeAuditModel):
is_available = models.BooleanField(
help_text='Is the book available to buy?'
)
published_date = models.DateField(
help_text='help_text="Please enter the date
˓→in <em>YYYY-MM-DD</em> format.'
When user visits a specific model from the admin page, to switch to a
different model user has to go back to home page and then move to the
required model. This is inconvinient if user has to switch between models
frequently.
To avoid this, a navigation menu bar can be added at the top as shown
below, so that users can switch between models with just 1 click.
<ul>
{% for app in app_list %}
<li><a href="{{ app.app_url }}">{{ app.name }}</
˓→ a>
<ul>
{% for model in app.models %}
<li><a href="{{ model.admin_url }}">
˓→{{ model.name }}</a></li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
FOUR
class BookAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'author')
admin.site.register(Book, BookAdmin)
By default, this will show a select box with entire authors list. Navigating
this select list and finding the required author is difficult.
To make this easier, we can provide autocomplete option for author field
13
Mastering Django Admin
class AuthorAdmin(admin.ModelAdmin):
search_fields = ('name',)
class BookAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'author')
autocomplete_fields = ('author',)
admin.site.register(Book, BookAdmin)
admin.site.register(Book, BookAdmin)
Here, book name field is liked to book change view. But author field
is shown as plain text. If we notice some typo or if we have to modify
author details, we have to go back to authors admin page, search for
relevant author and then change name.
This becomes tedious if users spend lot of time in admin for tasks like
this. Instead, if author field is hyperlinked to author change view, we can
directly go to that page and change the name.
Django provides an option to access admin views by its URL reversing
system. For example, we can get change view of author model in book
app using reverse(“admin:book_author_change”, args=id). Now we can
use this url to hyperlink author field in book admin.
class BookAdmin(admin.ModelAdmin):
list_display = ('name', 'author_link', )
return mark_safe(link)
author_link.short_description = 'Author'
Now in the book admin view, author field will be hyperlinked to its
change view and we can visit just by clicking it.
Depending on requirements, we can link any field in django to other
class BestSellerAdmin(RelatedFieldAdmin):
search_fields = ('book__name', )
list_display = ('id', 'year', 'rank', 'book')
admin.site.register(Bestseller, BestsellerAdmin)
class BestSellerAdmin(RelatedFieldAdmin):
list_display = ('id', 'rank', 'year', 'book',
˓→'author')
search_fields = ('book__name', )
admin.site.register(Bestseller, BestsellerAdmin)
related_names = name.split('__')
def dynamic_attribute(obj):
for related_name in related_names:
obj = getattr(obj, related_name)
return obj
dynamic_attribute.admin_order_field = admin_
˓→order_field or name
dynamic_attribute.short_description = short_
˓→description or related_names[-1].title().replace(
return dynamic_attribute
class RelatedFieldAdmin(admin.ModelAdmin):
def __getattr__(self, attr):
if '__' in attr:
return get_related_field(attr)
class BestSellerAdmin(RelatedFieldAdmin):
list_display = ('id', 'rank', 'year', 'book',
˓→'book__author')
FIVE
Inbuilt admin interface is one the most powerful & popular feature of
Django. Once we create the models, we need to register them with ad-
min, so that it can read schema and populate interface for it.
Let us register Book model in the admin interface.
# file: library/book/admin.py
class BookAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'author')
admin.site.register(Book, BookAdmin)
19
Mastering Django Admin
models = apps.get_models()
This code snippet should run after all admin.py files are loaded so that
auto registration happends after all manually added models are regis-
tered. Django provides AppConfig.ready() to perform any initialization
tasks which can be used to hook this code.
# file: library/book/apps.py
class BookAppConfig(AppConfig):
def ready(self):
models = apps.get_models()
for model in models:
try:
admin.site.register(model)
except admin.sites.AlreadyRegistered:
pass
This view is not at all useful for the users who want to see the data. It
will be more informative if we can show all the fields of the model in
admin.
class ListModelAdmin(admin.ModelAdmin):
def __init__(self, model, admin_site):
self.list_display = [field.name for field
˓→in model._meta.fields]
super().__init__(model, admin_site)
class BookAppConfig(AppConfig):
def ready(self):
models = apps.get_models()
for model in models:
try:
admin.site.register(model,
˓→ListModelAdmin)
except admin.sites.AlreadyRegistered:
pass
Now, if we look at Author admin page, it will be shown with all relevant
fields.
SIX
FILTERING IN ADMIN
class BookAdmin(admin.ModelAdmin):
search_fields = ('name', 'author__name')
terms against all the search_fields. For example a search for python for
data analysis translates to this SQL caluse.
WHERE
(name ILIKE '%python%' OR author.name ILIKE '%python
˓→%')
class BookAdminFilter(admin.ModelAdmin):
list_display = ('id', 'author', 'published_date
˓→', 'is_available', 'cover')
list_filter = ('is_available',)
We can also write custom filters so that we can set calculated fields and
add filters on top of them.
class CenturyFilter(admin.SimpleListFilter):
title = 'century'
parameter_name = 'published_date'
Here the number of choices are limited. But in some cases where the
choices are hundred or more, it is better to display a text input instead of
choices.
Let’s write a custom filter to filter books by published year. Let’s write
an input filter
class PublishedYearFilter(admin.SimpleListFilter):
title = 'published year'
parameter_name = 'published_date'
template = 'admin_input_filter.html'
query_params.pop(self.parameter_name, None)
all_choice = next(super().
˓→choices(changelist))
all_choice['query_params'] = query_params
yield all_choice
{% load i18n %}
<ul>
<li>
{% with choices.0 as all_choice %}
<form method="GET">
<input type="text" name="{{ spec.
˓→parameter_name }}" value="{{ spec.qvalue|default_
˓→if_none:"" }}"/>
{% if not all_choice.selected %}
<button type="button" class=
˓→"btn btn-info"><a href="{{ all_choice.query_
˓→string }}">Clear</a></button>
{% endif %}
</form>
{% endwith %}
(continues on next page)
https://stackoverflow.com/a/20588975/2698552
All the above methods will be useful only to a certain extent. Beyond
that, there are 3rd party packages like django-advanced-filters which ad-
vanced filtering abilites.
To setup the package
• Install the package with pip install django-advanced-filters.
• Add advanced_filters to INSTALLED_APPS.
• Add url(r’^advanced_filters/’, include(‘advanced_filters.urls’)) to
project urlconf.
• Run python manage.py migrate.
Once the setup is completed, we can add ``
class BookAdAdminFilter(AdminAdvancedFiltersMixin,
˓→admin.ModelAdmin):
In the admin page, a popup like this will be shown to apply advanced
filers.
A simple filter can be created to filter all the books that were published
between 1980 to 1990 which have a rating more than 3.75 and number of
pages is not more than 100. This filter can be named and saved for later
use.
SEVEN
For data cleanup and heavy content updates, bulk editing on a model
makes workflow easier. Django provides list_editable option to make
selected fields editable in the list view itself.
class BestSellerAdmin(RelatedFieldAdmin):
list_display = ('id', 'book', 'year', 'rank')
list_editable = ('book', 'year', 'rank')
33
Mastering Django Admin
class BookAdmin(admin.ModelAdmin):
actions = ('make_books_available',)
list_display = ('id', 'name', 'author')
Instead of having custom actions in the drop down, we can put dedi-
cated butons for most frequently used actions to reduce number of clicks
needed to perform an action.
https://github.com/crccheck/django-object-actions
class BookAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'author', 'is_
˓→available', 'delete')
˓→format(link)
return format_html(html)
Now in the admin interface, we have delete button for individual objects.
To delete an object, just click on delete button and then confirm to delete
{% block submit_buttons_bottom %}
{{ block.super }}
<div class="submit-row">
<input type="submit" value="Notify
˓→Author" name="notify-author">
</div>
{% endblock %}
EIGHT
39
Mastering Django Admin
Most of the django sites use /admin/ as the default path for admin inter-
face. This needs to be changed to a different path.
url(r'^secret-path/', admin.site.urls)
We can write a system check to check if /admin/ path is used and raise
an error.
@register(Tags.security, deploy=True)
def check_admin_path(app_configs, **kwargs):
errors = []
try:
default_admin = resolve('/admin/')
except Resolver404:
default_admin = None
if default_admin:
msg = 'Found admin in default "/admin/" path'
hint = 'Route admin via different url'
errors.append(Error(msg, hint))
return errors
url(r'^admin/', include('admin_honeypot.urls',
˓→namespace='admin_honeypot'))
3
https://github.com/dmpayton/django-admin-honeypot
Now, we can track all the login attempts on the honeypot admin from the
admin page.
class LibraryAdminConfig(AdminConfig):
default_site = 'library.admin.
˓→LibraryOTPAdminSite'
INSTALLED_APPS = [
# 'django.contrib.admin',
'library.apps.LibraryAdminConfig',
]
Now the admin login page will show OTP login form.
8.3 Environments
https://github.com/dizballanze/django-admin-env-notice
8.4 Miscellaneous
class BookAdmin(admin.ModelAdmin):
def get_urls(self):
urls = super().get_urls()
book_urls = [
path('book_char_data/', self.admin_site.
˓→admin_view(self.chart_data))
]
return book_urls + urls
8.4. Miscellaneous 43
Mastering Django Admin
NINE
FINAL WORDS
45