Swipe left or right to navigate to next or previous post

Guide on Best practices on Django

05 Jan 2023 . category: Django . Comments
#Server #Django

 Guide on Best practices on Django - Tapan BK

Here are some of the best practices that needs to be followed on Django

1. Correct Model Naming

While naming the model, it is recommended to use the singular nouns like User, Address, Blog. If the single unit of the model does not contains information on the several objects, it is recommended to use a singular noun.

2. Relationship Field Naming

For relationships fields like ForeignKey, OneToOneKey, OneToMany, ManyToMany, it is recommended to use a name to specify a relationship.

Imagine there is a model called Article, - in which one of the relationships is ForeignKey for model User. If this field contains information about the author of the article, then author will be a more appropriate name than user.

3. Correct Related Name

In most of the cases, the plural of the model in plural will be correct.


    class Owner(models.Model):
        pass

    class Item(models.Model):
        owner = models.ForeignKey(Owner, related_name='items')

4. Order of Attributes and methods in a Model

Preferred attributes and methods order in a model

  • Constants - for choices
  • model fields
  • custom manager
  • meta information
  • def __unicode__ (for python2) or def __str__(for python3)
  • special methods
  • clean method
  • save method
  • get_absolute_url method
  • other methods

5. Use NullBooleanField for nullable Boolean field

It's always better to specify default values for BooleanField. If you want to keep the boolean field empty by default, It is better to use NullBooleanField.

6. Keep the Business logic in Models

It is better to allocate business logic of the business in models. The business logic can be allocate in models with the help of model methods and model manager.

7. Remove field duplication in ModelForm

Do not duplicate model fields in ModelForm or ModelSerializer without any need. If it is required to use all model fields, use MetaFields. If it is required to redefine a widget for a field with nothing changed in the field, use Meta widgets to indicate widgets.

8. Avoid use of ObjectDoesNotExist

ModelName.DoesNotExist is more specific exception than ObjectDoesNotExist which is a positive practice.

9. Avoid using multiple flags in a model

    class Article(models.Model):
        is_published = models.BooleanField(default=False)
        is_verified = models.BooleanField(default=False)
        …

Assume an application where the article has the status of draft, verified and published. Instead of using the combination of is_published and is_verified or similar flags, we can use the single status flag that tracks the status of the article.

  class Article(models.Model):
        STATUSES = Choices('new', 'verified', 'published')
        status = models.IntegerField(choices=STATUSES, default=STATUSES.draft)

10. Remove redundant model name in a field name

While creating a field in a model, it's not logical to add the model name. Like when we create a user status on the user model, we can just create a status field instead of redundant user_status field.

11. Never use len(queryset)

Do not use len method to get the no of objects returned by the query set. While using the len, the queryset to select all the objects in the database is run. The data returned by queryset is transformed into python Object and then finally run the length on that object.

Instead of using the len we can use the count method. It is similar to running COUNT method in SQL query. With count, an easier query is carried out in the database with fewer resources which improve the performance.

12. if queryset is a bad idea

Do not use a queryset as a boolean value. In django, queryset are lazy loaded. But while using a queryset as boolean, the actual inappropriate queryset is carried out. So, instead of using if queryset, we can do if queryset.exist() which actually use less resource.


13. Remove _id in ForeignKeyField and OneToOneField

Do not add _id suffix to ForeignKeyField and OneToOneField.

14. Use abstract Models to share the same logic between models

If you want to share the same logic between models, create an abstract models.

    
    class CreatedAtModel(models.Model):
        created_at = models.DateTimeField(auto_now_add=True)
    
        class Meta:
            abstract = True
    
    class Post(CreatedAtModel):
        ...

    class Comment(CreatedAtModel):
        ...

15. Use custom Manager and QuerySet

To keep all the business logic in one place, we can use the custom managers and QuerySet.

If you want the no of comments count for the posts, we can:

    
    class CustomManager(models.Manager):

        def with_comments_counter(self):
    
            return self.get_queryset().annotate(comments_count=Count('comment_set'))

    posts = Post.objects.with_comments_counter()
    posts[0].comments_count

16. Avoid writing fat views

We should write most of the logic in model itself which results in fat models, skinny views.

For example, while implementing a functionality of sending an email to user, it is better to extend the model with an email function instead of writing the logic in the view. This makes the code easier to unit test.

17. Django Templates

Templates can be placed at two places, either in the app directory itself or in the root directory. It is recommended to put the templates in the root directory. But if you want to reuse app in multiple places, then you should place in the app directory.


    #Good practice
    root_folder/
        my_app1/
        my_app2/
        my_app3/
        templates/
    
    #If you want to make your app reusable
    root_folder/
        my_app1/
            templates/
        my_app2/
            templates/
        my_app3/
            templates/

18. Separate settings file for each environment

Use the separate settings file for the different environment.

    settings/
   ├── __init__.py
   ├── base.py
   ├── ci.py
   ├── local.py
   ├── staging.py
   ├── production.py
   └── qa.py

settings/local.py

    from .base import *
    ALLOWED_HOSTS = ['localhost']
    DEBUG = True
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql',
            'NAME': 'local_db',
            'HOST': '127.0.0.1',
            'PORT': '5432',
        }
    }

We can specify which configuration to use with setting parameter

    python manage.py runserver --settings=settings.local

Tapan B.K. | Full Stack Software Engineer

Tapan B.K. is Full Stack Software Engineer. In his spare time, Tapan likes to watch movies, visit new places.