The communication with the database and the abstraction about the database’s engine we are using is one of the best features of Django. All of this is thanks to the Managers, an interface between database and Django models.
All models in Django have at least one manager. By default, the models use django.db.models.manager. Manager like manager class default, which it’s assigned to objects field in the model.
Usually, the custom managers are used for two reasons:
- Complex and reusable filters. If it’s necessary to create a complex or reusable filter, you can develop your own manager with a method that makes this work.
# models.py class Book(models.Model): title = models.CharField(_('Title'), null=False, blank=False, max_length=256) pub_date = models.DateField() objects = MyCustomManager() # managers.py class MyCustomManager(models.Manager): def my_filter(date, contained_title): return super(MyCustomManager, self).get_queryset().exclude(pub_date__gt=date, title__icontains=contained_title).order_by('-pub_date', 'title')
With this code, Book.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), title__icontains='Hello').order_by('-pub_date', 'title') returns the same data as Book.objects.my_filter(datetime.date(2005, 1, 3),'Hello'). With this functionality we avoid errors when we are writing a very long filter.
- Modify the queryset. In Django, the model’s queryset is a complete or partial instances model list. We can save time by developing a custom manager that works with a filtered queryset.
If we have stored in database 10 objects of the model ‘Article’ (7 of them created in 2015 and the rest created before), we can develop a custom manager that works only with the articles created since 2015. In this case, Article.since_2015.all() would return 7 objects, all created from 2015 onwards. Also, we can keep using the different functions that a manager provides: Article.since_2015.get(), Article.since_2015.filter(), etc.
Also, we can define a manager through a custom QuerySet class as follows:
custom_manager = MyCustomQuerySet.as_manager()
Can we use more than one manager?
Django offers the possibility of using as many managers as we want, but only one is used as default. It is not advisable to use as default manager a custom manager that modifies the queryset. This would cause errors with related models, migrations, dumpdata, etc.
When we define a model, the first manager founded in the model’s fields will be selected as default manager. On the other hand, we can choose manually what manager will be the default manager defining the variable ‘default_manager_name’ in the Meta of our model.
Custom manager inheritance
Since Django 1.10, the models can inherit all managers from their parent classes, even if the parent class is abstract. The inheritance procedure works as follow:
- All managers are inherited
- If there is no managers in the parent classes or in the child class, Django provides the default manager (‘objects’) to the child class.
- If we aren’t using a custom manager in the child class, the default manager (‘default_manager_name’) will be inherited following the inheritance rules of python. We can clarify this procedure with an example:
class ParentNumberOne(models.Model): custom_manager = CustomManager() class ParentNumberTwo(models.Model): another_manager = AnotherManager() class Child(ParentNumberTwo, ParentNumberOne): pass
In this example, the Child’s default_manager_name variable will be ‘another_manager’ because it’s the first manager inherited (Child class inherits everything from the ParentNumberTwo class and later inherits from ParentNumberOne class). If the inheritance would be class Child(ParentNumberOne, ParentNumberTwo):, the ‘default_manager_name’ will be ‘custom_manager’.
Managers in abstract classes
The abstract classes can have managers, but their use it’s only related to the inheritance. The abstract classes haven’t got objects instanced, so the call AbstractClass.objects.all() throws an exception.