Tag Archives: django

Extending the Django Admin Interface

This blog post is just a collection of notes I took along the way that might help you if you’ve had a play with the Django Admin and now are wondering how to get a little more out of it.

I have just spent a few weeks working on the first phase of an anlaytics dashboard application that is built using a mixture of Django, jQuery and Flash with the Isotoma crew. The project needed some way to set up the different sites and regions and rather than create these screens “by hand” we decided that the default Django Admin functionality would do this job well.

django_admin

When I first saw the Django Admin screens I was shocked at how much you get “for free”. Having designed your database you can start editing it using screens that look better and work better than many CMSs.  And as a developer, the thought of writing code to do very simple tasks, such as adding data, deleting data, finding data is painfully boring and Django provides this functionality, along with user management, memberships, searching, filtering and permissions … all out-of-the-box.  And did I say it looks nice too?!

The example above shows a “Region listing” screen, that with a few quick tweaks has links to the Region on the site, lovely icons, shows a list of sites in that region and even has a handy dandy plus button that preopulates the site creation form with the relevant region.

Despite Django Admin’s obvious abilities it is often thought of as being “only for the developer” and not something that you’d let a client loose on. And if it was the case that a client wanted bespoke features you would of course need to create your application from scratch, but I believe that the default Django Admin can get you a lot further than you might first assume.

My Approach

All of the Django Admin application is hackable, but I am loathe to hack it willy-nilly because I like to be able to swap in a later version of Django and know that it’ll just work without having to remember a whole heap of tweaks I’ve made to Django Admin files. For this reason, I’m uncomfortable with the gorgeous Django Grappelli project, which adds a shade more visual pzazz to the Django Admin screens because it requires me to alter files in the Django Admin application ( I have a cludge that fixes this though ).

I like things to be simple. Lots of the solutions for extending the Django Admin screens you can find on Django Snippets are a step too far for me, requiring too many inter-dependent settings to work in combination, or worse.

And, with the 3 or 4 Advanced approaches I’ve used (below) you can add functionality to lists of objects, change the way the editing form behaves, add extra functionality to the editing forms AND make it look nice.

The Django Way Of Doing Things

One of the things that irks me about the Django community is that there isn’t the Django recommended way of doing things. The community assumes that I’ll have my own ideas and to force me to accept their arbitrary design decisions would be wrong.

Of course, they are kind of right, but at times I’d like a little guidance on best practice about where to keep your image files, your CSS files etc. I’d like there to be default way of doing things designed by people cleverer than me. I’d like someone to build a version of Django that included WYSIWYG editing on the admin side (with JQuery) and a load of examples of how to use jQuery built into the public site. I want jam on it!

Despite lots of Django applications advertising themselves as pluggable, they rarely are. When I’ve browsed the source code for lots of these applications I’ve found different ways of arranging templates, urls, libraries and graphics, making it almost impossible to drop a Django app into your project and have confidently make the changes that will guarantee it will work.

There a huge list of so-called pluggable apps here. It’s worth exploring how other apps do things just for the hell of it.

The Starter Project Poisoned Chalice?

I have seen Starter Projects being mooted and failed again and again. And whilst I doubt the use of them for anything too complex they are great for sharing a House Style (without dictating it) and for plonking copy & paste example of code for those things you can never quite remember how to do. There are quite a few starter projects on Google Code too.

My Starter Project  is one that I use to do the boring things that always need, often forget to implement but shouldn’t really waste time developing. You the kind of thing, favicons, robots.txt, RSS feeds… but also things like jQuery and the like.

Pretty much every Django project I create has a “templates” folder in which there is a base.html, index.html (which extends base.html). Every project I create has a CSS file somewhere in the mix. I wish that ./manage.py was more “on rails” than it currently is.

A good example of Django’s “hands off” approach is media handling. For example, Django kind of pushes you towards using a “/media/” URL for your Admin media and  folder called “static” for images,css and .js files. Early on I decided that I’d prefer it if this folder was called “media” and set about fixing up the URL handling to “work my way” – which was fine until I tried to integrate other peoples’ code. Other people assumed I wouldn’t be so stupid as to go against the grain and do things my way. Other peoples’ code didn’t work.

I tend to have a folder structure that looks a bit like this…

folders

… with css, images and js folders being used for shared items. In this case my application is called “main” so have a folder called “main” and in that put the css, images and js files used only by this application. The only reason for doing this is that it makes applications very easy to re-use. You simply add them to your settings file, move the media and then wire in the URLs.

There are a number of real  “starter” projects for Django such as this one, which have examples of how to create a blog, events or book databases.

And yes, I appreciate the lunacy of calling my application “main” but it does mean that I can often drop an application folder into Django and know that main.views.index will automatically map onto “/”.

My Misc Application

A frequent pain in the arse (for me) with Django was getting a WYSIWYG editor ( such as TinyMCE ) to play nicely with Django Admin. Ideally, I often want to create an editing interface that both looks and works as good as WordPress (without having to use PHP).

And with this in mind, I’ve created an application called “Misc” that has WYSIWYG editing installed by default that I can use when setting up a project to check that everything is in the right place and working before I start work on my application proper.

This app can have data bundled with it (in the form of fixtures) and contain a heap of code that I use for reference. For example, it’d be great if Django would auto-generate admin.py files but it doesn’t and I can never remember how to define an Admin class, so having an example to hand makes development quicker.

I always create a constant called LOCAL_FOLDER with the full path to django project for when I’m running it locally and on a server.

I tend to create a “libs” folder and a “scripts” folder at root level and add their paths to sys.path in settings.py

My Standard Imports

As part of my Starter Project approach, there are a number of “must have” additions.

  • Django Evolution. This is, for me, the most essential Django app because it let’s me change my database models and update the database without exporting and re-importing all the data. Whilst it might be useful as a database versioning tool, I’ve never used it to rollback to a previous database schema.
  • Django Filebrower. This is essentially a popup window that let’s you chose files from a given folder integrated into TinyMCE.
  • Django Tagging. This is wonderful. Lots of external apps use it so it’s worth adding it just for that reason.
  • Django Pagination. Great code that lets you do pagination in the template.

Honorable mentions go to…

  • DjangoLogging. This puts debugging information into the response object. Very cool (but watch out when it adds stuff to your Alax responses).
  • Django Extensions. This adds extra commands to manage.py. I already love it for ./manage.py shell_plus alone.

Standard Django Admin Stuff

Getting started with Django admin is great. The latest version of Django has lots of lovely new features to explore. The admin functionality has a raft of features you might not have tried yet… including…

  • Listing viewings list_display can take a function (but a list_filter can’t)
  • Filters are lovely. I almost always create a “modified” field so that I can just see the objects I’ve edited today. Because I can.
  • Default ordering is handy.
  • Slug fields are great (but need a little care) for creating textual urls (or slugs).
  • You can use Fieldsets to re-organise the editing form, and hide things a bit.
  • You can define a function for upload_to in FileFields to make personalised upload folders so that uploaded images don’t overwrite each other.
  • I just discovered the CurrentUserField ( very useful )
  • You can make the names of database classes more friendly with the class Meta: verbose_name_plural in models.py
  • You can put the buttons at the top AND the bottom of a change form.
  • You can add images or links in the listings view by defining a function then adding my_func.allow_tags = True

Warning. Admin.py hasn’t totally evolved from the “old way” of doing things yet. Somethings are still defined in models.py such as the help_text attribute.

Rebranding Your Django Admin Site

Every client wants their admin screens to be branded.They are like that.

And, I’ve found that if you are running lots of Django sites, then for your own sanity making each admin site different really helps. One of the first things I do is to install a proper favicon to make jumping between Firefox tabs more intuitive. Trust me this really helps.

Given that I don’t like hacking the Django Admin code… my solution was to hack the Django Admin code. I did try to copy only the admin files I’d hacked, sort of  overshadowing the templates.  And whilst this is a good idea, it doesn’t work. Django isn’t Zope.

What I do is copy the templates folder from django.contrib.admin into the root of my project and the add it to my settings.py like this…

TEMPLATE_DIRS = ( ‘templates’, )

This then means I can hack a logo into admin screens but I always have the fallback of removing my hacked folder and using the vanilla admin screens if it all goes horribly wrong. I can also easily update django in one hit.

In general, you can quickly theme your site simply by editing these files.

  • login_form.html
  • base_site.html
  • base.html

Whether you choose to play with Grappelli, which makes the admin screens even more beautiful is up to you, but I found I could get most of visual eye-candy by simply using their css file and avoid some of the issues they have when it comes to displaying collapsed fieldsets (trust me they’re lovely).

Adding this to my base_site.html

<link rel=”stylesheet” type=”text/css” href=”/static/css/grappelli.css” >

…after having fixed up the paths to media in grappelli.css creates something like this…

grappelli version

Advanced Django Admin Stuff

The features in the standard Django Admin are enough for most projects but I frequently come across issues where I wish there was a way of pushing Django a just that bit further.

As ever, any admin hacking has to take a “light touch” approach (not requiring lots of changes or patches) and not be about jQuery manipulation ( I’m not a JavaScript programmer! ).

Custom Django Forms

When you start using forms.ModelForm then you a whole heap of new features, such as being able to define actions (see below). My only issue with django forms is that when searching for examples you tend to get lots of old newforms examples.

Whilst you can create a Django admin site without touching ModelForms (which is nice)… the more powerful features come when you use these forms.

1. Custom Actions in Object Listings

New in Django 1.0 is the ability to create “actions”. Adding custom actions to the list view (for multiple items) means that you can do things like add an “approve” method to work list of objects, like this..

def approve(modeladmin, request, queryset):     print queryset # the selected objects     msg = "Items have been approved"     self.message_user(request, "%s" % msg)     approve.short_description = "Approve selected items"
class CountryAdmin( ButtonableModelAdmin ):
    list_display = ['name', 'code', 'small_image_img']
    actions= [approve]

See the http://docs.djangoproject.com/en/dev/ref/contrib/admin/actions/#ref-contrib-admin-actions.

custom actions

The example above shows both CustomActions and adding HTML to the list_display attribute to show the flag image.

2. Custom Display Widgets in the Change Form

To be frank, editing uploaded images in Django Admin is weak. I tried a number of approaches to improve on how images are handled in and editing form but found that the AdminImageWidget worked for me. To use it, I simple add the class to my admin file and overshadow the render() method. Simple! I can then add this line to my form.

large_image = forms.ImageField(widget=AdminImageWidget() )

You can see a simple example of this in the large and small image above, where I have changed the image widget to also display the image.

3. Custom buttons to the Change form

Lastly, it’s great to be able to add buttons to the editing form (next to the history button). Again, I simply add the  ButtonableModelAdmin class to my admin file and then add a function to my admin class and bingo, my admin forms have new features.

class CountryAdmin( ButtonableModelAdmin ): def my_button(self, obj):          "do something here"          url = "/admin" + "/".join(str( obj._meta ).split(".")) + "/" + str(obj.id) + "/"           return HttpResponseRedirect( url )
my_button.url = "my_button"    #puts this on the end of the admin URL. my_button.short_description='My lovely button' buttons = [ my_button,]

You can see “My Lovely Button” in one of the forms above. Isn’t it lovely?

4. Collapsed Fieldsets

Collapsed fieldsets are for when you want to edit lots of messy related objects. I first came across them in the Grappelli theme, and having tried them, there’ no going back.

Collapsed fieldsets really tidy up the interface, but I found another way of achieving the same idea here  http://www.djangosnippets.org/snippets/1492/

In the example below, the Statistics have been hidden by using ‘classes’:[‘collapse’], in the fieldets attribute, whilst  the Status Messages (a collection of related objects) are hidden using collapsed fieldsets. Both approaches are essential for tidying up a complicated interface.

collapsed fieldsets

A Common Django Admin Gotcha

Apart from old documentation lying around the internet, the most common admin gotcha that still gets me is the one whereby you make a change to your admin.py file, the server noticing the changes, reloads. You then get the error that your model has already ready been registered. Bizarrely, if you then make another request to the server you get a 404. Rebooting the server fixes this.

Conclusions

Having dug around a little I found that it was easier than I thought to…

  • Add actions to lists of objects
  • Add buttons to the edit form of individual objects
  • Create custom widgets for editing individual rows (such as images)
  • Theme the Django Admin without knackering your django install
  • Display related Inlines nicely (ish)

… which for most admin needs, is all you need.

Nobody Tells Me Anything… Ever…

… or is that I’m not listening? You tell me…

One of the most important features of open source software, is not the fact that you can fix bugs yourself, or the fact that it’s free, it’s the incredible educational good it effortlessly spreads. It was whilst I was looking at someone else’s Django code that I discovered this…

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.generic import GenericForeignKey

class MyListingItem(models.Model):
content_type	= models.ForeignKey(ContentType)
object			= GenericForeignKey()
object_id		= models.IntegerField()

… I’ve been trying to find a way to do this for years. I mistakenly thought that SQL was “a bit broke” because it didn’t naturally do this. What this does is let you build a list of any old objects so that you can easily have different methods for each object types. It’s effectively storing objects in an object-oriented way rather than relating objects in a relational database way. So in a TumbleLog I might store Delicious items, Tweets, Blog Posts etc. and each would have their own methods for being fetched, displayed etc. This, to me, is much more natural and sensible than the way I’ve been doing it.

If you know what I’m talking about you probably already know about this. If you haven’t a clue what I’m talking about it really doesn’t matter except to say, it’s amazing how you can literally labour under a small misapprehension for years that sends you miles off track… or is it just me (and how would I know?).

Django | 34. Generic relations | Django Documentation

DjangoCon 2008: Reusable Apps

I watched a few of the DjangoCon videos. I still marvel at how wonderful it is to be able to dip into a conference without stumping up the air fare. Of course Cal was the funniest, but this one, despite being a bit dry, was undoubtedly the most useful, and inspirational but in a geek way.

If you develop with Django and wonder why your apps get bloat, this video may help you. It did me. It’s sort of a mini philosophy of how to develop using Django that sort of echos the “do one thing only and do it well” idea and it helped me to decide where to draw the line with regards to Django applications.

YouTube – DjangoCon 2008: Reusable Apps

Byteflow and Eggs Benedicts

The Byteflow Blog Engine is written in Django. I will have to give this a whirl sometime.

I’m still getting my head around the quirks of the new WordPress Add media interface which to me is as ungainly as sitting Eggs Benedict on top of two circles of toast rather than the more traditional english muffin.

It was at a lovely hotel in Norwich that they served up this oddity, I mean, circles of toast?

Whilst in NZ I did a comprehensive survey of Egg Benedicts and found that apart from the eggs, what constitutes a “benedict” is completely up for grabs… I had them on muffins, bagels, bread baps… the meat was sometimes cooked ham, bacon or italian ham which made the ranking of the various Eggs Benedicts very difficult.

The outright winner though had to be “The Cup” up in the hills in CHCH.