Merge branch 'master' into email-only

This commit is contained in:
Andrew Chen Wang 2023-03-10 05:35:47 -05:00 committed by GitHub
commit deb1c1e8b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
79 changed files with 1473 additions and 295 deletions

View File

@ -1302,5 +1302,85 @@
"name": "Abe Hanoka",
"github_login": "abe-101",
"twitter_username": "abe__101"
},
{
"name": "Adin Hodovic",
"github_login": "adinhodovic",
"twitter_username": ""
},
{
"name": "Leifur Halldor Asgeirsson",
"github_login": "leifurhauks",
"twitter_username": ""
},
{
"name": "David",
"github_login": "buckldav",
"twitter_username": ""
},
{
"name": "rguptar",
"github_login": "rguptar",
"twitter_username": ""
},
{
"name": "Omer-5",
"github_login": "Omer-5",
"twitter_username": ""
},
{
"name": "TAKAHASHI Shuuji",
"github_login": "shuuji3",
"twitter_username": ""
},
{
"name": "Thomas Booij",
"github_login": "ThomasBooij95",
"twitter_username": ""
},
{
"name": "Pamela Fox",
"github_login": "pamelafox",
"twitter_username": "pamelafox"
},
{
"name": "Robin",
"github_login": "Kaffeetasse",
"twitter_username": ""
},
{
"name": "Patrick Tran",
"github_login": "theptrk",
"twitter_username": ""
},
{
"name": "tildebox",
"github_login": "tildebox",
"twitter_username": ""
},
{
"name": "duffn",
"github_login": "duffn",
"twitter_username": ""
},
{
"name": "Delphine LEMIRE",
"github_login": "DelphineLemire",
"twitter_username": ""
},
{
"name": "Hoai-Thu Vuong",
"github_login": "thuvh",
"twitter_username": ""
},
{
"name": "Arkadiusz Michał Ryś",
"github_login": "arrys",
"twitter_username": ""
},
{
"name": "mpsantos",
"github_login": "mpsantos",
"twitter_username": ""
}
]

View File

@ -18,3 +18,56 @@ updates:
interval: "daily"
labels:
- "update"
# Enable version updates for Docker
# We need to specify each Dockerfile in a separate entry because Dependabot doesn't
# support wildcards or recursively checking subdirectories. Check this issue for updates:
# https://github.com/dependabot/dependabot-core/issues/2178
- package-ecosystem: "docker"
directory: "{{cookiecutter.project_slug}}/compose/local/django/"
schedule:
interval: "daily"
labels:
- "update"
- package-ecosystem: "docker"
directory: "{{cookiecutter.project_slug}}/compose/local/docs/"
schedule:
interval: "daily"
labels:
- "update"
- package-ecosystem: "docker"
directory: "{{cookiecutter.project_slug}}/compose/local/node/"
schedule:
interval: "daily"
labels:
- "update"
- package-ecosystem: "docker"
directory: "{{cookiecutter.project_slug}}/compose/production/aws/"
schedule:
interval: "daily"
labels:
- "update"
- package-ecosystem: "docker"
directory: "{{cookiecutter.project_slug}}/compose/production/django/"
schedule:
interval: "daily"
labels:
- "update"
- package-ecosystem: "docker"
directory: "{{cookiecutter.project_slug}}/compose/production/postgres/"
schedule:
interval: "daily"
labels:
- "update"
- package-ecosystem: "docker"
directory: "{{cookiecutter.project_slug}}/compose/production/traefik/"
schedule:
interval: "daily"
labels:
- "update"

View File

@ -2,6 +2,7 @@ name: CI
on:
push:
branches: [ "master", "main" ]
pull_request:
concurrency:
@ -9,17 +10,6 @@ concurrency:
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.10"
cache: pip
- name: Run pre-commit
uses: pre-commit/action@v3.0.0
tests:
strategy:
fail-fast: false
@ -29,7 +19,7 @@ jobs:
- windows-latest
- macOS-latest
name: "Run tests"
name: "pytest ${{ matrix.os }}"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
@ -49,10 +39,14 @@ jobs:
script:
- name: Basic
args: ""
- name: Extended
args: "use_celery=y use_drf=y frontend_pipeline=Gulp"
- name: Celery & DRF
args: "use_celery=y use_drf=y"
- name: Gulp
args: "frontend_pipeline=Gulp"
- name: Webpack
args: "frontend_pipeline=Webpack"
name: "${{ matrix.script.name }} Docker"
name: "Docker ${{ matrix.script.name }}"
runs-on: ubuntu-latest
env:
DOCKER_BUILDKIT: 1
@ -74,12 +68,14 @@ jobs:
fail-fast: false
matrix:
script:
- name: With Celery
- name: Celery
args: "use_celery=y frontend_pipeline='Django Compressor'"
- name: With Gulp
args: "frontend_pipeline='Gulp'"
- name: Gulp
args: "frontend_pipeline=Gulp"
- name: Webpack
args: "frontend_pipeline=Webpack"
name: "${{ matrix.script.name }} Bare metal"
name: "Bare metal ${{ matrix.script.name }}"
runs-on: ubuntu-latest
services:
redis:

View File

@ -29,9 +29,11 @@ jobs:
pip install -r requirements.txt
- name: Update list
run: python scripts/update_contributors.py
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v4.15.3
uses: stefanzweifel/git-auto-commit-action@v4.16.0
with:
commit_message: Update Contributors
file_pattern: CONTRIBUTORS.md .github/contributors.json

View File

@ -3,30 +3,30 @@ default_stages: [commit]
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: check-yaml
- repo: https://github.com/asottile/pyupgrade
rev: v3.2.2
rev: v3.3.1
hooks:
- id: pyupgrade
args: [--py310-plus]
exclude: hooks/
- repo: https://github.com/psf/black
rev: 22.10.0
rev: 23.1.0
hooks:
- id: black
- repo: https://github.com/PyCQA/isort
rev: 5.10.1
rev: 5.12.0
hooks:
- id: isort
- repo: https://github.com/PyCQA/flake8
rev: 5.0.4
rev: 6.0.0
hooks:
- id: flake8

View File

@ -3,6 +3,412 @@ All enhancements and patches to Cookiecutter Django will be documented in this f
<!-- GENERATOR_PLACEHOLDER -->
## 2023.03.09
### Fixed
- Fix the omit configuration for coverage ([#4201](https://github.com/cookiecutter/cookiecutter-django/pull/4201))
### Updated
- Update ipdb to 0.13.13 ([#4202](https://github.com/cookiecutter/cookiecutter-django/pull/4202))
## 2023.03.07
### Updated
- Update mypy to 1.1.1 ([#4196](https://github.com/cookiecutter/cookiecutter-django/pull/4196))
- Update django-environ to 0.10.0 ([#4195](https://github.com/cookiecutter/cookiecutter-django/pull/4195))
## 2023.03.04
### Changed
- Add option to serve media files locally using nginx ([#2457](https://github.com/cookiecutter/cookiecutter-django/pull/2457))
### Documentation
- Include contributing page to the docs ([#4144](https://github.com/cookiecutter/cookiecutter-django/pull/4144))
### Updated
- Update myst-parser to 0.19.1 ([#4193](https://github.com/cookiecutter/cookiecutter-django/pull/4193))
- Update pytest to 7.2.2 ([#4191](https://github.com/cookiecutter/cookiecutter-django/pull/4191))
- Update drf-spectacular to 0.26.0 ([#4192](https://github.com/cookiecutter/cookiecutter-django/pull/4192))
## 2023.02.28
### Updated
- Update pre-commit to 3.1.1 ([#4188](https://github.com/cookiecutter/cookiecutter-django/pull/4188))
## 2023.02.27
### Updated
- Update sentry-sdk to 1.16.0 ([#4187](https://github.com/cookiecutter/cookiecutter-django/pull/4187))
## 2023.02.26
### Changed
- Fix readthedocs config file for generated project ([#4172](https://github.com/cookiecutter/cookiecutter-django/pull/4172))
### Updated
- Bump traefik from v2.2.11 to 2.9.8 ([#4164](https://github.com/cookiecutter/cookiecutter-django/pull/4164))
- Update coverage to 7.2.1 ([#4186](https://github.com/cookiecutter/cookiecutter-django/pull/4186))
## 2023.02.25
### Changed
- Run linting with pre-commit on GitLab ([#4150](https://github.com/cookiecutter/cookiecutter-django/pull/4150))
### Fixed
- Disable caching for linter job on GitHub actions ([#4166](https://github.com/cookiecutter/cookiecutter-django/pull/4166))
### Documentation
- Add instuction to run celery beat ([#4162](https://github.com/cookiecutter/cookiecutter-django/pull/4162))
### Updated
- Bump garland/aws-cli-docker from 1.15.47 to 1.16.140 ([#4136](https://github.com/cookiecutter/cookiecutter-django/pull/4136))
- Update djangorestframework-stubs to 1.9.1 ([#4184](https://github.com/cookiecutter/cookiecutter-django/pull/4184))
- Update whitenoise to 6.4.0 ([#4180](https://github.com/cookiecutter/cookiecutter-django/pull/4180))
- Update django-stubs to 1.15.0 ([#4183](https://github.com/cookiecutter/cookiecutter-django/pull/4183))
- Update django-crispy-forms to 2.0 ([#4158](https://github.com/cookiecutter/cookiecutter-django/pull/4158))
- Update django-cors-headers to 3.14.0 ([#4181](https://github.com/cookiecutter/cookiecutter-django/pull/4181))
- Update python-slugify to 8.0.1 ([#4178](https://github.com/cookiecutter/cookiecutter-django/pull/4178))
- Update pre-commit to 3.1.0 ([#4176](https://github.com/cookiecutter/cookiecutter-django/pull/4176))
- Update mypy to 1.0.1 ([#4168](https://github.com/cookiecutter/cookiecutter-django/pull/4168))
- Update werkzeug to 2.2.3 ([#4160](https://github.com/cookiecutter/cookiecutter-django/pull/4160))
- Update coverage to 7.2.0 ([#4177](https://github.com/cookiecutter/cookiecutter-django/pull/4177))
- Update django to 4.0.10 ([#4159](https://github.com/cookiecutter/cookiecutter-django/pull/4159))
- Update hiredis to 2.2.2 ([#4156](https://github.com/cookiecutter/cookiecutter-django/pull/4156))
## 2023.02.17
### Changed
- Update version of github actions on the template project ([#4167](https://github.com/cookiecutter/cookiecutter-django/pull/4167))
## 2023.02.09
### Changed
- Remove unused pip cache paths in GHA &amp; add a note for pre-commit.ci ([#4151](https://github.com/cookiecutter/cookiecutter-django/pull/4151))
### Updated
- Update mypy to 0.991 ([#4106](https://github.com/cookiecutter/cookiecutter-django/pull/4106))
## 2023.02.08
### Updated
- Update sphinx to 6.1.3 ([#4148](https://github.com/cookiecutter/cookiecutter-django/pull/4148))
- Update redis to 4.5.1 ([#4147](https://github.com/cookiecutter/cookiecutter-django/pull/4147))
## 2023.02.07
### Updated
- Bump postcss-preset-env from 7.8.3 to 8.0.1 ([#4115](https://github.com/cookiecutter/cookiecutter-django/pull/4115))
- Bump sass-loader from 12.6.0 to 13.2.0 ([#4116](https://github.com/cookiecutter/cookiecutter-django/pull/4116))
- Bump babel-loader from 8.3.0 to 9.1.2 ([#4117](https://github.com/cookiecutter/cookiecutter-django/pull/4117))
- Bump postcss-loader from 6.2.1 to 7.0.2 ([#4114](https://github.com/cookiecutter/cookiecutter-django/pull/4114))
- Bump webpack-cli from 4.10.0 to 5.0.1 ([#4118](https://github.com/cookiecutter/cookiecutter-django/pull/4118))
- Update redis to 4.5.0 ([#4142](https://github.com/cookiecutter/cookiecutter-django/pull/4142))
- Update sentry-sdk to 1.15.0 ([#4141](https://github.com/cookiecutter/cookiecutter-django/pull/4141))
## 2023.02.06
### Changed
- Change `RequestFactory` to `APIRequestFactory` in tests for API views ([#4110](https://github.com/cookiecutter/cookiecutter-django/pull/4110))
### Fixed
- Fix django-webpack-loader setup when running tests ([#4128](https://github.com/cookiecutter/cookiecutter-django/pull/4128))
### Documentation
- Added AWS ECS Full Deployment Article to README ([#2630](https://github.com/cookiecutter/cookiecutter-django/pull/2630))
### Updated
- Update hiredis to 2.2.1 ([#4123](https://github.com/cookiecutter/cookiecutter-django/pull/4123))
- Update tox to 4.4.4 ([#4133](https://github.com/cookiecutter/cookiecutter-django/pull/4133))
- Update django to 4.0.9 ([#4134](https://github.com/cookiecutter/cookiecutter-django/pull/4134))
- Update django-webpack-loader to 1.8.1 ([#4132](https://github.com/cookiecutter/cookiecutter-django/pull/4132))
## 2023.02.05
### Documentation
- Add note about which service to request when running locally with Docker &amp; Webpack or Gulp ([#4130](https://github.com/cookiecutter/cookiecutter-django/pull/4130))
## 2023.02.03
### Updated
- Update pre-commit to 3.0.4 ([#4127](https://github.com/cookiecutter/cookiecutter-django/pull/4127))
## 2023.02.02
### Updated
- Update python-slugify to 8.0.0 ([#4111](https://github.com/cookiecutter/cookiecutter-django/pull/4111))
- Update pre-commit to 3.0.3 ([#4121](https://github.com/cookiecutter/cookiecutter-django/pull/4121))
- Update black to 23.1.0 ([#4120](https://github.com/cookiecutter/cookiecutter-django/pull/4120))
- Update black pre-commit hook ([#4122](https://github.com/cookiecutter/cookiecutter-django/pull/4122))
## 2023.01.29
### Changed
- Add Webpack support ([#3623](https://github.com/cookiecutter/cookiecutter-django/pull/3623))
- Remove `BrokenLinkEmailsMiddleware` ([#4112](https://github.com/cookiecutter/cookiecutter-django/pull/4112))
## 2023.01.28
### Changed
- Refactor `merge_production_dotenvs_in_dotenv.py` ([#4105](https://github.com/cookiecutter/cookiecutter-django/pull/4105))
### Updated
- Update isort to 5.12.0 ([#4109](https://github.com/cookiecutter/cookiecutter-django/pull/4109))
- Auto-update pre-commit hooks ([#4108](https://github.com/cookiecutter/cookiecutter-django/pull/4108))
## 2023.01.27
### Updated
- Update django-stubs to 1.14.0 ([#4103](https://github.com/cookiecutter/cookiecutter-django/pull/4103))
## 2023.01.26
### Changed
- Rename BASE_DIR_PATH to BASE_DIR ([#4102](https://github.com/cookiecutter/cookiecutter-django/pull/4102))
### Updated
- Update pre-commit to 3.0.1 ([#4104](https://github.com/cookiecutter/cookiecutter-django/pull/4104))
- Update tox to 4.4.2 ([#4101](https://github.com/cookiecutter/cookiecutter-django/pull/4101))
## 2023.01.25
### Changed
- Rename ROOT_DIR to BASE_DIR ([#4086](https://github.com/cookiecutter/cookiecutter-django/pull/4086))
- Update postgres and redis to point to mini tiers ([#4099](https://github.com/cookiecutter/cookiecutter-django/pull/4099))
### Updated
- Update coverage to 7.1.0 ([#4100](https://github.com/cookiecutter/cookiecutter-django/pull/4100))
## 2023.01.24
### Updated
- Update pre-commit to 3.0.0 ([#4098](https://github.com/cookiecutter/cookiecutter-django/pull/4098))
## 2023.01.23
### Updated
- Update sentry-sdk to 1.14.0 ([#4096](https://github.com/cookiecutter/cookiecutter-django/pull/4096))
## 2023.01.22
### Updated
- Update django-compressor to 4.3.1 ([#4094](https://github.com/cookiecutter/cookiecutter-django/pull/4094))
## 2023.01.21
### Updated
- Update django-stubs to 1.13.2 ([#4093](https://github.com/cookiecutter/cookiecutter-django/pull/4093))
## 2023.01.19
### Fixed
- Add sourcemaps support to Gulp ([#4089](https://github.com/cookiecutter/cookiecutter-django/pull/4089))
### Updated
- Update coverage to 7.0.5 ([#4092](https://github.com/cookiecutter/cookiecutter-django/pull/4092))
- Update redis to 4.4.2 ([#4091](https://github.com/cookiecutter/cookiecutter-django/pull/4091))
- Update requests to 2.28.2 ([#4090](https://github.com/cookiecutter/cookiecutter-django/pull/4090))
- Update tox to 4.3.5 ([#4087](https://github.com/cookiecutter/cookiecutter-django/pull/4087))
## 2023.01.17
### Updated
- Update tox to 4.3.3 ([#4081](https://github.com/cookiecutter/cookiecutter-django/pull/4081))
## 2023.01.15
### Updated
- Update pytest to 7.2.1 ([#4077](https://github.com/cookiecutter/cookiecutter-django/pull/4077))
- Update pytz to 2022.7.1 ([#4078](https://github.com/cookiecutter/cookiecutter-django/pull/4078))
## 2023.01.12
### Updated
- Update sentry-sdk to 1.13.0 ([#4074](https://github.com/cookiecutter/cookiecutter-django/pull/4074))
## 2023.01.11
### Changed
- Update Celery instructions in the documentation ([#4061](https://github.com/cookiecutter/cookiecutter-django/pull/4061))
### Updated
- Update tox to 4.2.7 ([#4073](https://github.com/cookiecutter/cookiecutter-django/pull/4073))
## 2023.01.10
### Changed
- Add dump.rdb to gitignore ([#4062](https://github.com/cookiecutter/cookiecutter-django/pull/4062))
### Fixed
- Exclude `.venv` from code style checks ([#4069](https://github.com/cookiecutter/cookiecutter-django/pull/4069))
### Updated
- Update hiredis to 2.1.1 ([#4070](https://github.com/cookiecutter/cookiecutter-django/pull/4070))
## 2023.01.08
### Updated
- Update redis to 4.4.1 ([#4068](https://github.com/cookiecutter/cookiecutter-django/pull/4068))
- Update coverage to 7.0.4 ([#4067](https://github.com/cookiecutter/cookiecutter-django/pull/4067))
## 2023.01.07
### Updated
- Update tox to 4.2.6 ([#4064](https://github.com/cookiecutter/cookiecutter-django/pull/4064))
- Update django-storages to 1.13.2 ([#4057](https://github.com/cookiecutter/cookiecutter-django/pull/4057))
- Update isort to 5.11.4 ([#4058](https://github.com/cookiecutter/cookiecutter-django/pull/4058))
- Update rcssmin to 1.1.1 ([#4060](https://github.com/cookiecutter/cookiecutter-django/pull/4060))
- Update django-compressor to 4.3 ([#4063](https://github.com/cookiecutter/cookiecutter-django/pull/4063))
## 2023.01.06
### Changed
- Add `.git` to `.dockerignore` ([#4054](https://github.com/cookiecutter/cookiecutter-django/pull/4054))
- Fix link and add non-Docker commands to testing page in the docs ([#4036](https://github.com/cookiecutter/cookiecutter-django/pull/4036))
### Updated
- Update tox to 4.2.3 ([#4051](https://github.com/cookiecutter/cookiecutter-django/pull/4051))
## 2023.01.04
### Changed
- Fix typo on test settings ([#4049](https://github.com/cookiecutter/cookiecutter-django/pull/4049))
### Updated
- Update tox to 4.2.2 ([#4050](https://github.com/cookiecutter/cookiecutter-django/pull/4050))
- Update tox to 4.2.1 ([#4046](https://github.com/cookiecutter/cookiecutter-django/pull/4046))
- Update coverage to 7.0.3 ([#4047](https://github.com/cookiecutter/cookiecutter-django/pull/4047))
## 2023.01.03
### Updated
- Update flake8-isort to 6.0.0 ([#4022](https://github.com/cookiecutter/cookiecutter-django/pull/4022))
- Update tox to 4.1.3 ([#4041](https://github.com/cookiecutter/cookiecutter-django/pull/4041))
- Update pillow to 9.4.0 ([#4040](https://github.com/cookiecutter/cookiecutter-django/pull/4040))
- Update gitpython to 3.1.30 ([#4032](https://github.com/cookiecutter/cookiecutter-django/pull/4032))
- Update coverage to 7.0.2 ([#4042](https://github.com/cookiecutter/cookiecutter-django/pull/4042))
- Update whitenoise to 6.3.0 ([#4044](https://github.com/cookiecutter/cookiecutter-django/pull/4044))
## 2022.12.29
### Updated
- Update tox to 4.1.0 ([#4035](https://github.com/cookiecutter/cookiecutter-django/pull/4035))
- Update tox to 4.0.19 ([#4030](https://github.com/cookiecutter/cookiecutter-django/pull/4030))
- Update django-allauth to 0.52.0 ([#4033](https://github.com/cookiecutter/cookiecutter-django/pull/4033))
## 2022.12.26
### Updated
- Update tox to 4.0.17 ([#4027](https://github.com/cookiecutter/cookiecutter-django/pull/4027))
- Update pre-commit to 2.21.0 ([#4026](https://github.com/cookiecutter/cookiecutter-django/pull/4026))
## 2022.12.25
### Updated
- Auto-update pre-commit hooks ([#4021](https://github.com/cookiecutter/cookiecutter-django/pull/4021))
## 2022.12.24
### Updated
- Update coverage to 7.0.1 ([#4024](https://github.com/cookiecutter/cookiecutter-django/pull/4024))
## 2022.12.21
### Changed
- Retry when trying to store a Celery result in backend ([#3996](https://github.com/cookiecutter/cookiecutter-django/pull/3996))
- Update image URL for build status shield badge ([#4018](https://github.com/cookiecutter/cookiecutter-django/pull/4018))
### Updated
- Update pytz to 2022.7 ([#4020](https://github.com/cookiecutter/cookiecutter-django/pull/4020))
- Update ipdb to 0.13.11 ([#4019](https://github.com/cookiecutter/cookiecutter-django/pull/4019))
- Update tox to 4.0.16 ([#4017](https://github.com/cookiecutter/cookiecutter-django/pull/4017))
- Update sentry-sdk to 1.12.1 ([#4014](https://github.com/cookiecutter/cookiecutter-django/pull/4014))
- Update coverage to 7.0.0 ([#4013](https://github.com/cookiecutter/cookiecutter-django/pull/4013))
- Update django-anymail to 9.0 ([#4012](https://github.com/cookiecutter/cookiecutter-django/pull/4012))
- Auto-update pre-commit hooks ([#4005](https://github.com/cookiecutter/cookiecutter-django/pull/4005))
- Update isort to 5.11.3 ([#4010](https://github.com/cookiecutter/cookiecutter-django/pull/4010))
- Update drf-spectacular to 0.25.1 ([#4009](https://github.com/cookiecutter/cookiecutter-django/pull/4009))
- Update hiredis to 2.1.0 ([#4006](https://github.com/cookiecutter/cookiecutter-django/pull/4006))
## 2022.12.13
### Changed
- Improve documentation for Getting started with Docker ([#4003](https://github.com/cookiecutter/cookiecutter-django/pull/4003))
### Updated
- Update isort to 5.11.1 ([#3999](https://github.com/cookiecutter/cookiecutter-django/pull/3999))
- Auto-update pre-commit hooks ([#3998](https://github.com/cookiecutter/cookiecutter-django/pull/3998))
- Update isort to 5.11.0 ([#3997](https://github.com/cookiecutter/cookiecutter-django/pull/3997))
## 2022.12.10
### Updated
- Update tox to 4.0.5 ([#3993](https://github.com/cookiecutter/cookiecutter-django/pull/3993))
- Auto-update pre-commit hooks ([#3991](https://github.com/cookiecutter/cookiecutter-django/pull/3991))
## 2022.12.09
### Changed
- Remove bind option mounts for docker compose volumes ([#3981](https://github.com/cookiecutter/cookiecutter-django/pull/3981))
### Updated
- Update djangorestframework-stubs to 1.8.0 ([#3990](https://github.com/cookiecutter/cookiecutter-django/pull/3990))
- Update black to 22.12.0 ([#3988](https://github.com/cookiecutter/cookiecutter-django/pull/3988))
## 2022.12.08
### Updated
- Update tox to 4.0.3 ([#3987](https://github.com/cookiecutter/cookiecutter-django/pull/3987))
- Update tox to 4.0.2 ([#3985](https://github.com/cookiecutter/cookiecutter-django/pull/3985))
- Update django-stubs to 1.13.1 ([#3986](https://github.com/cookiecutter/cookiecutter-django/pull/3986))
## 2022.12.07
### Updated
- Auto-update pre-commit hooks ([#3983](https://github.com/cookiecutter/cookiecutter-django/pull/3983))
## 2022.12.06
### Changed
- Simplify production `DATABASES` setting to extend base definition ([#3969](https://github.com/cookiecutter/cookiecutter-django/pull/3969))
### Fixed
- Only set `SERVERS` for `drf-spectacular` in production ([#3609](https://github.com/cookiecutter/cookiecutter-django/pull/3609))
### Updated
- Update django-coverage-plugin to 3.0.0 ([#3979](https://github.com/cookiecutter/cookiecutter-django/pull/3979))
- Bump stefanzweifel/git-auto-commit-action from 4.15.4 to 4.16.0 ([#3978](https://github.com/cookiecutter/cookiecutter-django/pull/3978))
## 2022.12.04
### Updated
- Update redis to 4.4.0 ([#3977](https://github.com/cookiecutter/cookiecutter-django/pull/3977))
- Update django-debug-toolbar to 3.8.1 ([#3976](https://github.com/cookiecutter/cookiecutter-django/pull/3976))
## 2022.12.03
### Updated
- Auto-update pre-commit hooks ([#3975](https://github.com/cookiecutter/cookiecutter-django/pull/3975))
## 2022.12.02
### Updated
- Update flake8 to 6.0.0 ([#3974](https://github.com/cookiecutter/cookiecutter-django/pull/3974))
## 2022.11.30
### Changed
- Add Azure Storage as an option to serve static and media files ([#3967](https://github.com/cookiecutter/cookiecutter-django/pull/3967))
### Updated
- Auto-update pre-commit hooks ([#3970](https://github.com/cookiecutter/cookiecutter-django/pull/3970))
## 2022.11.26
### Changed
- Fix typo in flower start for watching celery ([#3966](https://github.com/cookiecutter/cookiecutter-django/pull/3966))
## 2022.11.24
### Updated
- Auto-update pre-commit hooks ([#3963](https://github.com/cookiecutter/cookiecutter-django/pull/3963))
## 2022.11.23
### Changed
- Fix graceful shutdown of local dev containers and use watchfiles for beat + flower ([#3925](https://github.com/cookiecutter/cookiecutter-django/pull/3925))
- feat(celery): Enable sending the sent task event by default ([#3961](https://github.com/cookiecutter/cookiecutter-django/pull/3961))
### Updated
- Bump stefanzweifel/git-auto-commit-action from 4.15.3 to 4.15.4 ([#3940](https://github.com/cookiecutter/cookiecutter-django/pull/3940))
- Update django-model-utils to 4.3.1 ([#3948](https://github.com/cookiecutter/cookiecutter-django/pull/3948))
- Update flake8-isort to 5.0.3 ([#3952](https://github.com/cookiecutter/cookiecutter-django/pull/3952))
## 2022.11.22
### Changed
- Remove USE_L10N due to deprecation ([#3960](https://github.com/cookiecutter/cookiecutter-django/pull/3960))
- Remove platform from compose file ([#3957](https://github.com/cookiecutter/cookiecutter-django/pull/3957))
- feat(celery): Send task events for Celery by default ([#3959](https://github.com/cookiecutter/cookiecutter-django/pull/3959))
### Updated
- Update python-slugify to 7.0.0 ([#3950](https://github.com/cookiecutter/cookiecutter-django/pull/3950))
- Update redis to 4.3.5 ([#3954](https://github.com/cookiecutter/cookiecutter-django/pull/3954))
- Update sentry-sdk to 1.11.1 ([#3955](https://github.com/cookiecutter/cookiecutter-django/pull/3955))
- Update uvicorn to 0.20.0 ([#3953](https://github.com/cookiecutter/cookiecutter-django/pull/3953))
- Update tox to 3.27.1 ([#3945](https://github.com/cookiecutter/cookiecutter-django/pull/3945))
## 2022.11.11
### Updated

View File

@ -166,6 +166,13 @@ Listed in alphabetical order.
</td>
<td></td>
</tr>
<tr>
<td>Adin Hodovic</td>
<td>
<a href="https://github.com/adinhodovic">adinhodovic</a>
</td>
<td></td>
</tr>
<tr>
<td>Agam Dua</td>
<td>
@ -285,6 +292,13 @@ Listed in alphabetical order.
</td>
<td></td>
</tr>
<tr>
<td>Arkadiusz Michał Ryś</td>
<td>
<a href="https://github.com/arrys">arrys</a>
</td>
<td></td>
</tr>
<tr>
<td>Arnav Choudhury</td>
<td>
@ -600,6 +614,13 @@ Listed in alphabetical order.
</td>
<td></td>
</tr>
<tr>
<td>David</td>
<td>
<a href="https://github.com/buckldav">buckldav</a>
</td>
<td></td>
</tr>
<tr>
<td>David Díaz</td>
<td>
@ -628,6 +649,13 @@ Listed in alphabetical order.
</td>
<td>jangeador</td>
</tr>
<tr>
<td>Delphine LEMIRE</td>
<td>
<a href="https://github.com/DelphineLemire">DelphineLemire</a>
</td>
<td></td>
</tr>
<tr>
<td>Demetris Stavrou</td>
<td>
@ -691,6 +719,13 @@ Listed in alphabetical order.
</td>
<td>dudanogueira</td>
</tr>
<tr>
<td>duffn</td>
<td>
<a href="https://github.com/duffn">duffn</a>
</td>
<td></td>
</tr>
<tr>
<td>Dónal Adams</td>
<td>
@ -887,6 +922,13 @@ Listed in alphabetical order.
</td>
<td></td>
</tr>
<tr>
<td>Hoai-Thu Vuong</td>
<td>
<a href="https://github.com/thuvh">thuvh</a>
</td>
<td></td>
</tr>
<tr>
<td>Howie Zhao</td>
<td>
@ -1153,6 +1195,13 @@ Listed in alphabetical order.
</td>
<td></td>
</tr>
<tr>
<td>Leifur Halldor Asgeirsson</td>
<td>
<a href="https://github.com/leifurhauks">leifurhauks</a>
</td>
<td></td>
</tr>
<tr>
<td>Leo won</td>
<td>
@ -1391,6 +1440,13 @@ Listed in alphabetical order.
</td>
<td></td>
</tr>
<tr>
<td>mpsantos</td>
<td>
<a href="https://github.com/mpsantos">mpsantos</a>
</td>
<td></td>
</tr>
<tr>
<td>Naveen</td>
<td>
@ -1426,6 +1482,13 @@ Listed in alphabetical order.
</td>
<td></td>
</tr>
<tr>
<td>Omer-5</td>
<td>
<a href="https://github.com/Omer-5">Omer-5</a>
</td>
<td></td>
</tr>
<tr>
<td>Pablo</td>
<td>
@ -1433,6 +1496,13 @@ Listed in alphabetical order.
</td>
<td></td>
</tr>
<tr>
<td>Pamela Fox</td>
<td>
<a href="https://github.com/pamelafox">pamelafox</a>
</td>
<td>pamelafox</td>
</tr>
<tr>
<td>Parbhat Puri</td>
<td>
@ -1440,6 +1510,13 @@ Listed in alphabetical order.
</td>
<td></td>
</tr>
<tr>
<td>Patrick Tran</td>
<td>
<a href="https://github.com/theptrk">theptrk</a>
</td>
<td></td>
</tr>
<tr>
<td>Pawan Chaurasia</td>
<td>
@ -1524,6 +1601,13 @@ Listed in alphabetical order.
</td>
<td></td>
</tr>
<tr>
<td>rguptar</td>
<td>
<a href="https://github.com/rguptar">rguptar</a>
</td>
<td></td>
</tr>
<tr>
<td>Richard Hajdu</td>
<td>
@ -1531,6 +1615,13 @@ Listed in alphabetical order.
</td>
<td></td>
</tr>
<tr>
<td>Robin</td>
<td>
<a href="https://github.com/Kaffeetasse">Kaffeetasse</a>
</td>
<td></td>
</tr>
<tr>
<td>Roman Afanaskin</td>
<td>
@ -1636,6 +1727,13 @@ Listed in alphabetical order.
</td>
<td></td>
</tr>
<tr>
<td>TAKAHASHI Shuuji</td>
<td>
<a href="https://github.com/shuuji3">shuuji3</a>
</td>
<td></td>
</tr>
<tr>
<td>Tames McTigue</td>
<td>
@ -1664,6 +1762,13 @@ Listed in alphabetical order.
</td>
<td>thibault</td>
</tr>
<tr>
<td>Thomas Booij</td>
<td>
<a href="https://github.com/ThomasBooij95">ThomasBooij95</a>
</td>
<td></td>
</tr>
<tr>
<td>Théo Segonds</td>
<td>
@ -1671,6 +1776,13 @@ Listed in alphabetical order.
</td>
<td></td>
</tr>
<tr>
<td>tildebox</td>
<td>
<a href="https://github.com/tildebox">tildebox</a>
</td>
<td></td>
</tr>
<tr>
<td>Tim Claessens</td>
<td>

View File

@ -1,11 +1,13 @@
# Cookiecutter Django
[![Build Status](https://img.shields.io/github/workflow/status/cookiecutter/cookiecutter-django/CI/master)](https://github.com/cookiecutter/cookiecutter-django/actions?query=workflow%3ACI)
[![Build Status](https://img.shields.io/github/actions/workflow/status/cookiecutter/cookiecutter-django/ci.yml?branch=master)](https://github.com/cookiecutter/cookiecutter-django/actions/workflows/ci.yml?query=branch%3Amaster)
[![Documentation Status](https://readthedocs.org/projects/cookiecutter-django/badge/?version=latest)](https://cookiecutter-django.readthedocs.io/en/latest/?badge=latest)
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/cookiecutter/cookiecutter-django/master.svg)](https://results.pre-commit.ci/latest/github/cookiecutter/cookiecutter-django/master)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
[![Updates](https://pyup.io/repos/github/cookiecutter/cookiecutter-django/shield.svg)](https://pyup.io/repos/github/cookiecutter/cookiecutter-django/)
[![Join our Discord](https://img.shields.io/badge/Discord-cookiecutter-5865F2?style=flat&logo=discord&logoColor=white)](https://discord.gg/uFXweDQc5a)
[![Code Helpers Badge](https://www.codetriage.com/cookiecutter/cookiecutter-django/badges/users.svg)](https://www.codetriage.com/cookiecutter/cookiecutter-django)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
Powered by [Cookiecutter](https://github.com/cookiecutter/cookiecutter), Cookiecutter Django is a framework for jumpstarting
production-ready Django projects quickly.
@ -27,9 +29,9 @@ production-ready Django projects quickly.
- Registration via [django-allauth](https://github.com/pennersr/django-allauth)
- Comes with custom user model ready to go
- Optional basic ASGI setup for Websockets
- Optional custom static build using Gulp and livereload
- Optional custom static build using Gulp or Webpack
- Send emails via [Anymail](https://github.com/anymail/django-anymail) (using [Mailgun](http://www.mailgun.com/) by default or Amazon SES if AWS is selected cloud provider, but switchable)
- Media storage using Amazon S3 or Google Cloud Storage
- Media storage using Amazon S3, Google Cloud Storage, Azure Storage or nginx
- Docker support using [docker-compose](https://github.com/docker/compose) for development and production (using [Traefik](https://traefik.io/) with [LetsEncrypt](https://letsencrypt.org/) support)
- [Procfile](https://devcenter.heroku.com/articles/procfile) for deploying to Heroku
- Instructions for deploying to [PythonAnywhere](https://www.pythonanywhere.com/)
@ -41,7 +43,7 @@ production-ready Django projects quickly.
*These features can be enabled during initial project setup.*
- Serve static files from Amazon S3, Google Cloud Storage or [Whitenoise](https://whitenoise.readthedocs.io/)
- Serve static files from Amazon S3, Google Cloud Storage, Azure Storage or [Whitenoise](https://whitenoise.readthedocs.io/)
- Configuration for [Celery](https://docs.celeryq.dev) and [Flower](https://github.com/mher/flower) (the latter in Docker setup only)
- Integration with [MailHog](https://github.com/mailhog/MailHog) for local email testing
- Integration with [Sentry](https://sentry.io/welcome/) for error logging
@ -153,6 +155,7 @@ Answer the prompts with your own desired [options](http://cookiecutter-django.re
1 - None
2 - Django Compressor
3 - Gulp
4 - Webpack
Choose from 1, 2, 3, 4 [1]: 1
use_celery [n]: y
use_mailhog [n]: n
@ -237,6 +240,7 @@ experience better.
## Articles
- [Cookiecutter Django With Amazon RDS](https://haseeburrehman.com/posts/cookiecutter-django-with-amazon-rds/) - Apr, 2, 2021
- [Complete Walkthrough: Blue/Green Deployment to AWS ECS using GitHub actions](https://github.com/Andrew-Chen-Wang/cookiecutter-django-ecs-github) - June 10, 2020
- [Using cookiecutter-django with Google Cloud Storage](https://ahhda.github.io/cloud/gce/django/2019/03/12/using-django-cookiecutter-cloud-storage.html) - Mar. 12, 2019
- [cookiecutter-django with Nginx, Route 53 and ELB](https://msaizar.com/blog/cookiecutter-django-nginx-route-53-and-elb/) - Feb. 12, 2018
- [cookiecutter-django and Amazon RDS](https://msaizar.com/blog/cookiecutter-django-and-amazon-rds/) - Feb. 7, 2018

View File

@ -28,6 +28,7 @@
"cloud_provider": [
"AWS",
"GCP",
"Azure",
"None"
],
"mail_service": [
@ -46,7 +47,8 @@
"frontend_pipeline": [
"None",
"Django Compressor",
"Gulp"
"Gulp",
"Webpack"
],
"use_celery": "n",
"use_mailhog": "n",

View File

@ -23,13 +23,13 @@ now = datetime.now()
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = []
extensions = ["myst_parser"]
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
# The suffix of source filenames.
source_suffix = ".rst"
source_suffix = [".rst", ".md"]
# The encoding of source files.
# source_encoding = 'utf-8-sig'

3
docs/contributing.md Normal file
View File

@ -0,0 +1,3 @@
```{include} ../CONTRIBUTING.md
```

View File

@ -12,13 +12,13 @@ Run these commands to deploy the project to Heroku:
heroku create --buildpack heroku/python
heroku addons:create heroku-postgresql:hobby-dev
heroku addons:create heroku-postgresql:mini
# On Windows use double quotes for the time zone, e.g.
# heroku pg:backups schedule --at "02:00 America/Los_Angeles" DATABASE_URL
heroku pg:backups schedule --at '02:00 America/Los_Angeles' DATABASE_URL
heroku pg:promote DATABASE_URL
heroku addons:create heroku-redis:hobby-dev
heroku addons:create heroku-redis:mini
# Assuming you chose Mailgun as mail service (see below for others)
heroku addons:create mailgun:starter
@ -109,10 +109,10 @@ Or add the DSN for your account, if you already have one:
.. _Sentry add-on: https://elements.heroku.com/addons/sentry
Gulp & Bootstrap compilation
++++++++++++++++++++++++++++
Gulp or Webpack
+++++++++++++++
If you've opted for Gulp, you'll most likely need to setup
If you've opted for Gulp or Webpack as frontend pipeline, you'll most likely need to setup
your app to use `multiple buildpacks`_: one for Python & one for Node.js:
.. code-block:: bash
@ -121,8 +121,8 @@ your app to use `multiple buildpacks`_: one for Python & one for Node.js:
At time of writing, this should do the trick: during deployment,
the Heroku should run ``npm install`` and then ``npm build``,
which runs Gulp in cookiecutter-django.
which run the SASS compilation & JS bundling.
If things don't work, please refer to the Heroku docs.
.. _multiple buildpacks: https://devcenter.heroku.com/articles/using-multiple-buildpacks-for-an-app
.. _multiple buildpacks: https://devcenter.heroku.com/articles/using-multiple-buildpacks-for-an-app

View File

@ -84,6 +84,32 @@ You can read more about this feature and how to configure it, at `Automatic HTTP
.. _Automatic HTTPS: https://docs.traefik.io/https/acme/
.. _webpack-whitenoise-limitation:
Webpack without Whitenoise limitation
-------------------------------------
If you opt for Webpack without Whitenoise, Webpack needs to know the static URL at build time, when running ``docker-compose build`` (See ``webpack/prod.config.js``). Depending on your setup, this URL may come from the following environment variables:
- ``AWS_STORAGE_BUCKET_NAME``
- ``DJANGO_AWS_S3_CUSTOM_DOMAIN``
- ``DJANGO_GCP_STORAGE_BUCKET_NAME``
- ``DJANGO_AZURE_CONTAINER_NAME``
The Django settings are getting these values at runtime via the ``.envs/.production/.django`` file , but Docker does not read this file at build time, it only look for a ``.env`` in the root of the project. Failing to pass the values correctly will result in a page without CSS styles nor javascript.
To solve this, you can either:
1. merge all the env files into ``.env`` by running::
merge_production_dotenvs_in_dotenv.py
2. create a ``.env`` file in the root of the project with just variables you need. You'll need to also define them in ``.envs/.production/.django`` (hence duplicating them).
3. set these variables when running the build command::
DJANGO_AWS_S3_CUSTOM_DOMAIN=example.com docker-compose -f production.yml build``.
None of these options are ideal, we're open to suggestions on how to improve this. If you think you have one, please open an issue or a pull request.
(Optional) Postgres Data Volume Modifications
---------------------------------------------
@ -161,3 +187,7 @@ For status check, run::
supervisorctl status
Media files without cloud provider
----------------------------------
If you chose no cloud provider and Docker, the media files will be served by an nginx service, from a ``production_django_media`` volume. Make sure to keep this around to avoid losing any media files.

View File

@ -3,9 +3,6 @@ Getting Up and Running Locally With Docker
.. index:: Docker
The steps below will get you up and running with a local development environment.
All of these commands assume you are in the root of your generated project.
.. note::
If you're new to Docker, please be aware that some resources are cached system-wide
@ -19,10 +16,16 @@ Prerequisites
* Docker; if you don't have it yet, follow the `installation instructions`_;
* Docker Compose; refer to the official documentation for the `installation guide`_.
* Pre-commit; refer to the official documentation for the `pre-commit`_.
* Cookiecutter; refer to the official GitHub repository of `Cookiecutter`_
.. _`installation instructions`: https://docs.docker.com/install/#supported-platforms
.. _`installation guide`: https://docs.docker.com/compose/install/
.. _`pre-commit`: https://pre-commit.com/#install
.. _`Cookiecutter`: https://github.com/cookiecutter/cookiecutter
Before Getting Started
----------------------
.. include:: generate-project-block.rst
Build the Stack
---------------
@ -210,6 +213,11 @@ By default, it's enabled both in local and production environments (``local.yml`
.. _`Flower`: https://github.com/mher/flower
Using Webpack or Gulp
~~~~~~~~~~~~~~~~~~~~~
When using Webpack or Gulp as the ``frontend_pipeline`` option, you should access your application at the address of the ``node`` service in order to see your correct styles. This is http://localhost:3000 by default. When using any of the other ``frontend_pipeline`` options, you should use the address of the ``django`` service, http://localhost:8000.
Developing locally with HTTPS
-----------------------------

View File

@ -24,9 +24,8 @@ First things first.
$ source <virtual env path>/bin/activate
#. Install cookiecutter-django: ::
$ cookiecutter gh:cookiecutter/cookiecutter-django
#.
.. include:: generate-project-block.rst
#. Install development requirements: ::
@ -43,6 +42,7 @@ First things first.
#. Create a new PostgreSQL database using createdb_: ::
$ createdb --username=postgres <project_slug>
``project_slug`` is what you have entered as the project_slug at the setup stage.
.. note::
@ -141,21 +141,38 @@ In production, we have Mailgun_ configured to have your back!
Celery
------
If the project is configured to use Celery as a task scheduler then by default tasks are set to run on the main thread
when developing locally. If you have the appropriate setup on your local machine then set the following
in ``config/settings/local.py``::
If the project is configured to use Celery as a task scheduler then, by default, tasks are set to run on the main thread when developing locally instead of getting sent to a broker. However, if you have Redis setup on your local machine, you can set the following in ``config/settings/local.py``::
CELERY_TASK_ALWAYS_EAGER = False
To run Celery locally, make sure redis-server is installed (instructions are available at https://redis.io/topics/quickstart), run the server in one terminal with `redis-server`, and then start celery in another terminal with the following command::
Next, make sure `redis-server` is installed (per the `Getting started with Redis`_ guide) and run the server in one terminal::
celery -A config.celery_app worker --loglevel=info
$ redis-server
Start the Celery worker by running the following command in another terminal::
$ celery -A config.celery_app worker --loglevel=info
That Celery worker should be running whenever your app is running, typically as a background process,
so that it can pick up any tasks that get queued. Learn more from the `Celery Workers Guide`_.
The project comes with a simple task for manual testing purposes, inside `<project_slug>/users/tasks.py`. To queue that task locally, start the Django shell, import the task, and call `delay()` on it::
$ python manage.py shell
>> from <project_slug>.users.tasks import get_users_count
>> get_users_count.delay()
You can also use Django admin to queue up tasks, thanks to the `django-celerybeat`_ package.
.. _Getting started with Redis guide: https://redis.io/docs/getting-started/
.. _Celery Workers Guide: https://docs.celeryq.dev/en/stable/userguide/workers.html
.. _django-celerybeat: https://django-celery-beat.readthedocs.io/en/latest/
Sass Compilation & Live Reloading
---------------------------------
If you've opted for Gulp as front-end pipeline, the project comes configured with `Sass`_ compilation and `live reloading`_. As you change you Sass/JS source files, the task runner will automatically rebuild the corresponding CSS and JS assets and reload them in your browser without refreshing the page.
If you've opted for Gulp or Webpack as front-end pipeline, the project comes configured with `Sass`_ compilation and `live reloading`_. As you change you Sass/JS source files, the task runner will automatically rebuild the corresponding CSS and JS assets and reload them in your browser without refreshing the page.
#. Make sure that `Node.js`_ v16 is installed on your machine.
#. In the project root, install the JS dependencies with::

View File

@ -0,0 +1,7 @@
Generate a new cookiecutter-django project: ::
$ cookiecutter gh:cookiecutter/cookiecutter-django
For more information refer to
:ref:`Project Generation Options <template-options>`.

View File

@ -27,6 +27,7 @@ Contents
websocket
faq
troubleshooting
contributing
Indices and tables
------------------

View File

@ -73,9 +73,10 @@ cloud_provider:
1. AWS_
2. GCP_
3. None
3. Azure_
4. None
Note that if you choose no cloud provider, media files won't work.
If you choose no cloud provider and docker, the production stack will serve the media files via an nginx Docker service. Without Docker, the media files won't work.
mail_service:
Select an email service that Django-Anymail provides
@ -101,7 +102,10 @@ frontend_pipeline:
1. None
2. `Django Compressor`_
3. `Gulp`_: support Bootstrap recompilation with real-time variables alteration.
3. `Gulp`_
4. `Webpack`_
Both Gulp and Webpack support Bootstrap recompilation with real-time variables alteration.
use_celery:
Indicates whether the project should be configured to use Celery_.
@ -151,9 +155,11 @@ debug:
.. _PostgreSQL: https://www.postgresql.org/docs/
.. _Gulp: https://github.com/gulpjs/gulp
.. _Webpack: https://webpack.js.org
.. _AWS: https://aws.amazon.com/s3/
.. _GCP: https://cloud.google.com/storage/
.. _Azure: https://azure.microsoft.com/en-us/products/storage/blobs/
.. _Amazon SES: https://aws.amazon.com/ses/
.. _Mailgun: https://www.mailgun.com

View File

@ -1,2 +1,3 @@
sphinx==5.3.0
sphinx-rtd-theme==1.1.1
sphinx==6.1.3
sphinx-rtd-theme==1.2.0
myst-parser==1.0.0

View File

@ -49,6 +49,9 @@ DJANGO_AWS_S3_CUSTOM_DOMAIN AWS_S3_CUSTOM_DOMAIN n/a
DJANGO_AWS_S3_MAX_MEMORY_SIZE AWS_S3_MAX_MEMORY_SIZE n/a 100_000_000
DJANGO_GCP_STORAGE_BUCKET_NAME GS_BUCKET_NAME n/a raises error
GOOGLE_APPLICATION_CREDENTIALS n/a n/a raises error
DJANGO_AZURE_ACCOUNT_KEY AZURE_ACCOUNT_KEY n/a raises error
DJANGO_AZURE_ACCOUNT_NAME AZURE_ACCOUNT_NAME n/a raises error
DJANGO_AZURE_CONTAINER_NAME AZURE_CONTAINER n/a raises error
SENTRY_DSN SENTRY_DSN n/a raises error
SENTRY_ENVIRONMENT n/a n/a production
SENTRY_TRACES_SAMPLE_RATE n/a n/a 0.0

View File

@ -28,10 +28,15 @@ Coverage
You should build your tests to provide the highest level of **code coverage**. You can run the ``pytest`` with code ``coverage`` by typing in the following command: ::
$ docker-compose -f local.yml run --rm django coverage run -m pytest
$ coverage run -m pytest
Once the tests are complete, in order to see the code coverage, run the following command: ::
$ coverage report
If you're running the project locally with Docker, use these commands instead: ::
$ docker-compose -f local.yml run --rm django coverage run -m pytest
$ docker-compose -f local.yml run --rm django coverage report
.. note::
@ -53,4 +58,4 @@ Once the tests are complete, in order to see the code coverage, run the followin
.. _develop locally with docker: ./developing-locally-docker.html
.. _customize: https://docs.pytest.org/en/latest/customize.html
.. _unittest: https://docs.python.org/3/library/unittest.html#module-unittest
.. _configuring: https://coverage.readthedocs.io/en/v4.5.x/config.html
.. _configuring: https://coverage.readthedocs.io/en/latest/config.html

View File

@ -1,5 +1,5 @@
Troubleshooting
=====================================
===============
This page contains some advice about errors and problems commonly encountered during the development of Cookiecutter Django applications.
@ -38,6 +38,16 @@ To fix this, you can either:
.. _rm: https://docs.docker.com/engine/reference/commandline/volume_rm/
.. _prune: https://docs.docker.com/v17.09/engine/reference/commandline/system_prune/
Variable is not set. Defaulting to a blank string
-------------------------------------------------
Example::
WARN[0000] The "DJANGO_AWS_STORAGE_BUCKET_NAME" variable is not set. Defaulting to a blank string.
WARN[0000] The "DJANGO_AWS_S3_CUSTOM_DOMAIN" variable is not set. Defaulting to a blank string.
You have probably opted for Docker + Webpack without Whitenoise. This is a know limitation of the combination, which needs a little bit of manual intervention. See the :ref:`dedicated section about it <webpack-whitenoise-limitation>`.
Others
------

View File

@ -10,6 +10,7 @@ TODO: restrict Cookiecutter Django project initialization to
"""
from __future__ import print_function
import json
import os
import random
import shutil
@ -87,15 +88,30 @@ def remove_heroku_build_hooks():
shutil.rmtree("bin")
def remove_sass_files():
shutil.rmtree(os.path.join("{{cookiecutter.project_slug}}", "static", "sass"))
def remove_gulp_files():
file_names = ["gulpfile.js"]
for file_name in file_names:
os.remove(file_name)
remove_sass_files()
def remove_sass_files():
shutil.rmtree(os.path.join("{{cookiecutter.project_slug}}", "static", "sass"))
def remove_webpack_files():
shutil.rmtree("webpack")
remove_vendors_js()
def remove_vendors_js():
vendors_js_path = os.path.join(
"{{ cookiecutter.project_slug }}",
"static",
"js",
"vendors.js",
)
if os.path.exists(vendors_js_path):
os.remove(vendors_js_path)
def remove_packagejson_file():
@ -104,6 +120,83 @@ def remove_packagejson_file():
os.remove(file_name)
def update_package_json(remove_dev_deps=None, remove_keys=None, scripts=None):
remove_dev_deps = remove_dev_deps or []
remove_keys = remove_keys or []
scripts = scripts or {}
with open("package.json", mode="r") as fd:
content = json.load(fd)
for package_name in remove_dev_deps:
content["devDependencies"].pop(package_name)
for key in remove_keys:
content.pop(key)
content["scripts"].update(scripts)
with open("package.json", mode="w") as fd:
json.dump(content, fd, ensure_ascii=False, indent=2)
fd.write("\n")
def handle_js_runner(choice, use_docker, use_async):
if choice == "Gulp":
update_package_json(
remove_dev_deps=[
"@babel/core",
"@babel/preset-env",
"babel-loader",
"concurrently",
"css-loader",
"mini-css-extract-plugin",
"postcss-loader",
"postcss-preset-env",
"sass-loader",
"webpack",
"webpack-bundle-tracker",
"webpack-cli",
"webpack-dev-server",
"webpack-merge",
],
remove_keys=["babel"],
scripts={
"dev": "gulp",
"build": "gulp generate-assets",
},
)
remove_webpack_files()
elif choice == "Webpack":
scripts = {
"dev": "webpack serve --config webpack/dev.config.js",
"build": "webpack --config webpack/prod.config.js",
}
remove_dev_deps = [
"browser-sync",
"cssnano",
"gulp",
"gulp-imagemin",
"gulp-plumber",
"gulp-postcss",
"gulp-rename",
"gulp-sass",
"gulp-uglify-es",
]
if not use_docker:
dev_django_cmd = (
"uvicorn config.asgi:application --reload"
if use_async
else "python manage.py runserver_plus"
)
scripts.update(
{
"dev": "concurrently npm:dev:*",
"dev:webpack": "webpack serve --config webpack/dev.config.js",
"dev:django": dev_django_cmd,
}
)
else:
remove_dev_deps.append("concurrently")
update_package_json(remove_dev_deps=remove_dev_deps, scripts=scripts)
remove_gulp_files()
def remove_celery_files():
file_names = [
os.path.join("config", "celery_app.py"),
@ -293,6 +386,7 @@ def set_flags_in_settings_files():
def remove_envs_and_associated_files():
shutil.rmtree(".envs")
os.remove("merge_production_dotenvs_in_dotenv.py")
shutil.rmtree("tests")
def remove_celery_compose_dirs():
@ -383,15 +477,26 @@ def main():
if "{{ cookiecutter.keep_local_envs_in_vcs }}".lower() == "y":
append_to_gitignore_file("!.envs/.local/")
if "{{ cookiecutter.frontend_pipeline }}" != "Gulp":
if "{{ cookiecutter.frontend_pipeline }}" in ["None", "Django Compressor"]:
remove_gulp_files()
remove_webpack_files()
remove_sass_files()
remove_packagejson_file()
if "{{ cookiecutter.use_docker }}".lower() == "y":
remove_node_dockerfile()
else:
handle_js_runner(
"{{ cookiecutter.frontend_pipeline }}",
use_docker=("{{ cookiecutter.use_docker }}".lower() == "y"),
use_async=("{{ cookiecutter.use_async }}".lower() == "y"),
)
if "{{ cookiecutter.cloud_provider}}" == "None":
if (
"{{ cookiecutter.cloud_provider }}" == "None"
and "{{ cookiecutter.use_docker }}".lower() == "n"
):
print(
WARNING + "You chose not to use a cloud provider, "
WARNING + "You chose to not use any cloud providers nor Docker, "
"media files won't be served in production." + TERMINATOR
)
remove_storages_module()

View File

@ -72,11 +72,8 @@ if (
sys.exit(1)
if (
"{{ cookiecutter.cloud_provider }}" == "GCP"
and "{{ cookiecutter.mail_service }}" == "Amazon SES"
) or (
"{{ cookiecutter.cloud_provider }}" == "None"
and "{{ cookiecutter.mail_service }}" == "Amazon SES"
"{{ cookiecutter.mail_service }}" == "Amazon SES"
and "{{ cookiecutter.cloud_provider }}" != "AWS"
):
print(
"You should either use AWS or select a different "

View File

@ -1,26 +1,26 @@
cookiecutter==2.1.1
sh==1.14.3; sys_platform != "win32"
sh==2.0.2; sys_platform != "win32"
binaryornot==0.4.4
# Code quality
# ------------------------------------------------------------------------------
black==22.10.0
isort==5.10.1
flake8==5.0.4
flake8-isort==5.0.0
pre-commit==2.20.0
black==23.1.0
isort==5.12.0
flake8==6.0.0
flake8-isort==6.0.0
pre-commit==3.1.1
# Testing
# ------------------------------------------------------------------------------
tox==3.27.1
pytest==7.2.0
tox==4.4.6
pytest==7.2.2
pytest-cookies==0.6.1
pytest-instafail==0.4.2
pyyaml==6.0
# Scripting
# ------------------------------------------------------------------------------
PyGithub==1.57
gitpython==3.1.29
PyGithub==1.58.0
gitpython==3.1.31
jinja2==3.1.2
requests==2.28.1
requests==2.28.2

View File

@ -82,14 +82,20 @@ def group_pulls_by_change_type(
grouped_pulls = {
"Changed": [],
"Fixed": [],
"Documentation": [],
"Updated": [],
}
for pull in pull_requests_list:
label_names = {label.name for label in pull.labels}
if "project infrastructure" in label_names:
# Don't mention it in the changelog
continue
if "update" in label_names:
group_name = "Updated"
elif "bug" in label_names:
group_name = "Fixed"
elif "docs" in label_names:
group_name = "Documentation"
else:
group_name = "Changed"
grouped_pulls[group_name].append(pull)

View File

@ -5,7 +5,7 @@ except ImportError:
from distutils.core import setup
# We use calendar versioning
version = "2022.11.11"
version = "2023.03.09"
with open("README.rst") as readme_file:
long_description = readme_file.read()

View File

@ -32,13 +32,11 @@ pytest
# Make sure the check doesn't raise any warnings
python manage.py check --fail-level WARNING
# Run npm build script if package.json is present
if [ -f "package.json" ]
then
npm install
if [ -f "gulpfile.js" ]
then
npm run build
fi
npm run build
fi
# Generate the HTML for the documentation

View File

@ -56,6 +56,8 @@ SUPPORTED_COMBINATIONS = [
{"cloud_provider": "AWS", "use_whitenoise": "n"},
{"cloud_provider": "GCP", "use_whitenoise": "y"},
{"cloud_provider": "GCP", "use_whitenoise": "n"},
{"cloud_provider": "Azure", "use_whitenoise": "y"},
{"cloud_provider": "Azure", "use_whitenoise": "n"},
{"cloud_provider": "None", "use_whitenoise": "y", "mail_service": "Mailgun"},
{"cloud_provider": "None", "use_whitenoise": "y", "mail_service": "Mailjet"},
{"cloud_provider": "None", "use_whitenoise": "y", "mail_service": "Mandrill"},
@ -82,7 +84,16 @@ SUPPORTED_COMBINATIONS = [
{"cloud_provider": "GCP", "mail_service": "SendinBlue"},
{"cloud_provider": "GCP", "mail_service": "SparkPost"},
{"cloud_provider": "GCP", "mail_service": "Other SMTP"},
# Note: cloud_providers GCP and None with mail_service Amazon SES is not supported
{"cloud_provider": "Azure", "mail_service": "Mailgun"},
{"cloud_provider": "Azure", "mail_service": "Mailjet"},
{"cloud_provider": "Azure", "mail_service": "Mandrill"},
{"cloud_provider": "Azure", "mail_service": "Postmark"},
{"cloud_provider": "Azure", "mail_service": "Sendgrid"},
{"cloud_provider": "Azure", "mail_service": "SendinBlue"},
{"cloud_provider": "Azure", "mail_service": "SparkPost"},
{"cloud_provider": "Azure", "mail_service": "Other SMTP"},
# Note: cloud_providers GCP, Azure, and None
# with mail_service Amazon SES is not supported
{"use_async": "y"},
{"use_async": "n"},
{"use_drf": "y"},
@ -90,6 +101,7 @@ SUPPORTED_COMBINATIONS = [
{"frontend_pipeline": "None"},
{"frontend_pipeline": "Django Compressor"},
{"frontend_pipeline": "Gulp"},
{"frontend_pipeline": "Webpack"},
{"use_celery": "y"},
{"use_celery": "n"},
{"use_mailhog": "y"},
@ -113,6 +125,7 @@ SUPPORTED_COMBINATIONS = [
UNSUPPORTED_COMBINATIONS = [
{"cloud_provider": "None", "use_whitenoise": "n"},
{"cloud_provider": "GCP", "mail_service": "Amazon SES"},
{"cloud_provider": "Azure", "mail_service": "Amazon SES"},
{"cloud_provider": "None", "mail_service": "Amazon SES"},
]
@ -122,11 +135,11 @@ def _fixture_id(ctx):
return "-".join(f"{key}:{value}" for key, value in ctx.items())
def build_files_list(root_dir):
def build_files_list(base_dir):
"""Build a list containing absolute paths to the generated files."""
return [
os.path.join(dirpath, file_path)
for dirpath, subdirs, files in os.walk(root_dir)
for dirpath, subdirs, files in os.walk(base_dir)
for file_path in files
]
@ -219,7 +232,7 @@ def test_travis_invokes_pytest(cookies, context, use_docker, expected_test_scrip
("y", "docker-compose -f local.yml run django pytest"),
],
)
def test_gitlab_invokes_flake8_and_pytest(
def test_gitlab_invokes_precommit_and_pytest(
cookies, context, use_docker, expected_test_script
):
context.update({"ci_tool": "Gitlab", "use_docker": use_docker})
@ -233,7 +246,9 @@ def test_gitlab_invokes_flake8_and_pytest(
with open(f"{result.project_path}/.gitlab-ci.yml") as gitlab_yml:
try:
gitlab_config = yaml.safe_load(gitlab_yml)
assert gitlab_config["flake8"]["script"] == ["flake8"]
assert gitlab_config["precommit"]["script"] == [
"pre-commit run --show-diff-on-failure --color=always --all-files"
]
assert gitlab_config["pytest"]["script"] == [expected_test_script]
except yaml.YAMLError as e:
pytest.fail(e)

View File

@ -41,3 +41,9 @@ docker-compose -f local.yml run django python manage.py check --fail-level WARNI
# Generate the HTML for the documentation
docker-compose -f local.yml run docs make html
# Run npm build script if package.json is present
if [ -f "package.json" ]
then
docker-compose -f local.yml run node npm run build
fi

View File

@ -8,3 +8,4 @@
.readthedocs.yml
.travis.yml
venv
.git

View File

@ -44,6 +44,12 @@ DJANGO_AWS_STORAGE_BUCKET_NAME=
# ------------------------------------------------------------------------------
GOOGLE_APPLICATION_CREDENTIALS=
DJANGO_GCP_STORAGE_BUCKET_NAME=
{% elif cookiecutter.cloud_provider == 'Azure' %}
# Azure
# ------------------------------------------------------------------------------
DJANGO_AZURE_ACCOUNT_KEY=
DJANGO_AZURE_ACCOUNT_NAME=
DJANGO_AZURE_CONTAINER_NAME=
{% endif %}
# django-allauth
# ------------------------------------------------------------------------------

View File

@ -27,16 +27,15 @@ jobs:
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v3
uses: actions/setup-python@v4
with:
python-version: "3.10"
cache: pip
cache-dependency-path: |
requirements/base.txt
requirements/local.txt
{%- if cookiecutter.open_source_license != 'Not open source' %}
# Consider using pre-commit.ci for open source project
{%- endif %}
- name: Run pre-commit
uses: pre-commit/action@v2.0.3
uses: pre-commit/action@v3.0.0
# With no caching at all the entire ci process takes 4m 30s to complete!
pytest:
@ -85,7 +84,7 @@ jobs:
{%- else %}
- name: Set up Python
uses: actions/setup-python@v3
uses: actions/setup-python@v4
with:
python-version: "3.10"
cache: pip

View File

@ -326,6 +326,9 @@ Session.vim
# Auto-generated tag files
tags
# Redis dump file
dump.rdb
### Project template
{%- if cookiecutter.use_mailhog == 'y' and cookiecutter.use_docker == 'n' %}
MailHog
@ -343,4 +346,9 @@ project.css
project.min.css
vendors.js
*.min.js
*.min.js.map
{%- endif %}
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
{{ cookiecutter.project_slug }}/static/webpack_bundles/
webpack-stats.json
{%- endif %}

View File

@ -7,21 +7,26 @@ variables:
POSTGRES_PASSWORD: ''
POSTGRES_DB: 'test_{{ cookiecutter.project_slug }}'
POSTGRES_HOST_AUTH_METHOD: trust
{% if cookiecutter.use_celery == 'y' -%}
{%- if cookiecutter.use_celery == 'y' %}
CELERY_BROKER_URL: 'redis://redis:6379/0'
{%- endif %}
flake8:
precommit:
stage: lint
image: python:3.10-alpine
image: python:3.10
variables:
PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit
cache:
paths:
- ${PRE_COMMIT_HOME}
before_script:
- pip install -q flake8
- pip install -q pre-commit
script:
- flake8
- pre-commit run --show-diff-on-failure --color=always --all-files
pytest:
stage: test
{% if cookiecutter.use_docker == 'y' -%}
{%- if cookiecutter.use_docker == 'y' %}
image: docker/compose:1.29.2
tags:
- docker
@ -34,7 +39,7 @@ pytest:
- docker-compose -f local.yml up -d
script:
- docker-compose -f local.yml run django pytest
{%- else -%}
{%- else %}
image: python:3.10
tags:
- python
@ -42,11 +47,8 @@ pytest:
- postgres:{{ cookiecutter.postgresql_version }}
variables:
DATABASE_URL: pgsql://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres/$POSTGRES_DB
before_script:
- pip install -r requirements/local.txt
script:
- pytest
{%- endif %}

View File

@ -10,7 +10,7 @@
<option value="celeryworker"/>
<option value="celerybeat"/>
{%- endif %}
{%- if cookiecutter.frontend_pipeline == 'Gulp' %}
{%- if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %}
<option value="node"/>
{%- endif %}
</list>

View File

@ -13,7 +13,7 @@
</facet>
</component>
<component name="NewModuleRootManager">
{% if cookiecutter.frontend_pipeline == 'Gulp' %}
{% if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %}
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/node_modules" />
</content>

View File

@ -3,30 +3,30 @@ default_stages: [commit]
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- repo: https://github.com/asottile/pyupgrade
rev: v3.2.2
rev: v3.3.1
hooks:
- id: pyupgrade
args: [--py310-plus]
- repo: https://github.com/psf/black
rev: 22.10.0
rev: 23.1.0
hooks:
- id: black
- repo: https://github.com/PyCQA/isort
rev: 5.10.1
rev: 5.12.0
hooks:
- id: isort
- repo: https://github.com/PyCQA/flake8
rev: 5.0.4
rev: 6.0.0
hooks:
- id: flake8
args: ["--config=setup.cfg"]

View File

@ -1,12 +1,20 @@
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Set the version of Python and other tools you might need
build:
os: ubuntu-22.04
tools:
python: "3.10"
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py
build:
image: testing
# Python requirements required to build your docs
python:
version: 3.10
install:
- requirements: requirements/local.txt

View File

@ -63,6 +63,20 @@ celery -A config.celery_app worker -l info
Please note: For Celery's import magic to work, it is important *where* the celery commands are run. If you are in the same folder with *manage.py*, you should be right.
To run [periodic tasks](https://docs.celeryq.dev/en/stable/userguide/periodic-tasks.html), you'll need to start the celery beat scheduler service. You can start it as a standalone process:
``` bash
cd {{cookiecutter.project_slug}}
celery -A config.celery_app beat
```
or you can embed the beat service inside a worker with the `-B` option (not recommended for production use):
``` bash
cd {{cookiecutter.project_slug}}
celery -A config.celery_app worker -B -l info
```
{%- endif %}
{%- if cookiecutter.use_mailhog == "y" %}
@ -128,13 +142,14 @@ See detailed [cookiecutter-django Heroku documentation](http://cookiecutter-djan
See detailed [cookiecutter-django Docker documentation](http://cookiecutter-django.readthedocs.io/en/latest/deployment-with-docker.html).
{%- endif %}
{%- if cookiecutter.frontend_pipeline == 'Gulp' %}
{%- if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %}
### Custom Bootstrap Compilation
The generated CSS is set up with automatic Bootstrap recompilation with variables of your choice.
Bootstrap v5 is installed using npm and customised by tweaking your variables in `static/sass/custom_bootstrap_vars`.
You can find a list of available variables [in the bootstrap source](https://github.com/twbs/bootstrap/blob/main/scss/_variables.scss), or get explanations on them in the [Bootstrap docs](https://getbootstrap.com/docs/5.1/customize/sass/).
You can find a list of available variables [in the bootstrap source](https://github.com/twbs/bootstrap/blob/v5.1.3/scss/_variables.scss), or get explanations on them in the [Bootstrap docs](https://getbootstrap.com/docs/5.1/customize/sass/).
Bootstrap's javascript as well as its dependencies is concatenated into a single file: `static/js/vendors.js`.
Bootstrap's javascript as well as its dependencies are concatenated into a single file: `static/js/vendors.js`.
{%- endif %}

View File

@ -5,4 +5,4 @@ set -o nounset
rm -f './celerybeat.pid'
celery -A config.celery_app beat -l INFO
exec watchfiles celery.__main__.main --args '-A config.celery_app beat -l INFO'

View File

@ -3,9 +3,6 @@
set -o errexit
set -o nounset
celery \
-A config.celery_app \
-b "${CELERY_BROKER_URL}" \
flower \
--basic_auth="${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}"
exec watchfiles celery.__main__.main \
--args \
"-A config.celery_app -b \"${CELERY_BROKER_URL}\" flower --basic_auth=\"${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}\""

View File

@ -4,4 +4,4 @@ set -o errexit
set -o nounset
watchfiles celery.__main__.main --args '-A config.celery_app worker -l INFO'
exec watchfiles celery.__main__.main --args '-A config.celery_app worker -l INFO'

View File

@ -7,7 +7,7 @@ set -o nounset
python manage.py migrate
{%- if cookiecutter.use_async == 'y' %}
uvicorn config.asgi:application --host 0.0.0.0 --reload --reload-include '*.html'
exec uvicorn config.asgi:application --host 0.0.0.0 --reload --reload-include '*.html'
{%- else %}
python manage.py runserver_plus 0.0.0.0:8000
exec python manage.py runserver_plus 0.0.0.0:8000
{%- endif %}

View File

@ -4,4 +4,4 @@ set -o errexit
set -o pipefail
set -o nounset
make livehtml
exec make livehtml

View File

@ -1,4 +1,4 @@
FROM garland/aws-cli-docker:1.15.47
FROM garland/aws-cli-docker:1.16.140
COPY ./compose/production/aws/maintenance /usr/local/bin/maintenance
COPY ./compose/production/postgres/maintenance/_sourced /usr/local/bin/maintenance/_sourced

View File

@ -1,6 +1,6 @@
ARG PYTHON_VERSION=3.10-slim-bullseye
{% if cookiecutter.frontend_pipeline == 'Gulp' -%}
{% if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] -%}
FROM node:16-bullseye-slim as client-builder
ARG APP_HOME=/app
@ -9,6 +9,20 @@ WORKDIR ${APP_HOME}
COPY ./package.json ${APP_HOME}
RUN npm install && npm cache clean --force
COPY . ${APP_HOME}
{%- if cookiecutter.frontend_pipeline == 'Webpack' and cookiecutter.use_whitenoise == 'n' %}
{%- if cookiecutter.cloud_provider == 'AWS' %}
ARG DJANGO_AWS_STORAGE_BUCKET_NAME
ENV DJANGO_AWS_STORAGE_BUCKET_NAME=${DJANGO_AWS_STORAGE_BUCKET_NAME}
ARG DJANGO_AWS_S3_CUSTOM_DOMAIN
ENV DJANGO_AWS_S3_CUSTOM_DOMAIN=${DJANGO_AWS_S3_CUSTOM_DOMAIN}
{%- elif cookiecutter.cloud_provider == 'GCP' %}
ARG DJANGO_GCP_STORAGE_BUCKET_NAME
ENV DJANGO_GCP_STORAGE_BUCKET_NAME=${DJANGO_GCP_STORAGE_BUCKET_NAME}
{%- elif cookiecutter.cloud_provider == 'Azure' %}
ARG DJANGO_AZURE_ACCOUNT_NAME
ENV DJANGO_AZURE_ACCOUNT_NAME=${DJANGO_AZURE_ACCOUNT_NAME}
{%- endif %}
{%- endif %}
RUN npm run build
{%- endif %}
@ -99,7 +113,7 @@ RUN chmod +x /start-flower
# copy application code to WORKDIR
{%- if cookiecutter.frontend_pipeline == 'Gulp' %}
{%- if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %}
COPY --from=client-builder --chown=django:django ${APP_HOME} ${APP_HOME}
{% else %}
COPY --chown=django:django . ${APP_HOME}

View File

@ -28,7 +28,7 @@ if compress_enabled; then
fi
{%- endif %}
{%- if cookiecutter.use_async == 'y' %}
/usr/local/bin/gunicorn config.asgi --bind 0.0.0.0:5000 --chdir=/app -k uvicorn.workers.UvicornWorker
exec /usr/local/bin/gunicorn config.asgi --bind 0.0.0.0:5000 --chdir=/app -k uvicorn.workers.UvicornWorker
{%- else %}
/usr/local/bin/gunicorn config.wsgi --bind 0.0.0.0:5000 --chdir=/app
exec /usr/local/bin/gunicorn config.wsgi --bind 0.0.0.0:5000 --chdir=/app
{%- endif %}

View File

@ -0,0 +1,2 @@
FROM nginx:1.17.8-alpine
COPY ./compose/production/nginx/default.conf /etc/nginx/conf.d/default.conf

View File

@ -0,0 +1,7 @@
server {
listen 80;
server_name localhost;
location /media/ {
alias /usr/share/nginx/media/;
}
}

View File

@ -1,4 +1,4 @@
FROM traefik:v2.2.11
FROM traefik:2.9.8
RUN mkdir -p /etc/traefik/acme \
&& touch /etc/traefik/acme/acme.json \
&& chmod 600 /etc/traefik/acme/acme.json

View File

@ -57,6 +57,18 @@ http:
# https://docs.traefik.io/master/routing/routers/#certresolver
certResolver: letsencrypt
{%- endif %}
{%- if cookiecutter.cloud_provider == 'None' %}
web-media-router:
rule: "Host(`{{ cookiecutter.domain_name }}`) && PathPrefix(`/media/`)"
entryPoints:
- web-secure
middlewares:
- csrf
service: django-media
tls:
certResolver: letsencrypt
{%- endif %}
middlewares:
csrf:
@ -77,6 +89,13 @@ http:
servers:
- url: http://flower:5555
{%- endif %}
{%- if cookiecutter.cloud_provider == 'None' %}
django-media:
loadBalancer:
servers:
- url: http://nginx:80
{%- endif %}
providers:
# https://docs.traefik.io/master/providers/file/

View File

@ -15,8 +15,8 @@ from django.core.asgi import get_asgi_application
# This allows easy placement of apps within the interior
# {{ cookiecutter.project_slug }} directory.
ROOT_DIR = Path(__file__).resolve(strict=True).parent.parent
sys.path.append(str(ROOT_DIR / "{{ cookiecutter.project_slug }}"))
BASE_DIR = Path(__file__).resolve(strict=True).parent.parent
sys.path.append(str(BASE_DIR / "{{ cookiecutter.project_slug }}"))
# If DJANGO_SETTINGS_MODULE is unset, default to the local settings
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")

View File

@ -5,15 +5,15 @@ from pathlib import Path
import environ
ROOT_DIR = Path(__file__).resolve(strict=True).parent.parent.parent
BASE_DIR = Path(__file__).resolve(strict=True).parent.parent.parent
# {{ cookiecutter.project_slug }}/
APPS_DIR = ROOT_DIR / "{{ cookiecutter.project_slug }}"
APPS_DIR = BASE_DIR / "{{ cookiecutter.project_slug }}"
env = environ.Env()
READ_DOT_ENV_FILE = env.bool("DJANGO_READ_DOT_ENV_FILE", default=False)
if READ_DOT_ENV_FILE:
# OS environment variables take precedence over variables from .env
env.read_env(str(ROOT_DIR / ".env"))
env.read_env(str(BASE_DIR / ".env"))
# GENERAL
# ------------------------------------------------------------------------------
@ -30,12 +30,10 @@ LANGUAGE_CODE = "en-us"
SITE_ID = 1
# https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n
USE_I18N = True
# https://docs.djangoproject.com/en/dev/ref/settings/#use-l10n
USE_L10N = True
# https://docs.djangoproject.com/en/dev/ref/settings/#use-tz
USE_TZ = True
# https://docs.djangoproject.com/en/dev/ref/settings/#locale-paths
LOCALE_PATHS = [str(ROOT_DIR / "locale")]
LOCALE_PATHS = [str(BASE_DIR / "locale")]
# DATABASES
# ------------------------------------------------------------------------------
@ -89,6 +87,9 @@ THIRD_PARTY_APPS = [
"corsheaders",
"drf_spectacular",
{%- endif %}
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
"webpack_loader",
{%- endif %}
]
LOCAL_APPS = [
@ -154,14 +155,13 @@ MIDDLEWARE = [
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.common.BrokenLinkEmailsMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
# STATIC
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#static-root
STATIC_ROOT = str(ROOT_DIR / "staticfiles")
STATIC_ROOT = str(BASE_DIR / "staticfiles")
# https://docs.djangoproject.com/en/dev/ref/settings/#static-url
STATIC_URL = "/static/"
# https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS
@ -285,6 +285,11 @@ CELERY_BROKER_URL = env("CELERY_BROKER_URL")
CELERY_RESULT_BACKEND = CELERY_BROKER_URL
# https://docs.celeryq.dev/en/stable/userguide/configuration.html#result-extended
CELERY_RESULT_EXTENDED = True
# https://docs.celeryq.dev/en/stable/userguide/configuration.html#result-backend-always-retry
# https://github.com/celery/celery/pull/6122
CELERY_RESULT_BACKEND_ALWAYS_RETRY = True
# https://docs.celeryq.dev/en/stable/userguide/configuration.html#result-backend-max-retries
CELERY_RESULT_BACKEND_MAX_RETRIES = 10
# https://docs.celeryq.dev/en/stable/userguide/configuration.html#std:setting-accept_content
CELERY_ACCEPT_CONTENT = ["json"]
# https://docs.celeryq.dev/en/stable/userguide/configuration.html#std:setting-task_serializer
@ -299,6 +304,10 @@ CELERY_TASK_TIME_LIMIT = 5 * 60
CELERY_TASK_SOFT_TIME_LIMIT = 60
# https://docs.celeryq.dev/en/stable/userguide/configuration.html#beat-scheduler
CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"
# https://docs.celeryq.dev/en/stable/userguide/configuration.html#worker-send-task-events
CELERY_WORKER_SEND_TASK_EVENTS = True
# https://docs.celeryq.dev/en/stable/userguide/configuration.html#std-setting-task_send_sent_event
CELERY_TASK_SEND_SENT_EVENT = True
{%- endif %}
# django-allauth
@ -354,11 +363,20 @@ SPECTACULAR_SETTINGS = {
"DESCRIPTION": "Documentation of API endpoints of {{ cookiecutter.project_name }}",
"VERSION": "1.0.0",
"SERVE_PERMISSIONS": ["rest_framework.permissions.IsAdminUser"],
"SERVERS": [
{"url": "http://127.0.0.1:8000", "description": "Local Development server"},
{"url": "https://{{ cookiecutter.domain_name }}", "description": "Production server"},
],
}
{%- endif %}
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
# django-webpack-loader
# ------------------------------------------------------------------------------
WEBPACK_LOADER = {
"DEFAULT": {
"CACHE": not DEBUG,
"STATS_FILE": BASE_DIR / "webpack-stats.json",
"POLL_INTERVAL": 0.1,
"IGNORE": [r".+\.hot-update.js", r".+\.map"],
}
}
{%- endif %}
# Your stuff...
# ------------------------------------------------------------------------------

View File

@ -69,7 +69,7 @@ if env("USE_DOCKER") == "yes":
hostname, _, ips = socket.gethostbyname_ex(socket.gethostname())
INTERNAL_IPS += [".".join(ip.split(".")[:-1] + ["1"]) for ip in ips]
{%- if cookiecutter.frontend_pipeline == 'Gulp' %}
{%- if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %}
try:
_, _, ips = socket.gethostbyname_ex("node")
INTERNAL_IPS.extend(ips)
@ -94,6 +94,12 @@ CELERY_TASK_ALWAYS_EAGER = True
# https://docs.celeryq.dev/en/stable/userguide/configuration.html#task-eager-propagates
CELERY_TASK_EAGER_PROPAGATES = True
{%- endif %}
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
# django-webpack-loader
# ------------------------------------------------------------------------------
WEBPACK_LOADER["DEFAULT"]["CACHE"] = not DEBUG # noqa F405
{%- endif %}
# Your stuff...
# ------------------------------------------------------------------------------

View File

@ -22,8 +22,6 @@ ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=["{{ cookiecutter.domai
# DATABASES
# ------------------------------------------------------------------------------
DATABASES["default"] = env.db("DATABASE_URL") # noqa F405
DATABASES["default"]["ATOMIC_REQUESTS"] = True # noqa F405
DATABASES["default"]["CONN_MAX_AGE"] = env.int("CONN_MAX_AGE", default=60) # noqa F405
# CACHES
@ -100,6 +98,10 @@ aws_s3_domain = AWS_S3_CUSTOM_DOMAIN or f"{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws
{% elif cookiecutter.cloud_provider == 'GCP' %}
GS_BUCKET_NAME = env("DJANGO_GCP_STORAGE_BUCKET_NAME")
GS_DEFAULT_ACL = "publicRead"
{% elif cookiecutter.cloud_provider == 'Azure' %}
AZURE_ACCOUNT_KEY = env("DJANGO_AZURE_ACCOUNT_KEY")
AZURE_ACCOUNT_NAME = env("DJANGO_AZURE_ACCOUNT_NAME")
AZURE_CONTAINER = env("DJANGO_AZURE_CONTAINER_NAME")
{% endif -%}
{% if cookiecutter.cloud_provider != 'None' or cookiecutter.use_whitenoise == 'y' -%}
@ -116,6 +118,9 @@ STATIC_URL = f"https://{aws_s3_domain}/static/"
STATICFILES_STORAGE = "{{cookiecutter.project_slug}}.utils.storages.StaticRootGoogleCloudStorage"
COLLECTFAST_STRATEGY = "collectfast.strategies.gcloud.GoogleCloudStrategy"
STATIC_URL = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/static/"
{% elif cookiecutter.cloud_provider == 'Azure' -%}
STATICFILES_STORAGE = "{{cookiecutter.project_slug}}.utils.storages.StaticRootAzureStorage"
STATIC_URL = f"https://{AZURE_ACCOUNT_NAME}.blob.core.windows.net/static/"
{% endif -%}
# MEDIA
@ -126,6 +131,9 @@ MEDIA_URL = f"https://{aws_s3_domain}/media/"
{%- elif cookiecutter.cloud_provider == 'GCP' %}
DEFAULT_FILE_STORAGE = "{{cookiecutter.project_slug}}.utils.storages.MediaRootGoogleCloudStorage"
MEDIA_URL = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/media/"
{%- elif cookiecutter.cloud_provider == 'Azure' %}
DEFAULT_FILE_STORAGE = "{{cookiecutter.project_slug}}.utils.storages.MediaRootAzureStorage"
MEDIA_URL = f"https://{AZURE_ACCOUNT_NAME}.blob.core.windows.net/media/"
{%- endif %}
# EMAIL
@ -228,7 +236,7 @@ COMPRESS_ENABLED = env.bool("COMPRESS_ENABLED", default=True)
{%- if cookiecutter.cloud_provider == 'None' %}
# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_STORAGE
COMPRESS_STORAGE = "compressor.storage.GzipCompressorFileStorage"
{%- elif cookiecutter.cloud_provider in ('AWS', 'GCP') and cookiecutter.use_whitenoise == 'n' %}
{%- elif cookiecutter.cloud_provider in ('AWS', 'GCP', 'Azure') and cookiecutter.use_whitenoise == 'n' %}
# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_STORAGE
COMPRESS_STORAGE = STATICFILES_STORAGE
{%- endif %}
@ -360,5 +368,15 @@ sentry_sdk.init(
traces_sample_rate=env.float("SENTRY_TRACES_SAMPLE_RATE", default=0.0),
)
{% endif %}
{% if cookiecutter.use_drf == "y" -%}
# django-rest-framework
# -------------------------------------------------------------------------------
# Tools that generate code samples can use SERVERS to point to the correct domain
SPECTACULAR_SETTINGS["SERVERS"] = [ # noqa F405
{"url": "https://{{ cookiecutter.domain_name }}", "description": "Production server"}
]
{%- endif %}
# Your stuff...
# ------------------------------------------------------------------------------

View File

@ -25,9 +25,17 @@ PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]
# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
# DEBUGING FOR TEMPLATES
# DEBUGGING FOR TEMPLATES
# ------------------------------------------------------------------------------
TEMPLATES[0]["OPTIONS"]["debug"] = True # type: ignore # noqa F405
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
# django-webpack-loader
# ------------------------------------------------------------------------------
WEBPACK_LOADER["DEFAULT"][ # noqa F405
"LOADER_CLASS"
] = "webpack_loader.loader.FakeWebpackLoader"
{%- endif %}
# Your stuff...
# ------------------------------------------------------------------------------

View File

@ -21,8 +21,8 @@ from django.core.wsgi import get_wsgi_application
# This allows easy placement of apps within the interior
# {{ cookiecutter.project_slug }} directory.
ROOT_DIR = Path(__file__).resolve(strict=True).parent.parent
sys.path.append(str(ROOT_DIR / "{{ cookiecutter.project_slug }}"))
BASE_DIR = Path(__file__).resolve(strict=True).parent.parent
sys.path.append(str(BASE_DIR / "{{ cookiecutter.project_slug }}"))
# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
# if running multiple sites in the same mod_wsgi process. To fix this, use
# mod_wsgi daemon mode with each site in its own daemon process, or use

View File

@ -10,6 +10,7 @@ const pjson = require('./package.json')
const autoprefixer = require('autoprefixer')
const browserSync = require('browser-sync').create()
const concat = require('gulp-concat')
const tildeImporter = require('node-sass-tilde-importer');
const cssnano = require ('cssnano')
const imagemin = require('gulp-imagemin')
const pixrem = require('pixrem')
@ -27,7 +28,6 @@ function pathsConfig(appName) {
const vendorsRoot = 'node_modules'
return {
bootstrapSass: `${vendorsRoot}/bootstrap/scss`,
vendorsJs: [
`${vendorsRoot}/@popperjs/core/dist/umd/popper.js`,
`${vendorsRoot}/bootstrap/dist/js/bootstrap.js`,
@ -61,8 +61,8 @@ function styles() {
return src(`${paths.sass}/project.scss`)
.pipe(sass({
importer: tildeImporter,
includePaths: [
paths.bootstrapSass,
paths.sass
]
}).on('error', sass.logError))
@ -85,13 +85,13 @@ function scripts() {
// Vendor Javascript minification
function vendorScripts() {
return src(paths.vendorsJs)
return src(paths.vendorsJs, { sourcemaps: true })
.pipe(concat('vendors.js'))
.pipe(dest(paths.js))
.pipe(plumber()) // Checks for errors
.pipe(uglify()) // Minifies the js
.pipe(rename({ suffix: '.min' }))
.pipe(dest(paths.js))
.pipe(dest(paths.js, { sourcemaps: '.' }))
}
// Image compression

View File

@ -11,7 +11,6 @@ services:
dockerfile: ./compose/local/django/Dockerfile
image: {{ cookiecutter.project_slug }}_local_django
container_name: {{ cookiecutter.project_slug }}_local_django
platform: linux/x86_64
depends_on:
- postgres
{%- if cookiecutter.use_celery == 'y' %}
@ -36,15 +35,14 @@ services:
image: {{ cookiecutter.project_slug }}_production_postgres
container_name: {{ cookiecutter.project_slug }}_local_postgres
volumes:
- {{ cookiecutter.project_slug }}_local_postgres_data:/var/lib/postgresql/data:Z
- {{ cookiecutter.project_slug }}_local_postgres_data_backups:/backups:z
- {{ cookiecutter.project_slug }}_local_postgres_data:/var/lib/postgresql/data
- {{ cookiecutter.project_slug }}_local_postgres_data_backups:/backups
env_file:
- ./.envs/.local/.postgres
docs:
image: {{ cookiecutter.project_slug }}_local_docs
container_name: {{ cookiecutter.project_slug }}_local_docs
platform: linux/x86_64
build:
context: .
dockerfile: ./compose/local/docs/Dockerfile
@ -107,7 +105,7 @@ services:
command: /start-flower
{%- endif %}
{%- if cookiecutter.frontend_pipeline == 'Gulp' %}
{%- if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] %}
node:
build:
@ -124,7 +122,9 @@ services:
command: npm run dev
ports:
- "3000:3000"
{%- if cookiecutter.frontend_pipeline == 'Gulp' %}
# Expose browsersync UI: https://www.browsersync.io/docs/options/#option-ui
- "3001:3001"
{%- endif %}
{%- endif %}

View File

@ -2,66 +2,25 @@ import os
from collections.abc import Sequence
from pathlib import Path
import pytest
ROOT_DIR_PATH = Path(__file__).parent.resolve()
PRODUCTION_DOTENVS_DIR_PATH = ROOT_DIR_PATH / ".envs" / ".production"
PRODUCTION_DOTENV_FILE_PATHS = [
PRODUCTION_DOTENVS_DIR_PATH / ".django",
PRODUCTION_DOTENVS_DIR_PATH / ".postgres",
BASE_DIR = Path(__file__).parent.resolve()
PRODUCTION_DOTENVS_DIR = BASE_DIR / ".envs" / ".production"
PRODUCTION_DOTENV_FILES = [
PRODUCTION_DOTENVS_DIR / ".django",
PRODUCTION_DOTENVS_DIR / ".postgres",
]
DOTENV_FILE_PATH = ROOT_DIR_PATH / ".env"
DOTENV_FILE = BASE_DIR / ".env"
def merge(
output_file_path: str, merged_file_paths: Sequence[str], append_linesep: bool = True
output_file: Path,
files_to_merge: Sequence[Path],
) -> None:
with open(output_file_path, "w") as output_file:
for merged_file_path in merged_file_paths:
with open(merged_file_path) as merged_file:
merged_file_content = merged_file.read()
output_file.write(merged_file_content)
if append_linesep:
output_file.write(os.linesep)
def main():
merge(DOTENV_FILE_PATH, PRODUCTION_DOTENV_FILE_PATHS)
@pytest.mark.parametrize("merged_file_count", range(3))
@pytest.mark.parametrize("append_linesep", [True, False])
def test_merge(tmpdir_factory, merged_file_count: int, append_linesep: bool):
tmp_dir_path = Path(str(tmpdir_factory.getbasetemp()))
output_file_path = tmp_dir_path / ".env"
expected_output_file_content = ""
merged_file_paths = []
for i in range(merged_file_count):
merged_file_ord = i + 1
merged_filename = f".service{merged_file_ord}"
merged_file_path = tmp_dir_path / merged_filename
merged_file_content = merged_filename * merged_file_ord
with open(merged_file_path, "w+") as file:
file.write(merged_file_content)
expected_output_file_content += merged_file_content
if append_linesep:
expected_output_file_content += os.linesep
merged_file_paths.append(merged_file_path)
merge(output_file_path, merged_file_paths, append_linesep)
with open(output_file_path) as output_file:
actual_output_file_content = output_file.read()
assert actual_output_file_content == expected_output_file_content
merged_content = ""
for merge_file in files_to_merge:
merged_content += merge_file.read_text()
merged_content += os.linesep
output_file.write_text(merged_content)
if __name__ == "__main__":
main()
merge(DOTENV_FILE, PRODUCTION_DOTENV_FILES)

View File

@ -1,13 +1,17 @@
{
"name": "{{cookiecutter.project_slug}}",
"version": "{{ cookiecutter.version }}",
"dependencies": {},
"devDependencies": {
"bootstrap": "^5.1.3",
"gulp-concat": "^2.6.1",
"@babel/core": "^7.16.5",
"@babel/preset-env": "^7.16.5",
"@popperjs/core": "^2.10.2",
"autoprefixer": "^10.4.0",
"babel-loader": "^9.1.2",
"bootstrap": "^5.1.3",
"browser-sync": "^2.27.7",
"css-loader": "^6.5.1",
"gulp-concat": "^2.6.1",
"concurrently": "^7.0.0",
"cssnano": "^5.0.11",
"gulp": "^4.0.2",
"gulp-imagemin": "^7.1.0",
@ -16,9 +20,19 @@
"gulp-rename": "^2.0.0",
"gulp-sass": "^5.0.0",
"gulp-uglify-es": "^3.0.0",
"mini-css-extract-plugin": "^2.4.5",
"node-sass-tilde-importer": "^1.0.2",
"pixrem": "^5.0.0",
"postcss": "^8.3.11",
"sass": "^1.43.4"
"postcss-loader": "^7.0.2",
"postcss-preset-env": "^8.0.1",
"sass": "^1.43.4",
"sass-loader": "^13.2.0",
"webpack": "^5.65.0",
"webpack-bundle-tracker": "^1.4.0",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.6.0",
"webpack-merge": "^5.8.0"
},
"engines": {
"node": "16"
@ -26,8 +40,11 @@
"browserslist": [
"last 2 versions"
],
"babel": {
"presets": ["@babel/preset-env"]
},
"scripts": {
"dev": "gulp",
"build": "gulp generate-assets"
"dev": "",
"build": ""
}
}

View File

@ -4,14 +4,33 @@ volumes:
production_postgres_data: {}
production_postgres_data_backups: {}
production_traefik: {}
{%- if cookiecutter.cloud_provider == 'None' %}
production_django_media: {}
{%- endif %}
services:
django:{% if cookiecutter.use_celery == 'y' %} &django{% endif %}
build:
context: .
dockerfile: ./compose/production/django/Dockerfile
{%- if cookiecutter.frontend_pipeline == 'Webpack' and cookiecutter.use_whitenoise == 'n' %}
args:
# These variable can be defined in an .env file in the root of the repo
{%- if cookiecutter.cloud_provider == 'AWS' %}
DJANGO_AWS_STORAGE_BUCKET_NAME: ${DJANGO_AWS_STORAGE_BUCKET_NAME}
DJANGO_AWS_S3_CUSTOM_DOMAIN: ${DJANGO_AWS_S3_CUSTOM_DOMAIN}
{%- elif cookiecutter.cloud_provider == 'GCP' %}
DJANGO_GCP_STORAGE_BUCKET_NAME: ${DJANGO_GCP_STORAGE_BUCKET_NAME}
{%- elif cookiecutter.cloud_provider == 'Azure' %}
DJANGO_AZURE_ACCOUNT_NAME: ${DJANGO_AZURE_ACCOUNT_NAME}
{%- endif %}
{%- endif %}
image: {{ cookiecutter.project_slug }}_production_django
platform: linux/x86_64
{%- if cookiecutter.cloud_provider == 'None' %}
volumes:
- production_django_media:/app/{{ cookiecutter.project_slug }}/media
{%- endif %}
depends_on:
- postgres
- redis
@ -26,8 +45,8 @@ services:
dockerfile: ./compose/production/postgres/Dockerfile
image: {{ cookiecutter.project_slug }}_production_postgres
volumes:
- production_postgres_data:/var/lib/postgresql/data:Z
- production_postgres_data_backups:/backups:z
- production_postgres_data:/var/lib/postgresql/data
- production_postgres_data_backups:/backups
env_file:
- ./.envs/.production/.postgres
@ -39,7 +58,7 @@ services:
depends_on:
- django
volumes:
- production_traefik:/etc/traefik/acme:z
- production_traefik:/etc/traefik/acme
ports:
- "0.0.0.0:80:80"
- "0.0.0.0:443:443"
@ -77,3 +96,14 @@ services:
volumes:
- production_postgres_data_backups:/backups:z
{%- endif %}
{%- if cookiecutter.cloud_provider == 'None' %}
nginx:
build:
context: .
dockerfile: ./compose/production/nginx/Dockerfile
image: {{ cookiecutter.project_slug }}_local_nginx
depends_on:
- django
volumes:
- production_django_media:/usr/share/nginx/media:ro
{%- endif %}

View File

@ -1,20 +1,20 @@
pytz==2022.6 # https://github.com/stub42/pytz
python-slugify==7.0.0 # https://github.com/un33k/python-slugify
Pillow==9.3.0 # https://github.com/python-pillow/Pillow
pytz==2022.7.1 # https://github.com/stub42/pytz
python-slugify==8.0.1 # https://github.com/un33k/python-slugify
Pillow==9.4.0 # https://github.com/python-pillow/Pillow
{%- if cookiecutter.frontend_pipeline == 'Django Compressor' %}
{%- if cookiecutter.windows == 'y' and cookiecutter.use_docker == 'n' %}
rcssmin==1.1.0 --install-option="--without-c-extensions" # https://github.com/ndparker/rcssmin
{%- else %}
rcssmin==1.1.0 # https://github.com/ndparker/rcssmin
rcssmin==1.1.1 # https://github.com/ndparker/rcssmin
{%- endif %}
{%- endif %}
argon2-cffi==21.3.0 # https://github.com/hynek/argon2_cffi
{%- if cookiecutter.use_whitenoise == 'y' %}
whitenoise==6.2.0 # https://github.com/evansd/whitenoise
whitenoise==6.4.0 # https://github.com/evansd/whitenoise
{%- endif %}
redis==4.3.5 # https://github.com/redis/redis-py
redis==4.5.1 # https://github.com/redis/redis-py
{%- if cookiecutter.use_docker == "y" or cookiecutter.windows == "n" %}
hiredis==2.0.0 # https://github.com/redis/hiredis-py
hiredis==2.2.2 # https://github.com/redis/hiredis-py
{%- endif %}
{%- if cookiecutter.use_celery == "y" %}
celery==5.2.7 # pyup: < 6.0 # https://github.com/celery/celery
@ -29,20 +29,23 @@ uvicorn[standard]==0.20.0 # https://github.com/encode/uvicorn
# Django
# ------------------------------------------------------------------------------
django==4.0.8 # pyup: < 4.1 # https://www.djangoproject.com/
django-environ==0.9.0 # https://github.com/joke2k/django-environ
django-model-utils==4.2.0 # https://github.com/jazzband/django-model-utils
django-allauth==0.51.0 # https://github.com/pennersr/django-allauth
django-crispy-forms==1.14.0 # https://github.com/django-crispy-forms/django-crispy-forms
django==4.0.10 # pyup: < 4.1 # https://www.djangoproject.com/
django-environ==0.10.0 # https://github.com/joke2k/django-environ
django-model-utils==4.3.1 # https://github.com/jazzband/django-model-utils
django-allauth==0.52.0 # https://github.com/pennersr/django-allauth
django-crispy-forms==2.0 # https://github.com/django-crispy-forms/django-crispy-forms
crispy-bootstrap5==0.7 # https://github.com/django-crispy-forms/crispy-bootstrap5
{%- if cookiecutter.frontend_pipeline == 'Django Compressor' %}
django-compressor==4.1 # https://github.com/django-compressor/django-compressor
django-compressor==4.3.1 # https://github.com/django-compressor/django-compressor
{%- endif %}
django-redis==5.2.0 # https://github.com/jazzband/django-redis
{%- if cookiecutter.use_drf == 'y' %}
# Django REST Framework
djangorestframework==3.14.0 # https://github.com/encode/django-rest-framework
django-cors-headers==3.13.0 # https://github.com/adamchainz/django-cors-headers
django-cors-headers==3.14.0 # https://github.com/adamchainz/django-cors-headers
# DRF-spectacular for api documentation
drf-spectacular==0.24.2 # https://github.com/tfranzel/drf-spectacular
drf-spectacular==0.26.0 # https://github.com/tfranzel/drf-spectacular
{%- endif %}
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
django-webpack-loader==1.8.1 # https://github.com/django-webpack/django-webpack-loader
{%- endif %}

View File

@ -1,7 +1,7 @@
-r base.txt
Werkzeug[watchdog]==2.2.2 # https://github.com/pallets/werkzeug
ipdb==0.13.9 # https://github.com/gotcha/ipdb
Werkzeug[watchdog]==2.2.3 # https://github.com/pallets/werkzeug
ipdb==0.13.13 # https://github.com/gotcha/ipdb
{%- if cookiecutter.use_docker == 'y' %}
psycopg2==2.9.5 # https://github.com/psycopg/psycopg2
{%- else %}
@ -13,36 +13,36 @@ watchfiles==0.18.1 # https://github.com/samuelcolvin/watchfiles
# Testing
# ------------------------------------------------------------------------------
mypy==0.982 # https://github.com/python/mypy
django-stubs==1.12.0 # https://github.com/typeddjango/django-stubs
pytest==7.2.0 # https://github.com/pytest-dev/pytest
mypy==1.1.1 # https://github.com/python/mypy
django-stubs==1.15.0 # https://github.com/typeddjango/django-stubs
pytest==7.2.2 # https://github.com/pytest-dev/pytest
pytest-sugar==0.9.6 # https://github.com/Frozenball/pytest-sugar
{%- if cookiecutter.use_drf == "y" %}
djangorestframework-stubs==1.7.0 # https://github.com/typeddjango/djangorestframework-stubs
djangorestframework-stubs==1.9.1 # https://github.com/typeddjango/djangorestframework-stubs
{%- endif %}
# Documentation
# ------------------------------------------------------------------------------
sphinx==5.3.0 # https://github.com/sphinx-doc/sphinx
sphinx==6.1.3 # https://github.com/sphinx-doc/sphinx
sphinx-autobuild==2021.3.14 # https://github.com/GaretJax/sphinx-autobuild
# Code quality
# ------------------------------------------------------------------------------
flake8==5.0.4 # https://github.com/PyCQA/flake8
flake8-isort==5.0.0 # https://github.com/gforcada/flake8-isort
coverage==6.5.0 # https://github.com/nedbat/coveragepy
black==22.10.0 # https://github.com/psf/black
flake8==6.0.0 # https://github.com/PyCQA/flake8
flake8-isort==6.0.0 # https://github.com/gforcada/flake8-isort
coverage==7.2.1 # https://github.com/nedbat/coveragepy
black==23.1.0 # https://github.com/psf/black
pylint-django==2.5.3 # https://github.com/PyCQA/pylint-django
{%- if cookiecutter.use_celery == 'y' %}
pylint-celery==0.3 # https://github.com/PyCQA/pylint-celery
{%- endif %}
pre-commit==2.20.0 # https://github.com/pre-commit/pre-commit
pre-commit==3.1.1 # https://github.com/pre-commit/pre-commit
# Django
# ------------------------------------------------------------------------------
factory-boy==3.2.1 # https://github.com/FactoryBoy/factory_boy
django-debug-toolbar==3.7.0 # https://github.com/jazzband/django-debug-toolbar
django-debug-toolbar==3.8.1 # https://github.com/jazzband/django-debug-toolbar
django-extensions==3.2.1 # https://github.com/django-extensions/django-extensions
django-coverage-plugin==2.0.4 # https://github.com/nedbat/django_coverage_plugin
django-coverage-plugin==3.0.0 # https://github.com/nedbat/django_coverage_plugin
pytest-django==4.5.2 # https://github.com/pytest-dev/pytest-django

View File

@ -8,35 +8,37 @@ psycopg2==2.9.5 # https://github.com/psycopg/psycopg2
Collectfast==2.2.0 # https://github.com/antonagestam/collectfast
{%- endif %}
{%- if cookiecutter.use_sentry == "y" %}
sentry-sdk==1.11.1 # https://github.com/getsentry/sentry-python
sentry-sdk==1.16.0 # https://github.com/getsentry/sentry-python
{%- endif %}
{%- if cookiecutter.use_docker == "n" and cookiecutter.windows == "y" %}
hiredis==2.0.0 # https://github.com/redis/hiredis-py
hiredis==2.2.2 # https://github.com/redis/hiredis-py
{%- endif %}
# Django
# ------------------------------------------------------------------------------
{%- if cookiecutter.cloud_provider == 'AWS' %}
django-storages[boto3]==1.13.1 # https://github.com/jschneier/django-storages
django-storages[boto3]==1.13.2 # https://github.com/jschneier/django-storages
{%- elif cookiecutter.cloud_provider == 'GCP' %}
django-storages[google]==1.13.1 # https://github.com/jschneier/django-storages
django-storages[google]==1.13.2 # https://github.com/jschneier/django-storages
{%- elif cookiecutter.cloud_provider == 'Azure' %}
django-storages[azure]==1.13.2 # https://github.com/jschneier/django-storages
{%- endif %}
{%- if cookiecutter.mail_service == 'Mailgun' %}
django-anymail[mailgun]==8.6 # https://github.com/anymail/django-anymail
django-anymail[mailgun]==9.0 # https://github.com/anymail/django-anymail
{%- elif cookiecutter.mail_service == 'Amazon SES' %}
django-anymail[amazon_ses]==8.6 # https://github.com/anymail/django-anymail
django-anymail[amazon_ses]==9.0 # https://github.com/anymail/django-anymail
{%- elif cookiecutter.mail_service == 'Mailjet' %}
django-anymail[mailjet]==8.6 # https://github.com/anymail/django-anymail
django-anymail[mailjet]==9.0 # https://github.com/anymail/django-anymail
{%- elif cookiecutter.mail_service == 'Mandrill' %}
django-anymail[mandrill]==8.6 # https://github.com/anymail/django-anymail
django-anymail[mandrill]==9.0 # https://github.com/anymail/django-anymail
{%- elif cookiecutter.mail_service == 'Postmark' %}
django-anymail[postmark]==8.6 # https://github.com/anymail/django-anymail
django-anymail[postmark]==9.0 # https://github.com/anymail/django-anymail
{%- elif cookiecutter.mail_service == 'Sendgrid' %}
django-anymail[sendgrid]==8.6 # https://github.com/anymail/django-anymail
django-anymail[sendgrid]==9.0 # https://github.com/anymail/django-anymail
{%- elif cookiecutter.mail_service == 'SendinBlue' %}
django-anymail[sendinblue]==8.6 # https://github.com/anymail/django-anymail
django-anymail[sendinblue]==9.0 # https://github.com/anymail/django-anymail
{%- elif cookiecutter.mail_service == 'SparkPost' %}
django-anymail[sparkpost]==8.6 # https://github.com/anymail/django-anymail
django-anymail[sparkpost]==9.0 # https://github.com/anymail/django-anymail
{%- elif cookiecutter.mail_service == 'Other SMTP' %}
django-anymail==8.6 # https://github.com/anymail/django-anymail
django-anymail==9.0 # https://github.com/anymail/django-anymail
{%- endif %}

View File

@ -1,10 +1,10 @@
[flake8]
max-line-length = 120
exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules,venv
exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules,venv,.venv
[pycodestyle]
max-line-length = 120
exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules,venv
exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules,venv,.venv
[isort]
line_length = 88
@ -34,7 +34,7 @@ django_settings_module = config.settings.test
ignore_errors = True
[coverage:run]
include = {{cookiecutter.project_slug}}/*
omit = *migrations*, *tests*
include = {{cookiecutter.project_slug}}/**
omit = */migrations/*, */tests/*
plugins =
django_coverage_plugin

View File

@ -0,0 +1,34 @@
from pathlib import Path
import pytest
from merge_production_dotenvs_in_dotenv import merge
@pytest.mark.parametrize(
("input_contents", "expected_output"),
[
([], ""),
([""], "\n"),
(["JANE=doe"], "JANE=doe\n"),
(["SEP=true", "AR=ator"], "SEP=true\nAR=ator\n"),
(["A=0", "B=1", "C=2"], "A=0\nB=1\nC=2\n"),
(["X=x\n", "Y=y", "Z=z\n"], "X=x\n\nY=y\nZ=z\n\n"),
],
)
def test_merge(
tmp_path: Path,
input_contents: list[str],
expected_output: str,
):
output_file = tmp_path / ".env"
files_to_merge = []
for num, input_content in enumerate(input_contents, start=1):
merge_file = tmp_path / f".service{num}"
merge_file.write_text(input_content)
files_to_merge.append(merge_file)
merge(output_file, files_to_merge)
assert output_file.read_text() == expected_output

View File

@ -0,0 +1,55 @@
const path = require('path');
const BundleTracker = require('webpack-bundle-tracker');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
target: "web",
context: path.join(__dirname, '../'),
entry: {
'project': path.resolve(__dirname, '../{{cookiecutter.project_slug}}/static/js/project'),
'vendors': path.resolve(__dirname, '../{{cookiecutter.project_slug}}/static/js/vendors'),
},
output: {
path: path.resolve(__dirname, '../{{cookiecutter.project_slug}}/static/webpack_bundles/'),
publicPath: '/static/webpack_bundles/',
filename: 'js/[name]-[fullhash].js',
chunkFilename: 'js/[name]-[hash].js',
},
plugins: [
new BundleTracker({filename: path.resolve(__dirname, '../webpack-stats.json')}),
new MiniCssExtractPlugin({ filename: 'css/[name].[contenthash].css' }),
],
module: {
rules: [
// we pass the output from babel loader to react-hot loader
{
test: /\.js$/,
loader: 'babel-loader',
},
{
test: /\.s?css$/i,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
'postcss-preset-env',
'autoprefixer',
'pixrem',
],
},
},
},
'sass-loader',
],
},
],
},
resolve: {
modules: ['node_modules'],
extensions: ['.js', '.jsx'],
},
};

View File

@ -0,0 +1,20 @@
const { merge } = require('webpack-merge');
const commonConfig = require('./common.config');
module.exports = merge(commonConfig, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
port: 3000,
proxy: {
{%- if cookiecutter.use_docker == 'n' %}
'/': 'http://0.0.0.0:8000',
{%- else %}
'/': 'http://django:8000',
{%- endif %}
},
// We need hot=false (Disable HMR) to set liveReload=true
hot: false,
liveReload: true,
},
});

View File

@ -0,0 +1,28 @@
const { merge } = require('webpack-merge');
const commonConfig = require('./common.config');
// This variable should mirror the one from config/settings/production.py
{%- if cookiecutter.use_whitenoise == 'n' %}
{%- if cookiecutter.cloud_provider == 'AWS' %}
const s3BucketName = process.env.DJANGO_AWS_STORAGE_BUCKET_NAME;
const awsS3Domain = process.env.DJANGO_AWS_S3_CUSTOM_DOMAIN ?
process.env.DJANGO_AWS_S3_CUSTOM_DOMAIN
: `${s3BucketName}.s3.amazonaws.com`;
const staticUrl = `https://${awsS3Domain}/static/`;
{%- elif cookiecutter.cloud_provider == 'GCP' %}
const staticUrl = `https://storage.googleapis.com/${process.env.DJANGO_GCP_STORAGE_BUCKET_NAME}/static/`;
{%- elif cookiecutter.cloud_provider == 'Azure' %}
const staticUrl = `https://${process.env.DJANGO_AZURE_ACCOUNT_NAME}.blob.core.windows.net/static/`;
{%- endif %}
{%- else %}
const staticUrl = '/static/';
{%- endif %}
module.exports = merge(commonConfig, {
mode: 'production',
devtool: 'source-map',
bail: true,
output: {
publicPath: `${staticUrl}webpack_bundles/`,
},
});

View File

@ -1 +1,5 @@
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
import '../sass/project.scss';
{%- endif %}
/* Project specific Javascript goes here. */

View File

@ -0,0 +1,2 @@
import '@popperjs/core';
import 'bootstrap';

View File

@ -1,5 +1,5 @@
@import "custom_bootstrap_vars";
@import "bootstrap";
@import "~bootstrap/scss/bootstrap";
// project specific CSS goes here

View File

@ -1,4 +1,8 @@
{% raw %}{% load static i18n {% endraw %}{% if cookiecutter.frontend_pipeline == 'Django Compressor' %}compress{% endif %}{% raw %}%}<!DOCTYPE html>
{% raw %}{% load static i18n {% endraw %}
{%- if cookiecutter.frontend_pipeline == 'Django Compressor' %}compress
{%- endif %}{% raw %}%}{% endraw %}
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}{% raw %}{% load render_bundle from webpack_loader %}{% endraw %}
{%- endif %}{% raw %}<!DOCTYPE html>
{% get_current_language as LANGUAGE_CODE %}
<html lang="{{ LANGUAGE_CODE }}">
<head>
@ -13,7 +17,7 @@
{% block css %}
{%- endraw %}
{%- if cookiecutter.frontend_pipeline != 'Gulp' %}
{%- if cookiecutter.frontend_pipeline in ['None', 'Django Compressor'] %}
{%- raw %}
<!-- Latest compiled and minified Bootstrap CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/css/bootstrap.min.css" integrity="sha512-GQGU0fMMi238uA+a/bdWJfpUGKUkBdgfFdgBm72SUQ6BeyWjoY/ton0tEjH+OSH9iP4Dfh+7HM0I9f5eR0L/4w==" crossorigin="anonymous" referrerpolicy="no-referrer" />
@ -31,6 +35,8 @@
{% endcompress %}
{%- endraw %}{% elif cookiecutter.frontend_pipeline == 'Gulp' %}{% raw %}
<link href="{% static 'css/project.min.css' %}" rel="stylesheet">
{%- endraw %}{% elif cookiecutter.frontend_pipeline == "Webpack" %}{% raw %}
{% render_bundle 'project' 'css' %}
{%- endraw %}{% endif %}{% raw %}
{% endblock %}
<!-- Le javascript
@ -38,8 +44,11 @@
{# Placed at the top of the document so pages load faster with defer #}
{% block javascript %}
{%- endraw %}{% if cookiecutter.frontend_pipeline == 'Gulp' %}{% raw %}
<!-- Vendor dependencies bundled as one file-->
<!-- Vendor dependencies bundled as one file -->
<script defer src="{% static 'js/vendors.min.js' %}"></script>
{%- endraw %}{% elif cookiecutter.frontend_pipeline == "Webpack" %}{% raw %}
<!-- Vendor dependencies bundled as one file -->
{% render_bundle 'vendors' 'js' attrs='defer' %}
{%- endraw %}{% else %}{% raw %}
<!-- Bootstrap JS -->
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.min.js" integrity="sha512-OvBgP9A2JBgiRad/mM36mkzXSXaJE9BEIENnVEmeZdITvwT09xnxLtT4twkCa8m/loMbPHsvPl0T8lRGVBwjlQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
@ -55,6 +64,8 @@
{% endcompress %}
{%- endraw %}{% elif cookiecutter.frontend_pipeline == 'Gulp' %}{% raw %}
<script defer src="{% static 'js/project.min.js' %}"></script>
{%- endraw %}{% elif cookiecutter.frontend_pipeline == "Webpack" %}{% raw %}
{% render_bundle 'project' 'js' attrs='defer' %}
{%- endraw %}{% endif %}{% raw %}
{% endblock javascript %}

View File

@ -10,7 +10,6 @@ User = get_user_model()
@admin.register(User)
class UserAdmin(auth_admin.UserAdmin):
form = UserAdminChangeForm
add_form = UserAdminCreationForm
fieldsets = (

View File

@ -1,27 +1,32 @@
from django.test import RequestFactory
import pytest
from rest_framework.test import APIRequestFactory
from {{ cookiecutter.project_slug }}.users.api.views import UserViewSet
from {{ cookiecutter.project_slug }}.users.models import User
class TestUserViewSet:
def test_get_queryset(self, user: User, rf: RequestFactory):
@pytest.fixture
def api_rf(self) -> APIRequestFactory:
return APIRequestFactory()
def test_get_queryset(self, user: User, api_rf: APIRequestFactory):
view = UserViewSet()
request = rf.get("/fake-url/")
request = api_rf.get("/fake-url/")
request.user = user
view.request = request
assert user in view.get_queryset()
def test_me(self, user: User, rf: RequestFactory):
def test_me(self, user: User, api_rf: APIRequestFactory):
view = UserViewSet()
request = rf.get("/fake-url/")
request = api_rf.get("/fake-url/")
request.user = user
view.request = request
response = view.me(request)
response = view.me(request) # type: ignore
assert response.data == {
{% if cookiecutter.username_type == "email" -%}

View File

@ -9,7 +9,6 @@ User = get_user_model()
class UserDetailView(LoginRequiredMixin, DetailView):
model = User
{%- if cookiecutter.username_type == "email" -%}
slug_field = "id"
@ -24,7 +23,6 @@ user_detail_view = UserDetailView.as_view()
class UserUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
model = User
fields = ["name"]
success_message = _("Information successfully updated")
@ -43,7 +41,6 @@ user_update_view = UserUpdateView.as_view()
class UserRedirectView(LoginRequiredMixin, RedirectView):
permanent = False
def get_redirect_url(self):

View File

@ -22,4 +22,15 @@ class StaticRootGoogleCloudStorage(GoogleCloudStorage):
class MediaRootGoogleCloudStorage(GoogleCloudStorage):
location = "media"
file_overwrite = False
{%- elif cookiecutter.cloud_provider == 'Azure' -%}
from storages.backends.azure_storage import AzureStorage
class StaticRootAzureStorage(AzureStorage):
location = "static"
class MediaRootAzureStorage(AzureStorage):
location = "media"
file_overwrite = False
{%- endif %}