Customize django admin page from the scratch

In my last project with django, I had to modify and extend django admin page massively, there were cases when I had to wipe out curren admin page and create on my own. Usually people don’t go through this kind of trouble I guess, they just write their own model and view rather than admin page. That could possibly be a work around. But I decided not to do that because django admin provides a lot more thing and actually it felt to be the right way to do that. There were no straight forward tutorial that covers all that so here I go, I can guide you a little bit on your way.

 

Usually when we don’t define our own admin page, but instead we use admin of django.contrib.

Example:

from django.contrib import admin
admin.site.register(Question)

Now for our own admin page we will extend AdminSite.

class MySiteAdminSite(admin.AdminSite):
    site_header = 'This is my Custom Admin Site'

It will not come into action unless we add this to url.py:

import admin

urlpatterns = patterns('',

    # (r'^admin/', include(django.contrib.admin.site.urls)),
     url(r'^admin/', include(admin.my_admin_site.urls)),

)+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

 

Our admin page is working but there is no model registered to our admin page, so there is nothing to show.

class ArticleAdmin(admin.ModelAdmin):

    fields =('author','title' ,'slug','body', 'categories','cover')
    def change_view(self, request,object_id, form_url='', extra_context=None):
        extra_context = extra_context or {}
        extra_context["show_save_as_draft"] = True

        return super(ArticleAdmin, self).change_view(request,object_id, form_url, extra_context)

    prepopulated_fields = {'slug': ('title',) }

    def save_model(self, request, obj, *kwwargs):
        if not obj.id:
            obj.slug = slugify(obj.title)
            
        if request.user.is_superuser or request.user==obj.author:
            super(ArticleAdmin,self).save_model(request, obj, *kwwargs)
        else:
            obj.author=None
            super(ArticleAdmin,self).save_model(request, obj, *kwwargs)


    def queryset(self, request):
        qs = super(ArticleAdmin, self).queryset(request)

        if request.user.is_superuser:
            return qs
        else:
            return qs.filter(author=request.user)

my_admin_site.register(Article, ArticleAdmin)

I actually wrote quite a lot code at my ArticleAdmin, I am going to explain it now. Basically we override  change_view, save_model and queryset method of modelAdmin class. change_view is responsible for embeding edit or create new form. This is what change_form.html look like:  https://github.com/django/django/blob/master/django/contrib/admin/templates/admin/change_form.html

You can pass your variable via extra_context, for my case I passed show_save_as_draft. and I edited change_form.html and put my edited version of it at template/admin/app/change_form.html

At save model, I actually checked some permission and slugify the link. pretty straight forward.

Now queryset is pretty interesting, it usually shows all the articles, does not it? what if we want that when a certain user logs in you want him to show only his articles? This is exactly what I did with filter.

 

Now we have a working custom admin that shows stuff at its dashboard, we are yet to modify our admin page.

class StripeAdminSite(admin.AdminSite):

    def index(self, request, extra_context=None):
        extra_context = extra_context or {}
        extra_context["site_visited_by"] = 10000


        return super(StripeAdminSite, self).index(request,extra_context)


    def censor_article(self,request,id):
        try:
            article=Article.objects.get(id=id)
            if request.user.is_superuser or article.author==request.user:
                article.censored=True
                article.save()
        except Article.DoesNotExist:
            pass

        from django.http import HttpResponseRedirect
        return HttpResponseRedirect(request.META.get('HTTP_REFERER'))


    def get_urls(self):
        #self.admin_site.admin_view(self.approve_staff)
        urls = super(MyAdminSite, self).get_urls()
        from django.conf.urls import url
        my_urls = [
            url(r'^article/censor/(?P<id>w+)/$', self.censor_article),
        ]

        return my_urls + urls

 

Here, index is what is being displayed when you hit /admin.  Your current index looks like this: https://github.com/sehmaschine/django-grappelli/blob/master/grappelli/templates/admin/index.html

You can override it by putting your own index.html at /template/admin/index.html you can pass variables via extra_context parameter of index function.

I wrote a view fuction censor_article at admin class and added corresponding url at get_urls function. Thats basically it.