Improve your experience at WBO. You should consider allowing javascript, cookies and loading of external resources from third-party sites.

Something you should not forget about models manager in Django

While writing a new app for this site, I almost got crazy with model managers (but it's my fault). I wished to have new managers on a model in order to have attributes for getting filtered lists of resources, instead of querying in views; the purpose was also to explore possibilities with managers. Where did I got stuck ?

Let's consider the following model.

1
2
3
4
5
6
7
8
class Article(models.Model):
    STATES = (
        ('draft', 'Draft'),
        ('masked', 'Masked'),
        ('published', 'Published'),
    )
    body = models.TextField()
    state = models.CharField(max_length=32, choices=STATES, default='draft')

I wished to have a manager for each state value.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class ArticlePublishedManager(models.Manager):
    def get_query_set(self):
        return super(ArticlePublishedManager, self).get_query_set() \
                .filter(state='published')

class ArticleMaskedManager(models.Manager):
    def get_query_set(self):
        return super(ArticleMaskedManager, self).get_query_set() \
                .filter(state='masked')

class ArticleDraftManager(models.Manager):
    def get_query_set(self):
        return super(ArticleDraftManager, self).get_query_set() \
                .filter(state='draft')

Then, the managers are defined as model attributes:

1
2
3
4
5
class Article(models.Model):
    (...)
    published = ArticlePublishedManager()
    masked = ArticleMaskedManager()
    draft = ArticleDraftManager()

Looks good. Everything was fine, then I realize that in the administration site, I can only see published articles. Hum. A quick look at the documentation and I see that I was missing a default manager. Then the model became:

1
2
3
4
5
6
class Article(models.Model):
    (...)
    published = ArticlePublishedManager()
    masked = ArticleMaskedManager()
    draft = ArticleDraftManager()
    objects = models.Manager()

But this does not solve the problem in administrative site. I dived again in the documentation and finally understood where the problem lives. The documentation says that when a new manager is added, a default manager has to be defined, and this new default manager is the first declared in the model. Generally, Django uses a lot of conventions; wrongly, I thought that it was enough to define a manager named objects. To get all in order, I just declared objects before published. The final model looks like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Article(models.Model):
    STATES = (
        ('draft', 'Draft'),
        ('masked', 'Masked'),
        ('published', 'Published'),
    )
    body = models.TextField()
    state = models.CharField(max_length=32, choices=STATES, default='draft')
    objects = models.Manager()
    published = ArticlePublishedManager()
    masked = ArticleMaskedManager()
    draft = ArticleDraftManager()

As usual with Django, this is well documented in Custom managers and model inheritance and Modifying initial Manager QuerySets. I did not take care enough when reading documentation... leading to at least one hour lost and lots of self-blaming.

back to top