Search code examples
djangodjango-modelsdjango-managers

Struggling to see the utility of custom Django Model Managers


I do not have any code for this question, so this will be more about the utility of customer Managers, more so than it is an implementation question.

I have read the documentation, many blog posts and tried to implement some myself, but I cannot find the utility in Django custom model Managers. I understand that they can help segment code and can help with DRY principles, but I struggle to see how. So this question can be broken down into a few subpoints

  • How does this help with DRY principles?
  • How does this help with code segmentation?
  • What can managers do that views or model methods cannot do?

Solution

  • You can encapsulate filtering, annotating, subqueries, etc. in a manager. For example:

    class ActiveManager(models.Manager):
    
        def get_queryset(self, *args, **kwargs):
            return super().get_queryset(*args, **kwargs).filter(
                active=True
            )

    and then apply this to a module with an active field:

    class MyActiveModel(models.Model):
        active = models.BooleanField(default=False)
    
        objects = ActiveManager()

    Now if we use MyActiveModel.objects.all(), it will only retrieve records with active = True. We thus do not need to apply this filtering in all views, we can make use of objects like in any model, and it will transparently filter the object.

    A Manager is also often used if you want to make an extension on the queryset.

    For example we can define an AuthorQuerySet, that offers an extra method .of_user(…) that for example retains the records where the given user is the author, or the user is a super user, retrieve all records with:

    class AuthorQuerySet(models.QuerySet):
    
        def from_user(self, user):
            if user.is_superuser:
                return self
            else:
                return self.filter(author=user)

    and then work with a manager that allows one to do this filtering:

    from django.conf import settings
    
    class Post(models.Model):
        author = models.ForeignKey(
            settings.AUTH_USER_MODEL,
            on_delete=models.CASCADE,
        )
    
        objects = AuthorQuerySet.as_manager()

    then we can retrieve objects that a user can see with:

    Post.objects.filter_user(myuser)

    or do this in a chain of filters:

    Post.objects.filter(title__istartswith='The').filter_user(myuser)

    It thus mainly deals with encapsulating logic, such that the filtering, annotating, etc. is defined only once and then used in the views in a transparent way. In other words, the view does not need to worry that it will retrieve only active objects, the manager takes care of that.

    How does this help with code segmentation?

    A model focusses on storing a record in the database and to present data. Normally it should not decide what records you see in what situation. The views are used for that. But if the logic is the same, it makes no sense to write this in the views, since it makes the code error prone in case the filtering, annotation, etc. that (nearly) always should happen, is in that case copied to a large number of views. By implementing this in the manager, we have a layer between the models and the views that can filter, annotate, etc. data always in the same way.

    What can managers do that views or model methods cannot do?

    One can repeat the same filtering code once per view, but that makes it hard to change that logic later on in the process, since then one should look in which views these models are accessed, and make changes to all these views. This would make it easy to overlook something and thus end up with an inconsistent state.

    One can also define static methods in the models to do the filtering. But the same model can work with multiple managers, such that, depending on the case, another model is selected.

    It is in other words a layer between the model and the view, and it works in a many-to-one to the model and a many-to-many to the view code.