Fix project tree

This commit is contained in:
saqibur 2022-01-26 14:04:30 +06:00
parent 3f16a3f107
commit 9dfa5b7154
3 changed files with 99 additions and 336 deletions

312
README.md
View File

@ -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)

View File

@ -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^@2&#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'