Fix project tree
This commit is contained in:
parent
3f16a3f107
commit
9dfa5b7154
312
README.md
312
README.md
|
@ -3,224 +3,110 @@ This is a template/project structure for developing django-based applications -
|
|||
either strictly through the `django-rest-framework` or just `django`.
|
||||
|
||||
The project is meant to be easily clonable, and used as the starter template for
|
||||
the next big thing your team develops.
|
||||
the next big thing our team develops.
|
||||
|
||||
First, we'll define the scope of today's discussion.
|
||||
## Project Tree
|
||||
```bash
|
||||
.
|
||||
├── apps
|
||||
│ ├── app
|
||||
│ │ ├── api
|
||||
│ │ │ ├── v1
|
||||
│ │ │ │ ├── __init__.py
|
||||
│ │ │ │ ├── serializers.py
|
||||
│ │ │ │ ├── services.py
|
||||
│ │ │ │ ├── tests.py
|
||||
│ │ │ │ ├── urls.py
|
||||
│ │ │ │ └── views.py
|
||||
│ │ │ ├── v2
|
||||
│ │ │ │ ├── __init__.py
|
||||
│ │ │ │ ├── serializers.py
|
||||
│ │ │ │ ├── services.py
|
||||
│ │ │ │ ├── tests.py
|
||||
│ │ │ │ ├── urls.py
|
||||
│ │ │ │ └── views.py
|
||||
│ │ │ └── __init__.py
|
||||
│ │ ├── management
|
||||
│ │ │ ├── commands.py
|
||||
│ │ │ └── __init__.py
|
||||
│ │ ├── migrations
|
||||
│ │ │ └── __init__.py
|
||||
│ │ ├── templates
|
||||
│ │ ├── tests
|
||||
│ │ ├── admin.py
|
||||
│ │ ├── apps.py
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── models.py
|
||||
│ │ ├── urls.py
|
||||
│ │ ├── utils.py
|
||||
│ │ └── views.py
|
||||
│ └── second_app
|
||||
│ ├── migrations
|
||||
│ │ └── __init__.py
|
||||
│ ├── templates
|
||||
│ ├── admin.py
|
||||
│ ├── apps.py
|
||||
│ ├── __init__.py
|
||||
│ ├── models.py
|
||||
│ ├── service.py
|
||||
│ ├── tests.py
|
||||
│ └── views.py
|
||||
├── config
|
||||
│ ├── settings
|
||||
│ │ ├── base.py
|
||||
│ │ ├── development.py
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── local.py
|
||||
│ │ ├── local_template.py
|
||||
│ │ └── production.py
|
||||
│ ├── asgi.py
|
||||
│ ├── __init__.py
|
||||
│ ├── urls.py
|
||||
│ └── wsgi.py
|
||||
├── deployments
|
||||
│ ├── django-project
|
||||
│ │ └── Dockerfile
|
||||
│ ├── nginx
|
||||
│ │ ├── default.conf
|
||||
│ │ └── Dockerfile
|
||||
│ └── docker-compose.yml
|
||||
├── docs
|
||||
│ ├── CHANGELOG.md
|
||||
│ ├── CONTRIBUTING.md
|
||||
│ ├── deployment.md
|
||||
│ ├── local-development.md
|
||||
│ └── swagger.yaml
|
||||
├── requirements
|
||||
│ ├── common.txt
|
||||
│ ├── development.txt
|
||||
│ ├── local.txt
|
||||
│ └── production.txt
|
||||
├── static
|
||||
├── entrypoint.sh
|
||||
├── manage.py
|
||||
├── pytest.ini
|
||||
└── README.md
|
||||
|
||||
We're here to talk about a long-standing project structure - not best practices. We'll deal with those later.
|
||||
|
||||
However, a lot of these decisions we're based on some accepted best practices. I'll be listing all my reference material at the presentation.
|
||||
|
||||
Additionally, I will not be taking questions or discussions today. Instead I ask that everyone takes detailed notes, and raises issues in the GitHub repository, and offers their contributions there. While I understand this adds extra steps for suggestions, it also serves as a place to concretely discuss improvements and offer detailed suggestions and examples in written form instead of verbal assumptions.
|
||||
|
||||
|
||||
|
||||
## Debugging and Tooling
|
||||
* Add Silk
|
||||
* Add Django Debug Toolbar
|
||||
|
||||
|
||||
## Coding Style:
|
||||
* https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/
|
||||
|
||||
## Instructions:
|
||||
* Should we add a note for when we add new imports?
|
||||
|
||||
# Design Principles
|
||||
* Each application should be designed in a way to be pluggable - dragged and
|
||||
dropped into any other project and it'll still work independently.
|
||||
|
||||
# Code Checking
|
||||
* We'll use mypy for static type checking
|
||||
|
||||
# Code Formatting
|
||||
* We'll use black as our auto-formatter
|
||||
|
||||
# Testing
|
||||
* We'll use pytest for testing
|
||||
|
||||
Disclaimer: I don't have 10 years of experience, nor do I have access to people
|
||||
with 10 years of experience. What I do have is good reference material - books,
|
||||
conferences, and documentation. These people are smarter than me, they are better
|
||||
developers and they have more experience - I'm somewhat collecting and presenting
|
||||
what they do.
|
||||
|
||||
|
||||
Mani na
|
||||
|
||||
# Starting
|
||||
Defining the scope of our projects
|
||||
We need a project structure that is;
|
||||
- Homogeneous across strativ applications
|
||||
- Can be used to build Django Rest APIs and also support Django Templates
|
||||
- We're limiting ourselves strictly to Django because there are developer
|
||||
expectations - for instance, if you're a Django developer, you'll expect the
|
||||
project to have a settings.py, a manage.py, etc.
|
||||
|
||||
What this allows -
|
||||
When anyone visits this project, they are provided with a high-level view of the
|
||||
project. We've found that this allows us to work easily with other developers
|
||||
and even non-developers.
|
||||
|
||||
|
||||
Please observe the following:
|
||||
➤ We like to place all our API components into a package within an app called api/.
|
||||
That allows us to isolate our API components in a consistent location. If we were to
|
||||
put it in the root of our app, then we would end up with a huge list of API-specific
|
||||
modules in the general area of the app.
|
||||
➤ Viewsets belong in their own module.
|
||||
➤ We always place routers in urls.py. Either at the app or project level, routers belong
|
||||
in urls.py.
|
||||
|
||||
|
||||
REST Framework Decisions:
|
||||
For projects with a lot of small, interconnecting apps, it can be hard to hunt
|
||||
down where a particular API view lives. In contrast to placing all API code
|
||||
within each relevant app, sometimes it makes more sense to build an app
|
||||
specifically for the API. This is where all the serializers, renderers, and views
|
||||
are placed. Therefore, the name of the app should reflect its API version (see
|
||||
Section 17.3.7: Version Your API).
|
||||
|
||||
For example, we might place all our views, serializers, and other API
|
||||
components in an app titled apiv4. The downside is the possibility for the API
|
||||
app to become too large and disconnected from the apps that power it. Hence we
|
||||
consider an alternative in the next subsection.
|
||||
|
||||
|
||||
# Zen of Python
|
||||
```
|
||||
Beautiful is better than ugly.
|
||||
Explicit is better than implicit.
|
||||
Simple is better than complex.
|
||||
Complex is better than complicated.
|
||||
Flat is better than nested.
|
||||
Sparse is better than dense.
|
||||
Readability counts.
|
||||
Special cases aren't special enough to break the rules.
|
||||
Although practicality beats purity.
|
||||
Errors should never pass silently.
|
||||
Unless explicitly silenced.
|
||||
In the face of ambiguity, refuse the temptation to guess.
|
||||
There should be one-- and preferably only one --obvious way to do it.
|
||||
Although that way may not be obvious at first unless you're Dutch.
|
||||
Now is better than never.
|
||||
Although never is often better than *right* now.
|
||||
If the implementation is hard to explain, it's a bad idea.
|
||||
If the implementation is easy to explain, it may be a good idea.
|
||||
Namespaces are one honking great idea -- let's do more of those!
|
||||
```
|
||||
|
||||
## Git Ignore
|
||||
|
||||
https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
|
||||
|
||||
Git Ignore:
|
||||
We're gonna follow ->
|
||||
https://github.com/github/gitignore/blob/main/Python.gitignore
|
||||
|
||||
With or without the nuclear option.
|
||||
https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
## Rationale
|
||||
### `apps`
|
||||
* A mother-folder containing all apps for our project.
|
||||
* An app can be a django template project, or an api
|
||||
|
||||
|
||||
### `config`
|
||||
* Contains project configuration files, including the primary URL file
|
||||
* Contains settings split into `base`, `local`, `production` and `development`
|
||||
|
||||
|
||||
### `deployments`
|
||||
* Contains Docker and nginx specific files for deploying in different
|
||||
environments
|
||||
|
||||
## Tree
|
||||
```
|
||||
│ .gitignore
|
||||
│ Dockerfile
|
||||
│ entrypoint.sh
|
||||
│ manage.py
|
||||
│ nginx.conf
|
||||
│ README.md
|
||||
│
|
||||
├───app
|
||||
│ │ admin.py
|
||||
│ │ apps.py
|
||||
│ │ models.py
|
||||
│ │ urls.py
|
||||
│ │ utils.py
|
||||
│ │ __init__.py
|
||||
│ │
|
||||
│ ├───api
|
||||
│ │ │ __init__.py
|
||||
│ │ │
|
||||
│ │ ├───v1
|
||||
│ │ │ serializers.py
|
||||
│ │ │ tests.py
|
||||
│ │ │ urls.py
|
||||
│ │ │ views.py
|
||||
│ │ │ viewsets.py
|
||||
│ │ │ __init__.py
|
||||
│ │ │
|
||||
│ │ └───v2
|
||||
│ │ serializers.py
|
||||
│ │ tests.py
|
||||
│ │ urls.py
|
||||
│ │ views.py
|
||||
│ │ viewsets.py
|
||||
│ │ __init__.py
|
||||
│ │
|
||||
│ ├───management
|
||||
│ │ commands.py
|
||||
│ │ __init__.py
|
||||
│ │
|
||||
│ ├───migrations
|
||||
│ │ __init__.py
|
||||
│ │
|
||||
│ └───templates
|
||||
├───config
|
||||
│ │ asgi.py
|
||||
│ │ settings.py
|
||||
│ │ urls.py
|
||||
│ │ wsgi.py
|
||||
│ │ __init__.py
|
||||
│ │
|
||||
│ └───settings
|
||||
│ base.py
|
||||
│ development.py
|
||||
│ local.py
|
||||
│ local_template.py
|
||||
│ production.py
|
||||
│
|
||||
├───core
|
||||
│ │ admin.py
|
||||
│ │ apps.py
|
||||
│ │ models.py
|
||||
│ │ tests.py
|
||||
│ │ views.py
|
||||
│ │ __init__.py
|
||||
│ │
|
||||
│ └───migrations
|
||||
│ __init__.py
|
||||
│
|
||||
├───docs
|
||||
│ CHANGELOG.md
|
||||
│ CONTRIBUTING.md
|
||||
│ LOCAL_DEVELOPMENT.md
|
||||
│ PRODUCTION_DEPLOYMENT.md
|
||||
│ swagger.yaml
|
||||
│
|
||||
├───requirements
|
||||
│ common.txt
|
||||
│ development.txt
|
||||
│ local.txt
|
||||
│ production.txt
|
||||
│
|
||||
├───static
|
||||
└───templates
|
||||
```
|
||||
|
||||
## References
|
||||
Two Scoops of Django by Daniel and Audrey Feldroy
|
||||
|
||||
Where to Write Business Logic:
|
||||
* https://stackoverflow.com/questions/57387711/django-rest-framework-where-to-write-complex-logic-in-serializer-py-or-views-py
|
||||
|
||||
Django Best Practices:
|
||||
* https://django-best-practices.readthedocs.io/en/latest/index.html
|
||||
|
||||
Reference:
|
||||
* https://github.com/cookiecutter/cookiecutter-django
|
||||
|
||||
|
||||
## TODO
|
||||
* Add versioning for swagger documentation - https://editor.swagger.io/
|
||||
- [Two Scoops of Django by Daniel and Audrey Feldroy](https://www.feldroy.com/books/two-scoops-of-django-3-x)
|
||||
- [Django Best Practices](https://django-best-practices.readthedocs.io/en/latest/index.html)
|
||||
- [Cookiecutter Django](https://github.com/cookiecutter/cookiecutter-django)
|
||||
- [HackSoft Django Style Guide](https://github.com/HackSoftware/Django-Styleguide)
|
||||
- [Radoslav Georgiev - Django Structure for Scale and Longevity](https://www.youtube.com/watch?v=yG3ZdxBb1oo)
|
||||
|
|
|
@ -1,123 +0,0 @@
|
|||
"""
|
||||
Django settings for config project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 4.0.1.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/4.0/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/4.0/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-$227hjjmuq2e!)o^@2v#+(-=@$v362o@8g#s9!2)tjn1)1a'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'config.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'config.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/4.0/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/4.0/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
Loading…
Reference in New Issue