Small talk about Django Admin
Django Admin is a Django feature that enables the user to manage the contents of the site. It is recommended that this feature is only used by a limited number of trusted authorized users who manage a site, rather than it being built as the main frontend of a website.
There are several websites using Django and one of them is Awin.
Here, Django reads all metadatas from all models registered in the site and provides automatic interface for the site admin. Developers can register their models, define the fields to display, define filters, and much more to make the admin page look more informative. The HTML template for admin pages are provided by Django itself, therefore, developers are given the chance to override these templates and make them interactive and informative even more.
Then, why do we need custom tags?
Tags in Django are special notations of Django’s template language that would help developers when building Django templates. These tags are designed to fulfill the needs of Django website presentation logic. There are two kinds of tag: built-in tags and custom tags. Built-in tags are the tags that Django automatically provides for developers. For a deeper look at built-in ones, you can learn more about them here.
Sometimes, developers find out that there are some functionalities that built-in tags can’t cover. Addressing this need, Django also provides custom tags developers to freely define so as to facilitate any special functionalities. One important use of this tag is that it can be used to provide custom data for our templates.
Understood! Now, where should I start?
Before going any further, we need to first make a Django Project with an app inside it. Let’s say we name the app: sale. Inside this sale app, we need to make a simple model in models.py, such as:
class Sale(models.Model): TYPE = ( (0, “In hold”), (1, “Sent”), (2, “Delivered”), ) product_name = models.CharField(max_length=50) price = models.DecimalField(null=True, decimal_places=2) date_created = models.DateTimeField(auto_now_add=True) type = models.IntegerField(default=0, choices=TYPE)
After that, we need to make a templatetags folder inside this sale app folder, with __init__.py inside it. We also need to add the sale app into INSTALLED_APPS in our project settings.py.
All set up! How do I make my first tag?
Our tags should be set up inside the folder templatetags we’ve prepared before. There are three types of custom tags supported by Django: simple tag, inclusion tag, and assignment tag. We’ll try to make one custom tag for each kind.
Simple tag is just like other Django template tags. What’s special about this custom tag is that this tag can receive a value and return a string value. For example, when we need the count of our sale object in our admin page, we can use this tag to get it. Inside our templatetags, we can create a file get_sale_count.py and write this code inside the file:
from django import template from sale.models import Sale register = template.Library() @register.simple_tag def get_sale_count(): return Sale.objects.count()
We can also make another file called get_sale_delivered_count.py to make a tag which can return the count of sale objects with type = 2 or delivered. Here below is the sample code for this file:
from django import template from sale.models import Sale register = template.Library() @register.simple_tag def get_sale_delivered_count(): return Sale.objects.filter(type=2).count()
The second type of tag is the inclusion tag. We use this type of tag when we want to render an HTML template with context data return by the tag itself. For example, we want to make a dedicated admin page for our sale objects with sale objects as the context data. Let’s say we have our template in sale_admin.html file, we can make a file called sale_admin.py inside our templatetags folder with code like below:
from django import template from sale.models import Sale register = template.Library() @register.inclusion_tag(‘sale_admin.html’) def sale_admin(): data = Sale.objects.all() return { ‘variable’ : data }
Our third and final type of tag is the assignment tag. This tag is quite similar with simple tag. It can receive input values, and return another value. The difference is this tag can return anything and not only string values. We can use this tag to return a dictionary which we can use for making tables or charts. For example, we want to make a chart that display our sale objects growth by month. For this chart, we need to pass periodical data from our objects to the return value. Below is the sample code that you should write inside chart_sale.py in your templatetags folder.
from django import template from sale.models import Sale register = template.Library() @register.assignment_tag def chart_sale(year) :context_data = {} context_data[‘summary’] = [] queryset = Sale.objects.filter(date_created__year=year) count = queryset.count() if (count == 0): return contex_data summary = queryset.annotate( # to get periodical count of objects period=Trunc( ‘date_created’, ‘month’, output_field=DateTimeField(), ), ).values(‘period’).annotate(total=Count(‘id’)).order_by(‘period’) context_data = {} context_data[‘summary’] = [] i = 1 # month iterator j = 0 # object iterator while (i <= 12): if (j < len(summary)): x = summary[j] if (x[‘period’].month == i): context_data[‘summary’].append({ ‘period’: x[‘period’], ‘total’: x[‘total’], ‘percent’: x[‘total’]/count * 100, ‘month’: i, }) i += 1 j += 1 else:context_data[‘summary’].append({ ‘period’: str(i) + ‘/’ + str(year)[2:], ‘total’: 0, ‘month’: i, ‘percent’: 0, }) i += 1 else:context_data[‘summary’].append({ ‘period’: str(i) + ‘/’ + str(year)[2:], ‘total’: 0, ‘percent’: 0, ‘month’: i, }) i += 1 return context_data
That code will return context_data variable which will contain 12 objects which represent 12 period in a year with count of objects created in that period.
My tags are done, how do I display them?
In this article, we will override the main dashboard in admin page. To do that, we have to make a templates folder in our project root directory. Also, don’t forget to add templates to TEMPLATES [ ‘DIRS’ ] in settings.py. Inside the templates folder, add another folder called admin. In this folder, we need index.html file that will override the original Django admin dashboard template. On top of the file, we need to use this code to extends base_site template and import our custom tags and some other tags:
{% extends “admin/base_site.html” %} {% load i18n static%} {% load chart_sale %}
For the base template, you can go here and copy the code of the original index.html. After that, we can add inside the {% block content %} section our custom divs. First, let’s try to make a table using our chart_sale tag we have defined before. Here is the sample code that you can place inside the content block.
{% chart_sale 2019 as summary %} <table style=”width: 100%;”> <thead> <tr> <th> <div class=”text”> <p><strong>Period</strong></p> </div> </th> <th> <div class=”text”> <p><strong>Count</strong></p> </div> </th> <th> <div class=”text”> <p><strong>Percentage</strong></p> </div> </th> </tr> </thead> <tbody> {% for row in summary.summary %} <tr> <td> {{ row.period }} </td> <td> {{ row.total }} </td> <td> {{ row.percent }}% </td> </tr> {% endfor %} </tbody> </table>
2019 in the first line is the value we pass to the tag function, and summary is the output variable that contains our tag’s return value. The for block means that for every object in summary, we create a table row containing the object’s information. Here, for example, we display the objects’ periods, total counts, and percentages.
Table is pretty simple, then how about charts?
Making charts is actually not that difficult, and bar charts are especially simple. First, don’t forget to load our tag on top of our template file like we did before while making the table. After that, you can use this code to make your bar chart:
{% chart_sale 2019 as summary %} <div class=”bar-chart”> {% for x in summary.summary %} <div class=”element”> <div class=”bar” style=”height:{{x.pct}}%”></div> <div style=”align-items: center;”> <p style=”font-size: 12px;”> {{ x.month }} </p> </div> </div> {% endfor %} </div>
And that’s it! Pretty compact right? You can make it more interesting by adding tooltips or other styling components. You can also decorate your template by overriding the {% block extrastyle %} and fill it with your own styling (<style> HTML tag).
Conclusion
One important management feature that Django provides is Django Admin. Here, authorized admin users can manage the content of their Django website. To make the admin pages more informative, we can override the admin page templates and build our own custom tags to provide custom data for our templates. After that, we can make our own tables and charts to present the data more interactively.
References
Custom tags
- https://pypi.org/project/pyexcel-xls/
- https://docs.djangoproject.com/en/2.2/howto/custom-template-tags/
Django Admin