Development

The importance of Django Model Managers

Apr 12, 2017
the_importance_of_django_model_managers main.png
5 min read

What is a Model Manager?

 

For every model in Django, there is an associated manager which is an interface for interacting with the database. To access the manager of a model, you simply need to use the objects property on the model e.g. FootballTeam.objects, however, this property is customisable.


If you wanted the manager property to be named teams, you would simply declare an attribute named teams of type Manager within the model.

 

                                                                   Screen Shot 2017-04-10 at 11.47.52 AM.png

 

Example:  

from django.db import models

FootballTeam(models.Model):
    teams = models.Manager()

 

There is a very big use case for creating a custom model manager for each model in your django application and this is a beneficial habit to get into for both yourself and other developers that have to work with the data models. This use case is maintenance.

 

Why are Model Managers so important?

 

As mobile app developers, we should be writing clean, efficient, maintainable code and follow the DRY (Don’t Repeat Yourself) software architecture principle. The django model managers play a good part in ensuring you tick all of these boxes; being more vital for maintenance and adhering to the DRY principle.

 

The reason it ticks these boxes is that it keeps the querying in your django application in a centralised place; queries that your application perform come from one place instead of being wrote on the fly in each View that requires the query.

 

                     Screen Shot 2017-04-10 at 11.48.56 AM.png

 

Consider the following example:    

 

FootballTeam Model

from django.db import models.DateField

import datetime

class FootballTeam(models.Model):
PREMIER_LEAGUE = 0 CHAMPIONSHIP = 1 LEAGUE_ONE = 2 LEAGUE = ( (PREMIER_LEAGUE, "Premier League"), (CHAMPIONSHIP, "Championship"), (LEAGUE_ONE, "League One"), ) name = models.CharField("Name", max_length=255, default="") nickname = models.CharField("Nickname", max_length=255, default="") league = models.IntegerField("League", choices=LEAGUE, default=0) founded = models.DateField("Founded", default = datetime.date.today)

 

Note: League would normally be a separate model here, however, for the purpose of this example, using a ChoiceField will suffice.

 

                     Screen Shot 2017-04-10 at 11.49.22 AM.png                             

 

Querying the FootballTeam Model  

 

API View:

from rest_framework.generics import ListAPIView

from .models import FootballTeam

class TopFootballTeamList(ListAPIView):
    def get_queryset(self):
        return
FootballTeam.objects.filter(league=FootballTeam.PREMIER_LEAGUE)

 

Django View:

from django.views import generic

from .models import FootballTeam

class TopFootballTeamListView(generic.ListView):
    template_name = 'football/teams.html'
    model = FootballTeam

    def get_queryset(self):
        return
FootballTeam.objects.filter(league=FootballTeam.PREMIER_LEAGUE)

 

The issue with the code above is there are two separate views which require the same functionality and therefore perform identical queries in the get_queryset method. This goes against the DRY principle and also causes extra development work when requiring to maintain these views.

 

If a requirement was to change and the “top football teams” were classed as Premier League and Championship teams rather than just Premier League teams, there are two views that have to be maintained regarding this query which leads to modifications having to be made in both respective views. The get_queryset method in both of these would have to be changed to the following:

from django.db.models import Q

    # View

    def get_queryset(self):
        return
    FootballTeam.objects.filter(Q(league=FootballTeam.PREMIER_LEAGUE) |
            Q(FootballTeam.CHAMPIONSHIP))

 

This may seem like a very small problem having to alter the query in two places, however, this is a very simplified example. In large systems where there will be more views and more complex queries, this type of maintenance takes up development and testing time, makes debugging harder, goes against the DRY principle and due to a query potentially being missed, can lead to bugs.

 


Screen Shot 2017-04-10 at 11.49.35 AM.png

 

How can this problem be solved?

 

To solve this issue, we can make use of model managers to create a centralised area for retrieving the top football teams using the FootballTeam model. This is achieved by the following:

from django.db import models
from django.db.models import Q
import datetime

class FootballTeamManager(models.Manager):

    def top_teams(self, **kwargs):
        return self.filter(Q(league=FootballTeam.PREMIER_LEAGUE))

FootballTeam(models.Model):

    PREMIER_LEAGUE = 0
    CHAMPIONSHIP  = 1
    LEAGUE_ONE = 2

    LEAGUE = (
        (PREMIER_LEAGUE, "Premier League"),
        (CHAMPIONSHIP, "Championship"),
        (LEAGUE_ONE, "League One"),
    )

    name = models.CharField("Name", max_length=255, default="")
    nickname = models.CharField("Nickname", max_length=255, default="")
    league = models.IntegerField("League", choices=LEAGUE, default=0)
    founded = models.DateField("Founded", default=datetime.date.today)

    objects = FootballTeamManager()

 

In the code snippet above, a model manager has been created named FootballTeamManager. This manager will be the sole place for implementing queries regarding the FootballTeam model, and by following this, we have created our centralised place for queries regarding this model. This follows the DRY principle and also ensures we have a central place for all developers on the project to go to when they need to modify a query; this is maintainable code.

 

To associate the FootballTeamManager with the FootballTeam model, replace the default model manager with our custom manager by adding the following code to the FootballTeam model objects = FootballTeamManager().

 


Screen Shot 2017-04-10 at 11.49.55 AM.png

 

 

To integrate the new manager methods into our views, we simply have to modify the

get_queryset methods to use the following:

def get_queryset(self):
    return FootballTeam.objects.top_teams()

 

Now, when the requirement to change the definition of top football teams to be football teams both in the Premier League and the Championship, we only have to modify the top_teams method of the model manager, rather than the two separate views.

def top_teams(self, **kwargs):
    return self.filter(Q(league=FootballTeam.PREMIER_LEAGUE) |
        Q(league=FootballTeam.CHAMPIONSHIP))

 

Once this type of development is enforced into a django project, it means that any developer can come onto that project and have confidence that they can change the query in one place and do not have to have knowledge of all views in the application that use the query.

 

Conclusion

 

Use model managers! Once in the habit of writing and using model managers, you will find development time speeds up, you have a cleaner and maintainable codebase, less occurrences of bugs regarding queries and future developers on the project will be very appreciative of your work; even more so in bigger projects.

 


 

I hope you would be interested in our blogpost where my colleague discussed about Custom stockView as an easy way to lay out components.

 

custom_stack_view_an_easy_way_to_create_subviews-1.jpg

 

Learn an easy way to lay out Components

 

Share this article
Stephen Jefferson

Author Stephen Jefferson

Stephen is an iOS and Python/Django developer with 4 years of experience and graduated from Teesside University with a first class honours in Computer Science. He is passionate about software architecture, problem solving and Middlesbrough FC.

View more posts by this author

FREE REPORT

Retail in a post-PC world: A hedgehog lab report.

Understand trends in retail industry.

This white paper offers an in-depth review of the retail sector, looking at changing trends and strategies retail brands can adopt to survive and thrive amidst growing competition. Through our primary and secondary research, we provide valuable insights and guidance to retail brands facing challenges in this ever-evolving landscape.

•  Changing trends in the retail sector
•  Broader trends from primary and secondary research
•  Building better experiences to boost sales
•  Digital platforms leveraging the retail experience
•  The Road Ahead
Get your free report here