Django Advanced Tips You Must Know | django, django-decorators, tips, celery, on_delete

Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. It is blazing fast and a very matured framework with all the functionalities you will ever need to create a full fledged website. It’s out of the box admin interface is just amazing. Some of the well-known websites likes Pinterest, Instagram, Disqus, Bitbucket etc are built using django framework.

With extensive documentation and active community, you will never get stuck with anything. Although its very easy to get started with django, there are few things which most developers miss out at first and then they have to get their hands dirty to clean the mess which could have been avoided in the first place. Some of the tips from my own experience are listed below

Use select_related or prefetch_related

Django-querysets are lazy i.e. there won’t be any database hit until you evaluate the queryset. That also means that if you have foreign key relationships, the data will be fetched in multiple queries.

Lets take a use case when you have to query the table with foreign keys and also need the data referenced via foreign key. Assuming the following models definitions

Class MyUser(models.Model):
    email = models.EmailField(unique=True)
    name = models.CharField(max_length=32)

Class Order(models.Model):
    orderNumber = models.PositiveIntegerField()
    myuser = models.ForeignKey(MyUser)

# Hits the database
order = Order.objects.get(orderNumber=124)

# hits the database again
myuser = order.myuser

To optimize the db hits, we can use ‘select_related‘ modifier which follows the foreign key relationships and fetches the result in a single complex query. You can refer any Foreign Key or a list of foreign keys and pass it to select_related(). Below is the example illustrating the effect.

# Hits the database
order = Order.objects.select_related('myuser').get(orderNumber=124)

# doesn't hit the database as value is already fetched in the previous query
myuser = order.myuser

Apart from select_related, there is a similar method prefetch_related(). Both these methods have similar purpose but the strategy to retrieve data is different.

select_related performs a sql join while prefetch_related does a separate lookup which allows it to prefetch many-to-many and many-to-one objects, which cannot be done using select_related. prefetch_related can also be used with generic relations and generic foreign keys. You can read more about them here

Make Sure to set models.on_delete

While we usually define the foreign key relationships as listed below

Class MyUser(models.Model):
   email = models.EmailField(unique=True)
   name = models.CharField(max_length=32)

Class Order(models.Model):
   orderNumber = models.PositiveIntegerField()
   myuser = models.ForeignKey(MyUser)

There is an optional argument on_delete which can become a nightmare if ignored. This argument specifies the action required should the referenced foreign key is deleted from the database. The possible values of on_delete are CASCADEPROTECTSET_NULL, SET_DEFAULT and SET() with the default value as CASCADE.

Considering the above model definitions, let’s say you delete a myuser row which is also referenced in some orders. Although you expect django to throw a foreign key constraint error like database does, this is not the default behavior of django. Apparently django will cascade the delete action and all the order rows which refer this myuser will also get deleted.

Having found this out the hard way after getting some irregularities in my database, I strongly suggest to set this argument with the option that best suits you. Here is the link for more information on this.

Use View Decorators

Decorators are an excellent way to restrict access to a particular set of users. You must have encountered @login_required decorator which ensures that a view is accessible for logged in users only. Decorators are nothing but callables returning other callable and its a very powerful tool.

You may encounter situations where you may have to write your own decorator. For example, you have a user group “managers” and you want to give them access to some additional views. Here’s how to achieve this.

from django.contrib.auth.decorators import user_passes_test
from django.contrib.auth.models import Group

def user_is_manager(user):
   group = Group.objects.get(name='Managers')
   return user.is_authenticated() and group in user.groups.all()

@user_passes_test(user_is_manager)
def restrictedView():
    .....

In this example, I have imported user_passes_test for illustration purpose but  you can also define a decorator on your own. I hope you get the idea.

Use Celery for asynchronous processing

In the initial stages of development, you may not require to do asynchronous processing. But as your project goes bigger, you will find it a necessity to put some processes for background processing. Any process which isn’t required to be executed synchronously can be send to a queue and eventually be consumed by a worker. Celery provides an excellent way to achieve all of this. A very common example is sending emails, sms etc. You can use redis or rabbit-mq as a broker. Celery will also help you schedule and execute cron jobs by setting up periodic tasks. There can be significant improvement in your website speed using this method.

There are lots of other important things as well like

  • Using django-debug-toolbar to improve database queries
  • Utilize django’s caching framework
  • Use named urls, reverse and url template tag
  • Use supervisor to monitor your processes

Let me know if you have any suggestions/feedback in the comments section below.

Fun FactGame of thrones season 6 is back, and its episode 4 is also titled as the book of stranger 🙂

Structured Data, Review
Title: Django Advanced Tips You Must Know
Reviewed by Anurag
Rating: 5.0
Summary: Django advance Tips | django-decorators, celery, on_delete
Description: Although its very easy to get started with django, there are few things which most developers miss out at first and then they to get their hands dirty to clean the mess which could have been avoided in the first place

Come on, I know you want to say it