Mastering Django: Best Practices for Writing High-Quality Web Applications
Tags: django, webdev, productivity, testing
Published: 2023-04-05 10:10:29

[Django](https://docs.djangoproject.com/en/4.2/) is a popular web framework for building high-quality web applications. It provides a robust set of tools and features that enable developers to build web applications quickly and efficiently. 

However, building a high-quality web application requires more than just knowing the basics of Django. To build a robust, scalable, and maintainable application, developers need to follow best practices and use Django's features correctly. 

In this blog post, we'll explore four essential practices that will help you keep your project organized, secure, and production-ready. 

We'll cover how to create and update a `.gitignore` file, how to manage sensitive information using `.env` files, how to use the Django settings package to manage project settings, and finally, we'll share some standard practices to keep your project production-ready. Let's get started!

<br>

> **_NOTE:_**  To highlight these design and configuration changes, I have authored a repo `pratyzsh/django-best-practices`. Please feel free to raise a pull request and support the project

{% embed https://github.com/pratyzsh/django-best-practices %}

---

### 1. Create and update `.gitignore` file
We need .gitignore files to exclude certain files and directories from being tracked by Git. This helps to keep our repositories clean and avoids cluttering them with unnecessary files. Additionally, it improves repository performance and security by preventing sensitive or large files from being accidentally committed.

Some common files you should always aim to exclude from commit history using `.gitignore`:
1. Virtual Environment folder
2. `.env` files
3. `static/` folder
4. Database files, in Django, we have `db.sqlite3` database as default which needs to be excluded

For Django, you can use this [base template](https://github.com/jpadilla/django-project-template/blob/master/.gitignore) and extend it based on your project requirements.

---

### 2. Add `.env` files to manage sensitive information

`.env` files are used to securely store sensitive information such as passwords and API keys. They keep this information separate from source code and configuration files. Additionally, they allow for easy switching between different environments without modifying code.

In case of Django, we have a bunch of sensitive information to deal with, like `SECRET_KEY`, `DATABASE_NAME`, `DATABASE_PASS` and so on.

Here's an example how we can do this in Django.

- Create `.env` file in the same directory as your `settings.py` file

- The `.env` file should look like this:

```
DATABASE_NAME=postgres
DATABASE_USER=adam
...
```

- After storing the secrets in the above file, we can configure the `settings.py` file like this:

```
import environ

env = environ.Env()
# reading .env file
environ.Env.read_env()

SECRET_KEY = env("SECRET_KEY")

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': env("DATABASE_NAME"),
        'USER': env("DATABASE_USER"),
        'PASSWORD': env("DATABASE_PASSWORD"),
        'HOST': env("DATABASE_HOST"),
        'PORT': env("DATABASE_PORT"),
    }
}
```

Refer to the [repository](https://github.com/pratyzsh/django-best-practices) to understand the implementation details.

---
### 3. Create different `settings.py` for different deployment environments

When you're developing a Django project, you'll typically have different settings depending on the environment you're working in, such as development, testing, staging, and production.

Few key reasons:

1. Security: Production environments have different security requirements than development or testing environments. Using different settings files ensures that security measures like HTTPS and limited database permissions are properly implemented.

2. Performance: Production environments have different performance requirements than development or testing environments. Using different settings files ensures that performance measures like caching and load balancing are properly implemented.

3. Debugging: Debugging in production is different than debugging in development or testing. Different logging or error reporting tools might be needed, which can be specified in different settings files.

4. Customisation: Different environments might have different requirements or configurations that need to be customised, such as email or SMS providers. Using different settings files ensures that these Customisation are properly implemented for each environment.

To manage this, there are multiple approaches that you can take. I learnt a lot from [this article](https://djangostars.com/blog/configuring-django-settings-best-practices/) and the approach I borrowed is creating a separate `settings/` package and change the reference in `manage.py` file.
The final project structure should look like this:

```
\
+-- sampleproject
|   +-- settings.py #default settings file
|   +-- ...
+-- settings #package with base.py as the new default settings file
|   +-- __init__.py
|   +-- base.py 
|   +-- development.py
|   +-- production.py
```

Once you have created a separate folder and added your separate setting files, make changes in the `manage.py` file.

```
...
def main():
    """Run administrative tasks."""
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.base") 
'''
reference changed to settings/base.py in the above line
'''
...
```
Once you have made these changes, you can independently manage all environments by changing their respecting settings.

In order to run the application using `development.py` in the example mentioned above, either we can change the `manage.py` based on our project requirement but you also run this command to extend settings dynamically.
```
python manage.py runserver --settings=settings.development
```
This is a fast way to toggle between environments before initiating the deployment process.

Refer to the [repository](https://github.com/pratyzsh/django-best-practices) to understand the implementation details.

---

As a final note, one thing I have personally found to be very useful in ensuring these best practices are followed, is to ensure proper naming, formatting and linting of my code. 

You can check out my other blog, [Streamlining Your Python Workflow with Black, Ruff, GitHub Actions, and Pre-Commit Hooks](https://dev.to/pratyushcode/streamlining-your-python-workflow-with-black-ruff-github-actions-and-pre-commit-hooks-nk2) which is also used in the repository. Do check out the blog and repository. Like and share if you find this valuable.

Implement these and experience a blazing fast workflow ✨✨✨