diff --git a/README.md b/README.md index 5c2eaae..fe85b86 100644 --- a/README.md +++ b/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) diff --git a/config/settings.py b/config/settings.py deleted file mode 100644 index 496e6a4..0000000 --- a/config/settings.py +++ /dev/null @@ -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' diff --git a/deployments/django-project/entrypoint.sh b/entrypoint.sh similarity index 100% rename from deployments/django-project/entrypoint.sh rename to entrypoint.sh