Fellow Lincoln Looper, Martin Mahner, posted an excellent write up on how to use proxy models to separate staff and user accounts in Django’s admin. We frequently have a need for this in client projects, but people don’t often consider proxy models for this functionality. Here’s another scenario we came across recently where they came in useful.

Background

Proxy Models were introduced in Django 1.1. The official documentation describes their usage, comparing and contrasting them with Model inheritance. One fact the official documentation does not explicitely state is that django won’t let you register multiple ModelAdmins to the same Model. This is where proxy models can come to the rescue.

Example

Consider a newsroom application with articles that must be categorized into sections:

#models.py
from django.db import models

class Section(models.Model)
    name = models.CharField(max_length=90)
    slug = models.SlugField(max_length=90)
    # ... More fields

class Article(models.Model):
    section = models.ForeignKey('Section', null=True, blank=True)
    # ... more fields

class ReviewArticle(Article):
    class Meta:
        proxy=True

Here, ReviewArticle is just a Python construct that doesn’t change the underlying database structure. It simply provides a way to logically segregate objects within the same model/table.

Benefits of Proxy Models

A typical usecase for proxy models is to overwrite the Python behavior of a model, by changing its default manager or class methods. As noted by Martin, proxy models are great for “faking” content separation in the admin. In addition, a new set of permissions is automatically setup for your proxy model: can add, can change, can delete.

This lets us present a different user experience for objects that are really in the same database table. In our example above, we can give admins a place to edit review articles that is customized specifically for the needs of review articles, even though under the hood, they are the same data model.

ModelAdmin Example

Here’s a simple example of using a proxy model in the Django admin

from django.contrib import admin
from myapp.models import Article, ReviewArticle

class ArticleAdmin(admin.ModelAdmin):
    pass
class ReviewArticleAdmin(admin.ModelAdmin):
    pass
    def queryset(self, request):
        return self.model.objects.filter(section__name="reviews")

admin.site.register(Article, ArticleAdmin)
admin.site.register(ReviewArticle, ReviewArticleAdmin)

Now modify your proxy model so all ReviewArticle objects are saved in the review section:

class ReviewArticle(Article):
    class Meta:
        proxy=True
    def save(self, *args, **kwargs):
        self.section = Section.objects.get(name='reviews')
        super(ReviewArticle, self).save(*args, **kwargs)

This is a trivial example, but with the customization options available in the Django admin, you can present a very different interface to your users. There are lots of options you can tweak in the ModelAdmin, including:

  • list_filter
  • list_display
  • search_fields
  • inlines

Conclusion

This simple solution can lead to a drastic improvements to the user experience. It is easily maintainable and doesn’t require having to dive into customizing the admin HTML/CSS.