Refactors You Need To Know for Your Django Project | by Rico Tadjudin | Apr, 2022

Photo by Chris Ried on Unsplash

** Note: This article is written for the purposes of an individual review for CSUI’s Software Engineering Project Course 2022. **

Refactoring is one of the most important practices you must do for all of your projects. Even when your projects are relatively small and simple, refactoring code is always encouraging and is the best practice for more readable, simple, and cleaner code. In this article, I’ll be showing you some things that can be refactored when you’re using Django (especially Django REST Framework) as your framework of choice! Let’s go!

You Need to Know These Refactors!
(Django Models) Custom Model Manager
(Django Models) Select Related and Prefetch Related
(Django REST Views) Class Based Views with Mixins and Generics
Conclusion

In these examples, I’ll be using these models that I’ve made for my Software Engineering Project Course 2022.

(Django Models) Custom Model Manager

According to Django Documentations, “Model Managers in Django is an interface through which database query operations are provided to Django models.”

You might not realize this, but you probably have used Django’s model manager when using Django Models. When you write Term.objects.all()the ‘objects’ in the middle of the default manager are provided to all models by Django.

Here’s where things get interesting: you can make your own custom model manager that can do other things convenient to you and your model.

For example, if I want to get all courses in a specific term (ie, term “2021/2022–1”), and I want them to be ordered by the course’s name, then normally we’d probably write this:

Course.objects.filter(term__name="2021/2022-1").order_by("name")

To be honest with you, there is no big problem with this, but if you’ll be using this exact filtering and ordering multiple times in your code, you’ll see that you’re going to repeat the code over and over again. This long filter and ordering can reduce the readability of your code. Things can improve if you use a custom model manager and QuerySet, like this for example:

Don’t forget to assign the new custom manager to the model. You can overwrite the default ‘objects’ manager or create a new custom manager with a different name. For this, I’ll be overwriting the default ‘objects’ manager.

With this, you can do the same thing as before by writing this:

Course.objects.get_by_term_name_with_ordered_name("2021/2022-1")

You could see that it’s more readable than the code before, and it’s simpler without repeating the .filter and .order_by. Sure, this example may not seem to improve the code much, but you can use custom managers for whatever you want and need. This can significantly improve your code readability and prevent you from repeating your code.

(Django Models) Select Related and Prefetch Related

You may not know yet about select_relatedand prefetch_related, but these functions provided by Django are really useful. In my opinion, a must-have for improved database query performance. This is because when you have already queried a bunch of objects to a QuerySet, and then you want to access its foreign key object’s attribute, Django will query the database again for every foreign key attribute of every object you’re calling.

Take a guess: if the code below is executed, how many queries does Django make to the database?

courses = Course.objects.filter(credit=4) #for example returns 2 objfor course in courses:
print(course.term.name)

You may think that it should be one, right? It should query the database when Course.objects.filter is executed? I’m sorry, but that answer is incorrect. The right answer is there will be three database query calls.

How? Obviously, the first query is on the filter. After that, because there are two Course objects in queryset, coursesand the code asks for a foreign key’s attribute, (course.term.name), then for every iteration of the for loop, Django will make an additional query call to the database.

This is because the initial Course.objects.filter does not have the information of the term’s name. Imagine if you’re doing 1,000 iterations or more. You’ll be querying the database 1,000 times for this alone.

To prevent this from happening, you can use select_relatedand prefetch_relatedto get those foreign key attributes on the initial filter query. You can search for the differences between the two, but in short, you use select_relatedwhen the object attribute is a single object (like OneToOneField or a ForeignKey), whereas you use prefetch_relatedwhen you’re going to get more than a single object or a set of things (ManyToManyFieldsor reverse ForeignKey‘s).

courses = Course.objects.filter(credit=4).select_related("term")for course in courses:
print(course.term.name)

By adding select_related("term")you will query all the information of the foreign key “term” of courses objects in the first query. Because of that, Django would not need to query the database for every iteration of the for loop.

This will significantly improve your database query performance (more like preventing massive amounts of database querying), and I personally think it makes your code more readable by explicitly telling readers you will need these attributes of a certain foreign key later.

(Django REST Views) Class-Based Views with Mixins and Generics

** Note: I’ll be discussing class-based views for the Django REST Framework. Class-based views for Django are a bit different, and you can see the Django documentation for more information **

Class-based views are probably already well known by many Django users. But in my opinion, you should use class-based views in moderation. For complex views, I think class-based views might lower readability (for those who are not fluent in class-based views) and lacks flexibility.

But for simple views, particularly REST views in Django REST Framework (DRF), you definitely should use class-based views with the provided mixins and generics as it’s significantly more simple, needs less code, and improves readability (for others who already know class -based views).

For example, with function-based views, I would write GetAndDeleteTermViews like this, keeping in mind that you should make the serializer for your model first for serializing the data. In this case, I’ve named mine TermSerializer:

There’s no big problem functionally with the above code. It should work fine as it’s supposed to. But you can see with more complex logic, it would get a lot messier. There would be a lot more lines of code, and/or a lot more ifs and elifs. This would lower the readability of your code. Here’s the same functionality with class-based views, mixins, and generics:

As you can see, it is a lot more simple and readable. One other advantage is all the errors that may happen (like 404 when the object does not exist) are already handled by Django. This will take less development time and improve readability for readers who have a basic understanding of class-based views.

I think these are essential refactors that you must consider in your Django projects. These are definitely not all the things that you need to refactor. You should search for more things that need refactoring, but I find a lot of people new to Django are not aware of things like model manager and select_related/prefetch_related in django.

All the effort you put in refactoring code will definitely pay off with a more clean, simple, readable, and improved code.

Leave a Comment