Django overrides data manager

Enhancing Django’s ORM with Custom Managers

Though Django’s ORM system is not designed as a complete replacement for SQL, it’s more than sufficient to power many Web applications. You’ve already learned how to supplement Django ORM queries by passing raw SQL commands via the extra method. However, custom managers give you another way. You’ve already used managers, even if you didn’t know it, as in this code example:

really_good_posts = Post.objects.filter(gemlike=True)

Post.objects is a manager object. It’s an instance of a class that inherits from models.Manager, and the methods of this class determine what you can do with a queryset—filtering, in the case of this example.

A custom manager is simply a class you define that also inherits from models.Manager. It can be useful in two distinct ways: changing the set of objects returned by default (normally all objects in the table) and adding new methods to manipulate that set of objects.

Changing the Default Set of Objects

Let’s say instead of writing the previous query over and over, you would like to have a more concise way to tell Django you only want the “gem-like” posts from your database. Easy. Write a manager like this:

class GemManager(models.Manager):
def get_query_set(self):
superclass = super(GemManager, self)
return superclass.get_query_set().filter(gemlike=True)

So far so good, but how do you use it? Simply assign it to be an attribute of your model.

class Post(models.Model):
“””My example Post class”””
title = CharField(max_length=100)
content = TextField()
gemlike = BooleanField(default=False)

objects = models.Manager()
gems = GemManager()

Notice your model now actually has two managers—one called objects and one called gems. They both return querysets; objects behaves just like the default manager of the same name.

Note

Our definition of the objects manager here is exactly equivalent to the one Django normally creates for us automatically. We have to define it explicitly on this model because of the way Django behaves in the presence of additional managers—the first one it sees on the model becomes the default manager, which is used by the admin when selecting objects. If we omitted the objects = models.Manager() line, the admin wouldn’t be able to show us the “non-gem-like” posts.

Having seen the code for the new manager gems, it’s probably pretty apparent what it does. Via Python’s super function, it calls the get_query_set method of the parent class (models.Manager), and then filters the result just like we were already doing before the custom manager.

How do we use it? Just as you’d expect—in the same way you use the default objects manager.

really_good_posts = Post.gems.all()

And of course, because your new custom manager returns a queryset, you can do further operations on it with filter, exclude, and other queryset methods.

Adding New Manager Methods

The custom manager we defined previously is useful syntactic sugar. That is, it makes what would otherwise be a fairly verbose query:

really_good_posts = Post.objects.filter(gemlike=True)

into something much more compact:

really_good_posts = Post.gems()

If we are working a lot with this set of objects, this would certainly make our code read better, but it isn’t the whole story on custom managers. We can get even more power by adding custom methods to our custom manager class, enabling us to pass arguments for more flexibility.

Continuing our contrived example, let’s imagine we want to be able to compactly specify a queryset containing only posts that mention a certain word in both the title and the content. We could specify this with normal Django syntax as follows:

cat_posts = Post.objects.filter(gemlike=True, title__contains=”cats”,
content__contains=”cats”)

That’s getting a little long, and if it’s a type of query that we use repeatedly, we find ourselves wanting something a bit more compact, such as:

cat_posts = Post.objects.all_about(“cats”)

Note

One of the interesting challenges posed by creating custom classes and methods such as these is clear naming. Finding a good method name that reads well in the Post.objects.foo sort of chain is worth some effort; you (and anyone else who uses your code) look at it often. As a rule of thumb, managers should be nouns (“objects,” “gems”); manager methods should be verbs (“exclude,” “filter”) or adjectives (“all,” “latest,” “blessed”).

Here’s what the code behind such a manager method looks like:

class AllAboutManager(models.Manager):
“””Only return posts that are really good and all about X”””
def all_about(self, text):
posts = super(AllAboutManager, self).get_query_set().filter(
gemlike=True,
title__contains=text,
content__contains=text)
return posts

Strictly speaking, custom Managers don’t give you any power you don’t have without them. However, the clearer and less cluttered you can make the API of your model, the easier it is to write and maintain all the other code in your project that makes use of it.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s