Blog

The importance of Django Model Managers

Date

14th January 2020

Read

6 min

Creator

Stephen Jefferson

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.

[object Object]

Example:  

from django.db import modelsFootballTeam(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.                     

[object Object]

Consider the following example:    

FootballTeam Model

from django.db import models.DateFieldimport datetimeclass 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. 

[object Object]

Querying the FootballTeam Model  

API View:

from rest_framework.generics import ListAPIViewfrom .models import FootballTeamclass TopFootballTeamList(ListAPIView): def get_queryset(self): returnFootballTeam.objects.filter(league=FootballTeam.PREMIER_LEAGUE)

Django View:

from django.views import genericfrom .models import FootballTeamclass TopFootballTeamListView(generic.ListView): template_name = 'football/teams.html' model = FootballTeam def get_queryset(self): returnFootballTeam.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.

[object Object]

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 modelsfrom django.db.models import Qimport datetimeclass 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().

[object Object]

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.