mirror of
https://github.com/HackSoftware/Django-Styleguide.git
synced 2024-11-10 19:56:40 +03:00
Merge pull request #88 from HackSoftware/settings-structure
Section: Settings
This commit is contained in:
commit
ac2d344f8d
134
README.md
134
README.md
|
@ -37,6 +37,10 @@ Django styleguide that we use in [HackSoft](https://hacksoft.io).
|
||||||
* [Nested serializers](#nested-serializers)
|
* [Nested serializers](#nested-serializers)
|
||||||
* [Advanced serialization](#advanced-serialization)
|
* [Advanced serialization](#advanced-serialization)
|
||||||
- [Urls](#urls)
|
- [Urls](#urls)
|
||||||
|
- [Settings](#settings)
|
||||||
|
* [Prefixing environment variables with `DJANGO_`](#prefixing-environment-variables-with-django_)
|
||||||
|
* [Integrations](#integrations)
|
||||||
|
* [Reading from `.env`](#reading-from-env)
|
||||||
- [Errors & Exception Handling](#errors--exception-handling)
|
- [Errors & Exception Handling](#errors--exception-handling)
|
||||||
* [How exception handling works (in the context of DRF)](#how-exception-handling-works-in-the-context-of-drf)
|
* [How exception handling works (in the context of DRF)](#how-exception-handling-works-in-the-context-of-drf)
|
||||||
+ [DRF's `ValidationError`](#drfs-validationerror)
|
+ [DRF's `ValidationError`](#drfs-validationerror)
|
||||||
|
@ -1019,6 +1023,136 @@ urlpatterns = [
|
||||||
|
|
||||||
**Splitting urls like that can give you the extra flexibility to move separate domain patterns to separate modules**, especially for really big projects, where you'll often have merge conflicts in `urls.py`.
|
**Splitting urls like that can give you the extra flexibility to move separate domain patterns to separate modules**, especially for really big projects, where you'll often have merge conflicts in `urls.py`.
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
When it comes to Django settings, we tend to follow the folder structure from [`cookiecutter-django`](https://github.com/cookiecutter/cookiecutter-django), with few adjustments:
|
||||||
|
|
||||||
|
- We separate Django specific settings from other settings.
|
||||||
|
- Everything should be included in `base.py`.
|
||||||
|
- There should be nothing that's only included in `production.py`.
|
||||||
|
- Things that need to only work in production are controlled via environment variables.
|
||||||
|
|
||||||
|
Here's the folder structure of our [`Styleguide-Example`](https://github.com/HackSoftware/Styleguide-Example) project:
|
||||||
|
|
||||||
|
```
|
||||||
|
config
|
||||||
|
├── __init__.py
|
||||||
|
├── django
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ ├── base.py
|
||||||
|
│ ├── local.py
|
||||||
|
│ ├── production.py
|
||||||
|
│ └── test.py
|
||||||
|
├── settings
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ ├── celery.py
|
||||||
|
│ ├── cors.py
|
||||||
|
│ ├── sentry.py
|
||||||
|
│ └── sessions.py
|
||||||
|
├── urls.py
|
||||||
|
├── env.py
|
||||||
|
└── wsgi.py
|
||||||
|
├── asgi.py
|
||||||
|
```
|
||||||
|
|
||||||
|
In `config/django`, we put everything that's Django related:
|
||||||
|
|
||||||
|
- `base.py` contains most of the settings & imports everything else from `config/settings`
|
||||||
|
- `production.py` imports from `base.py` and then overwrites some specific settings for production.
|
||||||
|
- `test.py` imports from `base.py` and then overwrites some specific settings for running tests.
|
||||||
|
- This should be used as the settings module in `pytest.ini`.
|
||||||
|
- `local.py` imports from `base.py` and can overwrite some specific settings for local development.
|
||||||
|
- If you want to use that, point to `local` in `manage.py`. Otherwise stick with `base.py`
|
||||||
|
|
||||||
|
In `config/settings`, we put everything else:
|
||||||
|
|
||||||
|
- Celery configuration.
|
||||||
|
- 3rd party configurations.
|
||||||
|
- etc.
|
||||||
|
|
||||||
|
This gives you a nice separation of modules.
|
||||||
|
|
||||||
|
Additionally, we usually have `config/env.py` with the following code:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import environ
|
||||||
|
|
||||||
|
env = environ.Env()
|
||||||
|
```
|
||||||
|
|
||||||
|
And then, whenever we need to read something from the environment, we import like that:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from config.env import env
|
||||||
|
```
|
||||||
|
|
||||||
|
Usually, at the end of the `base.py` module, we import everything from `config/settings`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from config.settings.cors import * # noqa
|
||||||
|
from config.settings.sessions import * # noqa
|
||||||
|
from config.settings.celery import * # noqa
|
||||||
|
from config.settings.sentry import * # noqa
|
||||||
|
```
|
||||||
|
|
||||||
|
### Prefixing environment variables with `DJANGO_`
|
||||||
|
|
||||||
|
In a lot of examples, you'll see that environment variables are usually prefixed with `DJANGO_`. This is very helpful when there are other applications running alongside your Django app & reading from the same environment.
|
||||||
|
|
||||||
|
We tend to prefix with `DJANGO_` only `DJANGO_SETTINGS_MODULE` and `DJANGO_DEBUG` & not prefix anything else.
|
||||||
|
|
||||||
|
This is mostly up to personal preference. **Just make sure you are consistent with that.**
|
||||||
|
|
||||||
|
### Integrations
|
||||||
|
|
||||||
|
Since everything should be imported in `base.py`, but sometimes we don't want to configure a certain integration for local development, we derived the following approach:
|
||||||
|
|
||||||
|
- Integration-specific settings are placed in `config/settings/some_integration.py`
|
||||||
|
- There's always a boolean setting called `USE_SOME_INTEGRATION`, which reads from the environment & defaults to `False`.
|
||||||
|
- If the value is `True`, then proceed reading other settings & failing if things are not present in the environment.
|
||||||
|
|
||||||
|
For example, lets take a look at `config/settings/sentry.py`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from config.env import env
|
||||||
|
|
||||||
|
SENTRY_DSN = env('SENTRY_DSN', default='')
|
||||||
|
|
||||||
|
if SENTRY_DSN:
|
||||||
|
import sentry_sdk
|
||||||
|
from sentry_sdk.integrations.django import DjangoIntegration
|
||||||
|
from sentry_sdk.integrations.celery import CeleryIntegration
|
||||||
|
|
||||||
|
# ... we proceed with sentry settings here ...
|
||||||
|
# View the full file here - https://github.com/HackSoftware/Styleguide-Example/blob/master/config/settings/sentry.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reading from `.env`
|
||||||
|
|
||||||
|
Having a local `.env` is a nice way of providing values for your settings.
|
||||||
|
|
||||||
|
And the good thing is, [`django-environ`](https://django-environ.readthedocs.io/en/latest/) provides you with a way to do that:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# That's in the beginning of base.py
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from config.env import env, environ
|
||||||
|
|
||||||
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
|
BASE_DIR = environ.Path(__file__) - 3
|
||||||
|
|
||||||
|
env.read_env(os.path.join(BASE_DIR, ".env"))
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you can have a `.env` (but it's not required) file in your project root & place values for your settings there.
|
||||||
|
|
||||||
|
There are 2 things worth mentioning here:
|
||||||
|
|
||||||
|
1. Don't put `.env` in your source control, since this will leak credentials.
|
||||||
|
2. Rather put an `.env.example` with empty values for everything, so new developers can figure out what's being used.
|
||||||
|
|
||||||
## Errors & Exception Handling
|
## Errors & Exception Handling
|
||||||
|
|
||||||
> 👀 If you want the code, hop to the `Styleguide-Example` project - <https://github.com/HackSoftware/Styleguide-Example/blob/master/styleguide_example/api/exception_handlers.py>
|
> 👀 If you want the code, hop to the `Styleguide-Example` project - <https://github.com/HackSoftware/Styleguide-Example/blob/master/styleguide_example/api/exception_handlers.py>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user