One of the Django pride itself is being ridiculously fast - fast in terms of development. It was designed to help developers take applications from concept to completion as quickly as possible. That's why the framework automates creation of admin interfaces for models. Admin interface is an essential to enable the adding, editing and deletion of site content with permission limited to trusted site administrators.

However, you wish to have something more on default admin interface - beautiful design, extra functions, customized version of design. There are many work around for this, Have look on wiki https://www.djangopackages.com/grids/g/admin-interface.

Among them, django-suite is our favorite. They claim to be mordern theme. For us, it just beautiful design, and moreover it works perfectly. At some points, you need to customize theme drastically. Like, in our case we needed to have custom dashboard with charts, graph and lot of summery information.

None of existing packages fullfill our need. Finally, we needed customizing admin theme.

Requirement

Replace Django default admin index with dashboard that contains - chart and summery information. For this tutorial, we'll make django project that shows user count by group whenever you logged in in django admin.

 

Approach

We decided to replace index.html by Monkey Patching (admin/templates/index.html) with our custom html and build various views that response summery data in JSON format. We decided directly feed some those data to Google Charts and generate different graphs.

Action

For this example, assume following structure of project. This project was saved in /var/www directory

jaljaleproject
  |--jaljaleproject
  |     |-- settings.py 
  |     +-- urls.py 
  |--jaljaleapp
  |     |-- __init__.py
  |     |-- views.py 
  |     +-- models.py
  +--static
        +-- admin
              +-- templates
                     +-- admin
                          +-- index.html
   

1. Replacing default index (Monkey Patch)

For this we have to provide our index_template file. Add following line in jaljaleproject.urls.py, before urlpatterns = patterns( ... code starts.

from django.contrib.admin.sites import AdminSite
AdminSite.index_template = 'static/index.html'

Make sure var/www/jaljaleproject/static is added in static STATICFILES_DIRS in settings.py

2. Creating custom function

a. We added view functions in jaljaleproject/views.py that generate some aggregate data for dashbboard reporting. For example, we wanted count number of registered user by group and construct Google Chart.

@login_required
def countuser(request):
    user_list = User.objects.values('groups__name').annotate(ucount=Count('id'))

    cols = [
        {'id': '1', 'type': 'string', 'pattern':'', 'label': 'Group'},
        {'id': '2', 'type': 'number', 'pattern':'', 'label': 'Count'}]

    rows = []
    for user in user_list:
        name = user['groups__name'] or 'Unassigned'
        rows.append({'c': [
                {'v': name, 'f': name},
                {'v': user['ucount'], 'f': ''}]})

    return HttpResponse(json.dumps({'cols': cols, 'rows': rows}), content_type='application/json; utf-8')

3. Editing jaljaleproject.urls.py

Now let us add routing inforamtion in urls.py. 

url(r'usercount/$', 'japjaleapp.views.usercount', name="usercount"),

4. The static/admin/index.html file

{% extends "admin/base_site.html" %}
{% load i18n admin_static %}

{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/dashboard.css" %}" />{% endblock %}

{% block coltype %}colMS{% endblock %}

{% block bodyclass %}{{ block.super }} dashboard{% endblock %}

{% block breadcrumbs %}{% endblock %}

{% block content %}
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">

google.load('visualization', '1.1', {'packages':['corechart']});

function drawUserCount(data) {
    var options = {'title':'User Group',
                   'width':400,
                   'height':300,
               };

    // Instantiate and draw our chart, passing in some options.
    var chart = new google.visualization.PieChart(document.getElementById('user_gruop'));
    chart.draw(data, options);
}

$( document ).ready(function() {

    data = $.ajax({
        url: "{% url 'usercount' %}",
        dataType:"json",
        async: false
    }).responseText

    var dataAll = new google.visualization.DataTable(data);
    google.setOnLoadCallback(
        drawUserCount(dataAll)
    )

});
</script>
<div id="user_gruop"></div>

{% endblock %}
{% if app_list %}
    {% for app in app_list %}
        <div class="app-{{ app.app_label }} module">
        <table>
        <caption>
            <a href="{{ app.app_url }}" class="section" title="{% blocktrans with name=app.name %}Models in the {{ name }} application{% endblocktrans %}">{{ app.name }}</a>
        </caption>
        {% for model in app.models %}
            <tr class="model-{{ model.object_name|lower }}">
            {% if model.admin_url %}
                <th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
            {% else %}
                <th scope="row">{{ model.name }}</th>
            {% endif %}

            {% if model.add_url %}
                <td><a href="{{ model.add_url }}" class="addlink">{% trans 'Add' %}</a></td>
            {% else %}
                <td> </td>
            {% endif %}

            {% if model.admin_url %}
                <td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
            {% else %}
                <td> </td>
            {% endif %}
            </tr>
        {% endfor %}
        </table>
        </div>
    {% endfor %}
{% else %}
    <p>{% trans "You don't have permission to edit anything." %}</p>
{% endif %}
</div>


{% block sidebar %}

{% endblock %}

Now run the app and see the django admin.