app-generator / docs

App Generator - The Official Documentation | AppSeed
https://docs.appseed.us
1 stars 1 forks source link

Django Settings and Configuration #76

Open mahfujul-helios opened 6 months ago

mahfujul-helios commented 6 months ago

Django Settings and Configuration

In Django, settings and configuration play a crucial role in defining the behavior of your application. Here's a detailed overview:

Settings:

  1. Purpose: Settings in Django are a set of configuration variables that define how your application behaves. These variables control various aspects such as database connections, template paths, middleware, authentication, and much more.

  2. Settings File: Django applications typically have a settings file (often named settings.py) where these configuration variables are defined. This file is located in the project directory and is written in Python syntax.

  3. Customization: Django settings are highly customizable, allowing developers to tailor their application to specific requirements. You can modify existing settings or define custom settings as needed.

  4. Global Scope: Settings are global and apply to the entire Django project. Changing a setting affects the behavior of the entire application.

  5. Security Considerations: Some settings, such as SECRET_KEY and DEBUG, are critical for security. It's essential to keep sensitive information secure and avoid exposing it in version control or publicly accessible environments.

Configuration:

  1. Dynamic Configuration: Django settings can be dynamically configured based on various factors such as environment variables, command-line arguments, or custom logic. This allows for flexibility and adaptability across different deployment environments.

  2. Splitting Settings: In larger projects, settings can be split into multiple files for better organization and maintainability. This is often achieved using the split_settings library or by importing settings from separate modules.

  3. Default Settings: Django provides default settings for most configurations, allowing developers to get started quickly without having to define every setting manually. However, these defaults can be overridden as needed.

  4. Multiple Environments: Django settings can be configured for different environments such as development, testing, staging, and production. This ensures consistency and helps prevent errors when deploying applications to different environments.

  5. Third-Party Integration: Django settings can also be used to configure third-party packages and libraries used in your application. Many packages provide their own settings that can be customized to fit your specific use case.

  6. Documentation: It's essential to document your project's settings thoroughly to ensure clarity and maintainability. Documenting the purpose and usage of each setting helps developers understand how the application works and how to configure it properly.

Conclusion:

Understanding Django settings and configuration is essential for building and maintaining Django applications effectively. By properly configuring settings, you can tailor your application to meet specific requirements, ensure security, and adapt to different deployment environments. Proper documentation and best practices help maintain clarity and consistency across projects, making it easier for developers to collaborate and maintain the application over time.

app-generator commented 6 months ago

Django Settings and Configuration

Managing Django Settings: Best Practices

Introduction

Configuring Django settings effectively is crucial for every Django project. This guide addresses common issues encountered while managing Django settings and provides best practices to overcome them.

1. Different Environments

Approach:

Example:

# settings/local.py
from .base import *

DEBUG = True

Setting Django Configurations: Different Approaches

There is no built-in universal way to configure Django settings without hardcoding them. But books, open-source and work projects provide a lot of recommendations and approaches on how to do it best. Let’s take a brief look at the most popular ones to examine their weaknesses and strengths.

settings_local.py

This is the oldest method. I used it when I was configuring a Django project on a production server for the first time. I saw a lot of people use it back in the day, and I still see it now. The basic idea of this method is to extend all environment-specific settings in the settings_local.py file, which is ignored by VCS. Here’s an example: settings.py:

ALLOWED_HOSTS = ['example.com']
DEBUG = False
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'production_db',
        'USER': 'user',
        'PASSWORD': 'password',
        'HOST': 'db.example.com',
        'PORT': '5432',
        'OPTIONS': {
            'sslmode': 'require'
        }
    }
}

...

from .settings_local import *

settings_local.py file:

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

Pros:

Cons:

Separate settings file for each environment

This is an extension of the previous approach. It allows you to keep all configurations in version control systems (VCS) and to share default settings between developers.

In this case, there are multiple files from which projects on Django get settings, and you make a settings packagewith the following file structure:

init

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',
    }

To specify for a project you run which Django configuration file to use, you need to set an additional parameter:

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

Pros:

Cons:

Environment variables

To solve the issue with sensitive data, you can use environment variables in Django.

import os

SECRET_KEY = os.environ['SECRET_KEY']
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ['DATABASE_NAME'],
        'HOST': os.environ['DATABASE_HOST'],
        'PORT': int(os.environ['DATABASE_PORT']),
    }
}

This is the simplest example using Python os.environ and it has several issues:

To fix KeyError, you can write your own custom wrapper. For example:

import os

from django.core.exceptions import ImproperlyConfigured

def get_env_value(env_variable):
    try:
        return os.environ[env_variable]
    except KeyError:
        error_msg = 'Set the {} environment variable'.format(var_name)
        raise ImproperlyConfigured(error_msg)

SECRET_KEY = get_env_value('SECRET_KEY')
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': get_env_value('DATABASE_NAME'),
        'HOST': get_env_value('DATABASE_HOST'),
        'PORT': int(get_env_value('DATABASE_PORT')),
    }
}

Also, you can set default values for this wrapper and add type conversion. But actually there is no need to write this wrapper, because you can use a third-party library (we’ll talk about this later):

12 Factors

12 Factors is a collection of recommendations on how to build distributed web-apps that will be easy to deploy and scale in the Cloud. It was created by Heroku a well-known Cloud hosting provider. As the name suggests, the collection consists of twelve parts:

Each point describes a recommended way to implement a specific aspect of the project. Some of these points are covered by instruments like Django, Python, pip. Some are covered by design patterns or the infrastructure setup. In the context of this article, we are interested in one part: Configuration.

Its main rule is to store configuration in the environment. Following this recommendation will give us strict separation of config from code. You can read more on 12factor.net.

django-environ

Based on the above, we see that Django env variables are the perfect place to store settings. Now it’s time to talk about the toolkit.

Writing code using os.environcould be tricky sometimes and require additional effort to handle errors. It’s better to use django-environ instead.

Technically it’s a merge of:

This app gives a well-functioning API for reading values from environment variables or text files, handful type conversion, etc. Let’s look at some examples. Django settings.pyfile before:

import os

SITE_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))

DEBUG = True
TEMPLATE_DEBUG = DEBUG

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'production_db',
        'USER': 'user',
        'PASSWORD': 'password',
        'HOST': 'db.example.com',
        'PORT': '5432',
        'OPTIONS': {
            'sslmode': 'require'
        }
    }
}

MEDIA_ROOT = os.path.join(SITE_ROOT, 'assets')
MEDIA_URL = 'media/'
STATIC_ROOT = os.path.join(SITE_ROOT, 'static')
STATIC_URL = 'static/'

SECRET_KEY = 'Some-Autogenerated-Secret-Key'

CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': '127.0.0.1:6379/1',
    }
}

Settings.py in Django after:


import environ

root = environ.Path(__file__) - 3  # get root of the project
env = environ.Env()
environ.Env.read_env()  # reading .env file

SITE_ROOT = root()

DEBUG = env.bool('DEBUG', default=False)
TEMPLATE_DEBUG = DEBUG

DATABASES = {'default': env.db('DATABASE_URL')}

public_root = root.path('public/')
MEDIA_ROOT = public_root('media')
MEDIA_URL = env.str('MEDIA_URL', default='media/')
STATIC_ROOT = public_root('static')
STATIC_URL = env.str('STATIC_URL', default='static/')

SECRET_KEY = env.str('SECRET_KEY')

CACHES = {'default': env.cache('REDIS_CACHE_URL')}

.env file:

DEBUG=True
DATABASE_URL=postgres://user:password@db.example.com:5432/production_db?sslmode=require
REDIS_CACHE_URL=redis://user:password@cache.example.com:6379/1
SECRET_KEY=Some-Autogenerated-Secret-Key

Setting Structure

Instead of splitting settings by environments, you can split them by the source: Django, third- party apps (Celery, DRF, etc.), and your custom settings. File structure: setting

__init__.py file:

from .django import *       # All Django related settings
from .third_party import *  # Celery, Django REST Framework & other 3rd parties
from .project import *      # You custom settings

Each module could be done as a package, and you can split it more granularly: afters

Naming Conventions

Naming of variables is one of the most complex parts of development. So is naming of settings. We can’t imply on Django or third-party applications, but we can follow these simple rules for our custom (project) settings:

Bad example:

API_SYNC_CRONTAB = env.str('API_SYNC_CRONTAB')

Good example:

# Run job for getting new tweets.
# Accept string in crontab format. By default: every 30 minutes.
MYAWESOMEPROJECT_TWEETS_API_SYNC_CRONTAB = env.str(
    'MYAWESOMEPROJECT_TWEETS_API_SYNC_CRONTAB', default='30 * * * *'
)

Change MYAWESOMEPROJECT to you real project name.

Django Settings: Best practices

Here are some of the Django production settings best practices:

Conclusion

The Settings file is a small but very important part of any Django project. If you do it wrong, you’ll have a lot of issues during all phases of development. But if you do it right, it will be a good basis for your project that will allow it to grow and scale in the future.

Using the Django settings environment variables approach, you can easily switch from a monolith to microservice architecture, wrap your project in Docker containers, and deploy it in any VPS or Cloud hosting platform such as: Amazon, Google Cloud, or your own Kubernetes cluster.

Properly utilizing Django models methods can significantly enhance your application’s database interactions and data management.