diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 53a486671..23ca7a37f 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -2,11 +2,4 @@ github: [pydanny, browniebroke] patreon: feldroy -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -custom: ["https://www.patreon.com/browniebroke"] +open_collective: cookiecutter-django diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md index 0e5ec12c4..da0480f1e 100644 --- a/.github/ISSUE_TEMPLATE/bug.md +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -42,7 +42,7 @@ labels: bug - Python version, run `python3 -V`: - Docker version (if using Docker), run `docker --version`: - - docker-compose version (if using Docker), run `docker-compose --version`: + - docker compose version (if using Docker), run `docker compose --version`: - ... - Options selected and/or [replay file](https://cookiecutter.readthedocs.io/en/latest/advanced/replay.html): diff --git a/.github/contributors.json b/.github/contributors.json index 13c4eafd3..48e9faad3 100644 --- a/.github/contributors.json +++ b/.github/contributors.json @@ -53,6 +53,12 @@ "twitter_username": "sfdye", "is_core": true }, + { + "name": "Jelmer Draaijer", + "github_login": "foarsitter", + "twitter_username": "", + "is_core": true + }, { "name": "18", "github_login": "dezoito", @@ -553,11 +559,6 @@ "github_login": "jvanbrug", "twitter_username": "" }, - { - "name": "Jelmer Draaijer", - "github_login": "foarsitter", - "twitter_username": "" - }, { "name": "Jerome Caisip", "github_login": "jeromecaisip", @@ -1397,5 +1398,60 @@ "name": "Matheus Jardim Bernardes", "github_login": "matheusjardimb", "twitter_username": "" + }, + { + "name": "masavini", + "github_login": "masavini", + "twitter_username": "" + }, + { + "name": "Joseph Hanna", + "github_login": "sanchimenea", + "twitter_username": "" + }, + { + "name": "tmajerech", + "github_login": "tmajerech", + "twitter_username": "" + }, + { + "name": "villancikos", + "github_login": "villancikos", + "twitter_username": "" + }, + { + "name": "Imran Rahman", + "github_login": "infraredCoding", + "twitter_username": "" + }, + { + "name": "hleroy", + "github_login": "hleroy", + "twitter_username": "" + }, + { + "name": "Shayan Karimi", + "github_login": "shywn-mrk", + "twitter_username": "shywn_mrk" + }, + { + "name": "Sadra Yahyapour", + "github_login": "lnxpy", + "twitter_username": "lnxpylnxpy" + }, + { + "name": "Tharushan", + "github_login": "Tharushan", + "twitter_username": "" + }, + { + "name": "Fateme Fouladkar", + "github_login": "FatemeFouladkar", + "twitter_username": "" + }, + { + "name": "zhaoruibing", + "github_login": "zhaoruibing", + "twitter_username": "" } ] \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2c7876d3c..1c7afbd52 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,20 +17,20 @@ repos: - id: detect-private-key - repo: https://github.com/pre-commit/mirrors-prettier - rev: "v3.0.0-alpha.9-for-vscode" + rev: "v3.0.1" hooks: - id: prettier args: ["--tab-width", "2"] - repo: https://github.com/asottile/pyupgrade - rev: v3.4.0 + rev: v3.10.1 hooks: - id: pyupgrade args: [--py311-plus] exclude: hooks/ - repo: https://github.com/psf/black - rev: 23.3.0 + rev: 23.7.0 hooks: - id: black @@ -40,7 +40,7 @@ repos: - id: isort - repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 + rev: 6.1.0 hooks: - id: flake8 diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b56dc7c2..70c5c84d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,363 @@ All enhancements and patches to Cookiecutter Django will be documented in this f +## 2023.08.10 + + +### Fixed + +- Corrected 'or' translation to pt-br ([#4507](https://github.com/cookiecutter/cookiecutter-django/pull/4507)) + +## 2023.08.04 + + +### Updated + +- Auto-update pre-commit hooks ([#4503](https://github.com/cookiecutter/cookiecutter-django/pull/4503)) + +## 2023.08.01 + + +### Updated + +- Auto-update pre-commit hooks ([#4499](https://github.com/cookiecutter/cookiecutter-django/pull/4499)) + +- Update django-anymail to 10.1 ([#4497](https://github.com/cookiecutter/cookiecutter-django/pull/4497)) + +- Update sentry-sdk to 1.29.2 ([#4496](https://github.com/cookiecutter/cookiecutter-django/pull/4496)) + +- Update django to 4.2.4 ([#4495](https://github.com/cookiecutter/cookiecutter-django/pull/4495)) + +- Update flake8 to 6.1.0 ([#4489](https://github.com/cookiecutter/cookiecutter-django/pull/4489)) + +- Update uvicorn to 0.23.2 ([#4490](https://github.com/cookiecutter/cookiecutter-django/pull/4490)) + +- Update sentry-sdk to 1.29.1 ([#4494](https://github.com/cookiecutter/cookiecutter-django/pull/4494)) + +## 2023.07.30 + + +### Fixed + +- Fix `README.md` file extension in `setup.py` ([#4488](https://github.com/cookiecutter/cookiecutter-django/pull/4488)) + +## 2023.07.28 + + +### Changed + +- Add support for Drone CI ([#4382](https://github.com/cookiecutter/cookiecutter-django/pull/4382)) + +## 2023.07.27 + + +### Documentation + +- Document that `docker exec` does not work for running management commands ([#4487](https://github.com/cookiecutter/cookiecutter-django/pull/4487)) + +- Add Webpack instructions for developping locally with HTTPS ([#4486](https://github.com/cookiecutter/cookiecutter-django/pull/4486)) + +## 2023.07.25 + + +### Updated + +- Upgrade to traefik 2.10.4 ([#4483](https://github.com/cookiecutter/cookiecutter-django/pull/4483)) + +## 2023.07.24 + + +### Fixed + +- Add missing custom CRSF error page in prod ([#4464](https://github.com/cookiecutter/cookiecutter-django/pull/4464)) + +### Documentation + +- Replace `docker-compose` by `docker compose` in docs ([#4463](https://github.com/cookiecutter/cookiecutter-django/pull/4463)) + +### Updated + +- Update drf-spectacular to 0.26.4 ([#4481](https://github.com/cookiecutter/cookiecutter-django/pull/4481)) + +## 2023.07.20 + + +### Updated + +- Update djlint to 1.32.1 ([#4475](https://github.com/cookiecutter/cookiecutter-django/pull/4475)) + +## 2023.07.19 + + +### Updated + +- Update factory-boy to 3.3.0 ([#4472](https://github.com/cookiecutter/cookiecutter-django/pull/4472)) + +- Update gunicorn to 21.2.0 ([#4473](https://github.com/cookiecutter/cookiecutter-django/pull/4473)) + +- Update djlint to 1.32.0 ([#4471](https://github.com/cookiecutter/cookiecutter-django/pull/4471)) + +## 2023.07.18 + + +### Updated + +- Update gunicorn to 21.1.0 ([#4470](https://github.com/cookiecutter/cookiecutter-django/pull/4470)) + +- Update uvicorn to 0.23.1 ([#4468](https://github.com/cookiecutter/cookiecutter-django/pull/4468)) + +- Update gunicorn to 21.0.1 ([#4466](https://github.com/cookiecutter/cookiecutter-django/pull/4466)) + +## 2023.07.13 + + +### Updated + +- Update sentry-sdk to 1.28.1 ([#4458](https://github.com/cookiecutter/cookiecutter-django/pull/4458)) + +## 2023.07.11 + + +### Changed + +- Improve type hints for `UserSerializer` ([#4429](https://github.com/cookiecutter/cookiecutter-django/pull/4429)) + +- [pre-commit.ci] pre-commit autoupdate ([#4453](https://github.com/cookiecutter/cookiecutter-django/pull/4453)) + +### Fixed + +- Fix `/tmp` bind mount in devcontainer config ([#4455](https://github.com/cookiecutter/cookiecutter-django/pull/4455)) + +### Updated + +- Update black to 23.7.0 ([#4452](https://github.com/cookiecutter/cookiecutter-django/pull/4452)) + +## 2023.07.10 + + +### Fixed + +- Prevent user's name being shown twice on user details page if username is set to email ([#4436](https://github.com/cookiecutter/cookiecutter-django/pull/4436)) + +- Add missing trailing space in `EMAIL_SUBJECT_PREFIX` setting ([#4434](https://github.com/cookiecutter/cookiecutter-django/pull/4434)) + +### Documentation + +- Clarify documentation on which port to use to access the application when using Webpack or Gulp ([#4413](https://github.com/cookiecutter/cookiecutter-django/pull/4413)) + +### Updated + +- Update django-coverage-plugin to 3.1.0 ([#4446](https://github.com/cookiecutter/cookiecutter-django/pull/4446)) + +- Update pillow to 10.0.0 ([#4432](https://github.com/cookiecutter/cookiecutter-django/pull/4432)) + +- Update django-cors-headers to 4.2.0 ([#4445](https://github.com/cookiecutter/cookiecutter-django/pull/4445)) + +- Update sentry-sdk to 1.28.0 ([#4444](https://github.com/cookiecutter/cookiecutter-django/pull/4444)) + +## 2023.07.09 + + +### Fixed + +- Fix missing run configurations when PyCharm is selected ([#4441](https://github.com/cookiecutter/cookiecutter-django/pull/4441)) + +## 2023.07.08 + + +### Updated + +- Update sentry-sdk to 1.27.1 ([#4440](https://github.com/cookiecutter/cookiecutter-django/pull/4440)) + +## 2023.07.04 + + +### Changed + +- Add PostgreSQL 15 ([#4431](https://github.com/cookiecutter/cookiecutter-django/pull/4431)) + +- [pre-commit.ci] pre-commit autoupdate ([#4438](https://github.com/cookiecutter/cookiecutter-django/pull/4438)) + +### Updated + +- Update sentry-sdk to 1.27.0 ([#4439](https://github.com/cookiecutter/cookiecutter-django/pull/4439)) + +- Update postcss-preset-env to 9.0.0 ([#4437](https://github.com/cookiecutter/cookiecutter-django/pull/4437)) + +## 2023.07.03 + + +### Changed + +- Add a devcontainer configuration with Docker ([#4198](https://github.com/cookiecutter/cookiecutter-django/pull/4198)) + +### Updated + +- Update django-stubs to 4.2.3 ([#4430](https://github.com/cookiecutter/cookiecutter-django/pull/4430)) + +- Update django to 4.2.3 ([#4435](https://github.com/cookiecutter/cookiecutter-django/pull/4435)) + +## 2023.06.30 + + +### Changed + +- Add option to use django-allauth workflow in the admin ([#1921](https://github.com/cookiecutter/cookiecutter-django/pull/1921)) + +## 2023.06.29 + + +### Changed + +- Replace psycopg2 by psycopg3 ([#4421](https://github.com/cookiecutter/cookiecutter-django/pull/4421)) + +## 2023.06.28 + + +### Changed + +- Upgrade to django 4.2 ([#4393](https://github.com/cookiecutter/cookiecutter-django/pull/4393)) + +### Fixed + +- Fix PostgreSQL version in GitHub workflow ([#4423](https://github.com/cookiecutter/cookiecutter-django/pull/4423)) + +### Updated + +- Update werkzeug to 2.3.6 ([#4427](https://github.com/cookiecutter/cookiecutter-django/pull/4427)) + +- Update django-compressor to 4.4 ([#4422](https://github.com/cookiecutter/cookiecutter-django/pull/4422)) + +## 2023.06.27 + + +### Changed + +- Populate User `name` field during social auth ([#3968](https://github.com/cookiecutter/cookiecutter-django/pull/3968)) + +- Add djLint for HTML formatting and linting ([#4389](https://github.com/cookiecutter/cookiecutter-django/pull/4389)) + +### Fixed + +- Only include prettier pre-commit hook with node-based front-end pipeline ([#4418](https://github.com/cookiecutter/cookiecutter-django/pull/4418)) + +### Updated + +- Update djangorestframework-stubs to 3.14.2 ([#4420](https://github.com/cookiecutter/cookiecutter-django/pull/4420)) + +- Update django-stubs to 4.2.2 ([#4419](https://github.com/cookiecutter/cookiecutter-django/pull/4419)) + +## 2023.06.26 + + +### Updated + +- Update pytest to 7.4.0 ([#4412](https://github.com/cookiecutter/cookiecutter-django/pull/4412)) + +- Update redis to 4.6.0 ([#4415](https://github.com/cookiecutter/cookiecutter-django/pull/4415)) + +- Update mypy to 1.4.1 ([#4416](https://github.com/cookiecutter/cookiecutter-django/pull/4416)) + +## 2023.06.22 + + +### Updated + +- Update pygithub to 1.59.0 ([#4410](https://github.com/cookiecutter/cookiecutter-django/pull/4410)) + +- Update drf-spectacular to 0.26.3 ([#4411](https://github.com/cookiecutter/cookiecutter-django/pull/4411)) + +- Update sentry-sdk to 1.26.0 ([#4409](https://github.com/cookiecutter/cookiecutter-django/pull/4409)) + +## 2023.06.21 + + +### Updated + +- Upgrade traefik to 2.10.3 ([#4408](https://github.com/cookiecutter/cookiecutter-django/pull/4408)) + +## 2023.06.19 + + +### Updated + +- Auto-update pre-commit hooks ([#4405](https://github.com/cookiecutter/cookiecutter-django/pull/4405)) + +- Update celery to 5.3.1 ([#4404](https://github.com/cookiecutter/cookiecutter-django/pull/4404)) + +## 2023.06.18 + + +### Changed + +- Fix missing celery env variable when running compilemessages ([#4403](https://github.com/cookiecutter/cookiecutter-django/pull/4403)) + +### Updated + +- Update flower to 2.0.0 ([#4402](https://github.com/cookiecutter/cookiecutter-django/pull/4402)) + +## 2023.06.17 + + +## 2023.06.16 + + +### Updated + +- Update whitenoise to 6.5.0 ([#4400](https://github.com/cookiecutter/cookiecutter-django/pull/4400)) + +- Update django-redis to 5.3.0 ([#4399](https://github.com/cookiecutter/cookiecutter-django/pull/4399)) + +- Auto-update pre-commit hooks ([#4395](https://github.com/cookiecutter/cookiecutter-django/pull/4395)) + +## 2023.06.14 + + +### Updated + +- Update django-cors-headers to 4.1.0 ([#4391](https://github.com/cookiecutter/cookiecutter-django/pull/4391)) + +- Update django-upgrade to 1.14.0 ([#4394](https://github.com/cookiecutter/cookiecutter-django/pull/4394)) + +- Update django-webpack-loader to 2.0.1 ([#4392](https://github.com/cookiecutter/cookiecutter-django/pull/4392)) + +- Update pre-commit to 3.3.3 ([#4390](https://github.com/cookiecutter/cookiecutter-django/pull/4390)) + +## 2023.06.11 + + +### Updated + +- Update pytest to 7.3.2 ([#4384](https://github.com/cookiecutter/cookiecutter-django/pull/4384)) + +- Auto-update pre-commit hooks ([#4385](https://github.com/cookiecutter/cookiecutter-django/pull/4385)) + +## 2023.06.09 + + +### Fixed + +- Fix missing `compilemessages` step before deploying to prod ([#4363](https://github.com/cookiecutter/cookiecutter-django/pull/4363)) + +## 2023.06.08 + + +### Fixed + +- Fix failure in user view test caused by translations ([#4374](https://github.com/cookiecutter/cookiecutter-django/pull/4374)) + +### Updated + +- Update to Python 3.11.4 in production Docker compose ([#4378](https://github.com/cookiecutter/cookiecutter-django/pull/4378)) + +- Update to Python 3.11.4 in docs Docker compose ([#4379](https://github.com/cookiecutter/cookiecutter-django/pull/4379)) + +- Update to Python 3.11.4 in local Docker compose ([#4380](https://github.com/cookiecutter/cookiecutter-django/pull/4380)) + +- Update celery to 5.3.0 ([#4369](https://github.com/cookiecutter/cookiecutter-django/pull/4369)) + +- Update werkzeug to 2.3.5 ([#4377](https://github.com/cookiecutter/cookiecutter-django/pull/4377)) + ## 2023.06.07 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 4e2f1fb31..38fc87e9b 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -74,6 +74,13 @@ accept and merge pull requests. sfdye + + Jelmer Draaijer + + foarsitter + + + _Audrey is also the creator of Cookiecutter. Audrey and Daniel are on @@ -789,6 +796,13 @@ Listed in alphabetical order. fabaff + + Fateme Fouladkar + + FatemeFouladkar + + + Felipe Arruda @@ -929,6 +943,13 @@ Listed in alphabetical order. + + hleroy + + hleroy + + + Hoai-Thu Vuong @@ -950,6 +971,13 @@ Listed in alphabetical order. + + Imran Rahman + + infraredCoding + + + innicoder @@ -1006,13 +1034,6 @@ Listed in alphabetical order. - - Jelmer Draaijer - - foarsitter - - - Jens Nilsson @@ -1069,6 +1090,13 @@ Listed in alphabetical order. + + Joseph Hanna + + sanchimenea + + + jugglinmike @@ -1307,6 +1335,13 @@ Listed in alphabetical order. + + masavini + + masavini + + + Mateusz Ostaszewski @@ -1671,6 +1706,13 @@ Listed in alphabetical order. + + Sadra Yahyapour + + lnxpy + + lnxpylnxpy + Sam Collins @@ -1692,6 +1734,13 @@ Listed in alphabetical order. sebastianreyese + + Shayan Karimi + + shywn-mrk + + shywn_mrk + Simon Rey @@ -1776,6 +1825,13 @@ Listed in alphabetical order. + + Tharushan + + Tharushan + + + Thibault J. @@ -1825,6 +1881,13 @@ Listed in alphabetical order. + + tmajerech + + tmajerech + + + Tom Atkins @@ -1888,6 +1951,13 @@ Listed in alphabetical order. + + villancikos + + villancikos + + + Vitaly Babiy @@ -1972,6 +2042,13 @@ Listed in alphabetical order. + + zhaoruibing + + zhaoruibing + + + ### Special Thanks diff --git a/README.md b/README.md index d718ee34c..7f2c358e7 100644 --- a/README.md +++ b/README.md @@ -19,11 +19,11 @@ production-ready Django projects quickly. ## Features -- For Django 4.1 +- For Django 4.2 - Works with Python 3.11 - Renders Django projects with 100% starting test coverage - Twitter [Bootstrap](https://github.com/twbs/bootstrap) v5 -- [12-Factor](http://12factor.net/) based settings via [django-environ](https://github.com/joke2k/django-environ) +- [12-Factor](https://12factor.net) based settings via [django-environ](https://github.com/joke2k/django-environ) - Secure by default. We believe in SSL. - Optimized development and production settings - Registration via [django-allauth](https://github.com/pennersr/django-allauth) @@ -51,15 +51,16 @@ _These features can be enabled during initial project setup._ ## Constraints - Only maintained 3rd party libraries are used. -- Uses PostgreSQL everywhere: 10.19 - 14.1 ([MySQL fork](https://github.com/mabdullahadeel/cookiecutter-django-mysql) also available). +- Uses PostgreSQL everywhere: 10 - 15 ([MySQL fork](https://github.com/mabdullahadeel/cookiecutter-django-mysql) also available). - Environment variables for configuration (This won't work with Apache/mod_wsgi). ## Support this Project! -This project is run by volunteers. Please support them in their efforts to maintain and improve Cookiecutter Django: +This project is an open source project run by volunteers. You can sponsor us via [OpenCollective](https://opencollective.com/cookiecutter-django) or individually via GitHub Sponsors: - Daniel Roy Greenfeld, Project Lead ([GitHub](https://github.com/pydanny), [Patreon](https://www.patreon.com/danielroygreenfeld)): expertise in Django and AWS ELB. - Fabio C. Barrionuevo, Core Developer ([GitHub](https://github.com/luzfcb)): expertise in Python/Django, hands-on DevOps and frontend experience. +- Bruno Alla, Core Developer ([GitHub](https://github.com/browniebroke)): expertise in Python/Django and DevOps. - Nikita Shupeyko, Core Developer ([GitHub](https://github.com/webyneter)): expertise in Python/Django, hands-on DevOps and frontend experience. Projects that provide financial support to the maintainers: @@ -125,14 +126,19 @@ Answer the prompts with your own desired [options](http://cookiecutter-django.re Choose from 1, 2 [1]: 1 timezone [UTC]: America/Los_Angeles windows [n]: n - use_pycharm [n]: y + Select an editor to use. The choices are: + 1 - None + 2 - PyCharm + 3 - VS Code + Choose from 1, 2, 3 [1]: 1 use_docker [n]: n Select postgresql_version: - 1 - 14 - 2 - 13 - 3 - 12 - 4 - 11 - 5 - 10 + 1 - 15 + 2 - 14 + 3 - 13 + 4 - 12 + 5 - 11 + 6 - 10 Choose from 1, 2, 3, 4, 5 [1]: 1 Select cloud_provider: 1 - AWS @@ -241,6 +247,7 @@ experience better. ## Articles +- [How to Make Your Own Django Cookiecutter Template!](https://medium.com/@FatemeFouladkar/how-to-make-your-own-django-cookiecutter-template-a753d4cbb8c2) - Aug. 10, 2023 - [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 diff --git a/cookiecutter.json b/cookiecutter.json index 970a53795..f66e281f5 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -16,9 +16,9 @@ "username_type": ["username", "email"], "timezone": "UTC", "windows": "n", - "use_pycharm": "n", + "editor": ["None", "PyCharm", "VS Code"], "use_docker": "n", - "postgresql_version": ["14", "13", "12", "11", "10"], + "postgresql_version": ["15", "14", "13", "12", "11", "10"], "cloud_provider": ["AWS", "GCP", "Azure", "None"], "mail_service": [ "Mailgun", @@ -39,7 +39,7 @@ "use_sentry": "n", "use_whitenoise": "n", "use_heroku": "n", - "ci_tool": ["None", "Travis", "Gitlab", "Github"], + "ci_tool": ["None", "Travis", "Gitlab", "Github", "Drone"], "keep_local_envs_in_vcs": "y", "debug": "n" } diff --git a/docs/deployment-with-docker.rst b/docs/deployment-with-docker.rst index c1b8c6d7b..3d2f9f813 100644 --- a/docs/deployment-with-docker.rst +++ b/docs/deployment-with-docker.rst @@ -1,7 +1,7 @@ Deployment with Docker ====================== -.. index:: deployment, docker, docker-compose, compose +.. index:: deployment, docker, docker compose, compose Prerequisites @@ -89,7 +89,7 @@ You can read more about this feature and how to configure it, at `Automatic HTTP 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: +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`` @@ -107,7 +107,7 @@ To solve this, you can either: 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``. + 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. @@ -122,42 +122,42 @@ Building & Running Production Stack You will need to build the stack first. To do that, run:: - docker-compose -f production.yml build + docker compose -f production.yml build Once this is ready, you can run it with:: - docker-compose -f production.yml up + docker compose -f production.yml up To run the stack and detach the containers, run:: - docker-compose -f production.yml up -d + docker compose -f production.yml up -d To run a migration, open up a second terminal and run:: - docker-compose -f production.yml run --rm django python manage.py migrate + docker compose -f production.yml run --rm django python manage.py migrate To create a superuser, run:: - docker-compose -f production.yml run --rm django python manage.py createsuperuser + docker compose -f production.yml run --rm django python manage.py createsuperuser If you need a shell, run:: - docker-compose -f production.yml run --rm django python manage.py shell + docker compose -f production.yml run --rm django python manage.py shell To check the logs out, run:: - docker-compose -f production.yml logs + docker compose -f production.yml logs If you want to scale your application, run:: - docker-compose -f production.yml up --scale django=4 - docker-compose -f production.yml up --scale celeryworker=2 + docker compose -f production.yml up --scale django=4 + docker compose -f production.yml up --scale celeryworker=2 .. warning:: don't try to scale ``postgres``, ``celerybeat``, or ``traefik``. To see how your containers are doing run:: - docker-compose -f production.yml ps + docker compose -f production.yml ps Example: Supervisor @@ -165,12 +165,12 @@ Example: Supervisor Once you are ready with your initial setup, you want to make sure that your application is run by a process manager to survive reboots and auto restarts in case of an error. You can use the process manager you are most familiar with. All -it needs to do is to run ``docker-compose -f production.yml up`` in your projects root directory. +it needs to do is to run ``docker compose -f production.yml up`` in your projects root directory. If you are using ``supervisor``, you can use this file as a starting point:: [program:{{cookiecutter.project_slug}}] - command=docker-compose -f production.yml up + command=docker compose -f production.yml up directory=/path/to/{{cookiecutter.project_slug}} redirect_stderr=true autostart=true diff --git a/docs/developing-locally-docker.rst b/docs/developing-locally-docker.rst index 3dbe6e47d..6af4fe7c2 100644 --- a/docs/developing-locally-docker.rst +++ b/docs/developing-locally-docker.rst @@ -32,7 +32,7 @@ Build the Stack This can take a while, especially the first time you run this particular command on your development system:: - $ docker-compose -f local.yml build + $ docker compose -f local.yml build Generally, if you want to emulate production environment use ``production.yml`` instead. And this is true for any other actions you might need to perform: whenever a switch is required, just do it! @@ -51,7 +51,7 @@ This brings up both Django and PostgreSQL. The first time it is run it might tak Open a terminal at the project root and run the following for local development:: - $ docker-compose -f local.yml up + $ docker compose -f local.yml up You can also set the environment variable ``COMPOSE_FILE`` pointing to ``local.yml`` like this:: @@ -59,23 +59,25 @@ You can also set the environment variable ``COMPOSE_FILE`` pointing to ``local.y And then run:: - $ docker-compose up + $ docker compose up To run in a detached (background) mode, just:: - $ docker-compose up -d + $ docker compose up -d +The site should start and be accessible at http://localhost:3000 if you selected Webpack or Gulp as frontend pipeline and http://localhost:8000 otherwise. + Execute Management Commands --------------------------- -As with any shell command that we wish to run in our container, this is done using the ``docker-compose -f local.yml run --rm`` command: :: +As with any shell command that we wish to run in our container, this is done using the ``docker compose -f local.yml run --rm`` command: :: - $ docker-compose -f local.yml run --rm django python manage.py migrate - $ docker-compose -f local.yml run --rm django python manage.py createsuperuser + $ docker compose -f local.yml run --rm django python manage.py migrate + $ docker compose -f local.yml run --rm django python manage.py createsuperuser Here, ``django`` is the target service we are executing the commands against. - +Also, please note that the ``docker exec`` does not work for running management commands. (Optionally) Designate your Docker Development Server IP -------------------------------------------------------- @@ -154,8 +156,8 @@ You have to modify the relevant requirement file: base, local or production by a To get this change picked up, you'll need to rebuild the image(s) and restart the running container: :: - docker-compose -f local.yml build - docker-compose -f local.yml up + docker compose -f local.yml build + docker compose -f local.yml up Debugging ~~~~~~~~~ @@ -169,7 +171,7 @@ If you are using the following within your code to debug: :: Then you may need to run the following for it to work as desired: :: - $ docker-compose -f local.yml run --rm --service-ports django + $ docker compose -f local.yml run --rm --service-ports django django-debug-toolbar @@ -229,7 +231,12 @@ By default, it's enabled both in local and production environments (``local.yml` 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. +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 your 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. + +The stack comes with a dedicated node service to build the static assets, watch for changes and proxy requests to the Django app with live reloading scripts injected in the response. For everything to work smoothly, you need to access the application at the port served by the node service, which is http://localhost:3000 by default. + +.. _Sass: https://sass-lang.com/ +.. _live reloading: https://browsersync.io Developing locally with HTTPS ----------------------------- @@ -309,7 +316,7 @@ You should allow the new hostname. :: Rebuild your ``docker`` application. :: - $ docker-compose -f local.yml up -d --build + $ docker compose -f local.yml up -d --build Go to your browser and type in your URL bar ``https://my-dev-env.local`` @@ -323,3 +330,26 @@ See `https with nginx`_ for more information on this configuration. Add ``certs/*`` to the ``.gitignore`` file. This allows the folder to be included in the repo but its contents to be ignored. *This configuration is for local development environments only. Do not use this for production since you might expose your local* ``rootCA-key.pem``. + +Webpack +~~~~~~~ + +If you are using Webpack: + +1. On the ``nginx-proxy`` service in ``local.yml``, change ``depends_on`` to ``node`` instead of ``django``. + +2. On the ``node`` service in ``local.yml``, add the following environment configuration: + + :: + + environment: + - VIRTUAL_HOST=my-dev-env.local + - VIRTUAL_PORT=3000 + +3. Add the following configuration to the ``devServer`` section of ``webpack/dev.config.js``: + + :: + + client: { + webSocketURL: 'auto://0.0.0.0:0/ws', // note the `:0` after `0.0.0.0` + }, diff --git a/docs/developing-locally.rst b/docs/developing-locally.rst index b79033aaa..88c8b3206 100644 --- a/docs/developing-locally.rst +++ b/docs/developing-locally.rst @@ -80,10 +80,12 @@ First things first. $ python manage.py runserver 0.0.0.0:8000 -or if you're running asynchronously: :: + or if you're running asynchronously: :: $ uvicorn config.asgi:application --host 0.0.0.0 --reload --reload-include '*.html' + If you've opted for Webpack or Gulp as frontend pipeline, please see the :ref:`dedicated section ` below. + .. _PostgreSQL: https://www.postgresql.org/download/ .. _Redis: https://redis.io/download .. _CookieCutter: https://github.com/cookiecutter/cookiecutter @@ -169,10 +171,12 @@ You can also use Django admin to queue up tasks, thanks to the `django-celerybea .. _django-celerybeat: https://django-celery-beat.readthedocs.io/en/latest/ -Sass Compilation & Live Reloading ---------------------------------- +.. _bare-metal-webpack-gulp: -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. +Using Webpack or Gulp +--------------------- + +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 your 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`_ v18 is installed on your machine. #. In the project root, install the JS dependencies with:: @@ -183,9 +187,12 @@ If you've opted for Gulp or Webpack as front-end pipeline, the project comes con $ npm run dev - The app will now run with live reloading enabled, applying front-end changes dynamically. + This will start 2 processes in parallel: the static assets build loop on one side, and the Django server on the other. + +#. Access your application at the address of the ``node`` service in order to see your correct styles. This is http://localhost:3000 by default. + + .. note:: Do NOT access the application using the Django port (8000 by default), as it will result in broken styles and 404s when accessing static assets. -.. note:: The task will start 2 processes in parallel: the static assets build loop on one side, and the Django server on the other. You do NOT need to run Django as your would normally with ``manage.py runserver``. .. _Node.js: http://nodejs.org/download/ .. _Sass: https://sass-lang.com/ diff --git a/docs/docker-postgres-backups.rst b/docs/docker-postgres-backups.rst index 875d737eb..c40b6fd69 100644 --- a/docs/docker-postgres-backups.rst +++ b/docs/docker-postgres-backups.rst @@ -8,7 +8,7 @@ Prerequisites ------------- #. the project was generated with ``use_docker`` set to ``y``; -#. the stack is up and running: ``docker-compose -f local.yml up -d postgres``. +#. the stack is up and running: ``docker compose -f local.yml up -d postgres``. Creating a Backup @@ -16,7 +16,7 @@ Creating a Backup To create a backup, run:: - $ docker-compose -f local.yml exec postgres backup + $ docker compose -f local.yml exec postgres backup Assuming your project's database is named ``my_project`` here is what you will see: :: @@ -31,7 +31,7 @@ Viewing the Existing Backups To list existing backups, :: - $ docker-compose -f local.yml exec postgres backups + $ docker compose -f local.yml exec postgres backups These are the sample contents of ``/backups``: :: @@ -55,9 +55,9 @@ With a single backup file copied to ``.`` that would be :: $ docker cp 9c5c3f055843:/backups/backup_2018_03_13T09_05_07.sql.gz . -You can also get the container ID using ``docker-compose -f local.yml ps -q postgres`` so if you want to automate your backups, you don't have to check the container ID manually every time. Here is the full command :: +You can also get the container ID using ``docker compose -f local.yml ps -q postgres`` so if you want to automate your backups, you don't have to check the container ID manually every time. Here is the full command :: - $ docker cp $(docker-compose -f local.yml ps -q postgres):/backups ./backups + $ docker cp $(docker compose -f local.yml ps -q postgres):/backups ./backups .. _`command`: https://docs.docker.com/engine/reference/commandline/cp/ @@ -66,7 +66,7 @@ Restoring from the Existing Backup To restore from one of the backups you have already got (take the ``backup_2018_03_13T09_05_07.sql.gz`` for example), :: - $ docker-compose -f local.yml exec postgres restore backup_2018_03_13T09_05_07.sql.gz + $ docker compose -f local.yml exec postgres restore backup_2018_03_13T09_05_07.sql.gz You will see something like :: @@ -94,5 +94,5 @@ Backup to Amazon S3 ---------------------------------- For uploading your backups to Amazon S3 you can use the aws cli container. There is an upload command for uploading the postgres /backups directory recursively and there is a download command for downloading a specific backup. The default S3 environment variables are used. :: - $ docker-compose -f production.yml run --rm awscli upload - $ docker-compose -f production.yml run --rm awscli download backup_2018_03_13T09_05_07.sql.gz + $ docker compose -f production.yml run --rm awscli upload + $ docker compose -f production.yml run --rm awscli download backup_2018_03_13T09_05_07.sql.gz diff --git a/docs/document.rst b/docs/document.rst index 974c66c69..26f5d56a1 100644 --- a/docs/document.rst +++ b/docs/document.rst @@ -11,7 +11,7 @@ After you have set up to `develop locally`_, run the following command from the If you set up your project to `develop locally with docker`_, run the following command: :: - $ docker-compose -f local.yml up docs + $ docker compose -f local.yml up docs Navigate to port 9000 on your host to see the documentation. This will be opened automatically at `localhost`_ for local, non-docker development. diff --git a/docs/project-generation-options.rst b/docs/project-generation-options.rst index a1d788173..bd368ae84 100644 --- a/docs/project-generation-options.rst +++ b/docs/project-generation-options.rst @@ -53,20 +53,25 @@ timezone: windows: Indicates whether the project should be configured for development on Windows. -use_pycharm: - Indicates whether the project should be configured for development with PyCharm_. +editor: + Select an editor to use. The choices are: + + 1. None + 2. PyCharm_ + 3. `VS Code`_ use_docker: - Indicates whether the project should be configured to use Docker_ and `Docker Compose`_. + Indicates whether the project should be configured to use Docker_, `Docker Compose`_ and `devcontainer`_. postgresql_version: Select a PostgreSQL_ version to use. The choices are: - 1. 14 - 2. 13 - 3. 12 - 4. 11 - 5. 10 + 1. 15 + 2. 14 + 3. 13 + 4. 12 + 5. 11 + 6. 10 cloud_provider: Select a cloud provider for static & media files. The choices are: @@ -130,6 +135,7 @@ ci_tool: 2. `Travis CI`_ 3. `Gitlab CI`_ 4. `Github Actions`_ + 5. `Drone CI`_ keep_local_envs_in_vcs: Indicates whether the project's ``.envs/.local/`` should be kept in VCS @@ -148,9 +154,11 @@ debug: .. _Apache Software License 2.0: http://www.apache.org/licenses/LICENSE-2.0 .. _PyCharm: https://www.jetbrains.com/pycharm/ +.. _VS Code: https://github.com/microsoft/vscode .. _Docker: https://github.com/docker/docker .. _Docker Compose: https://docs.docker.com/compose/ +.. _devcontainer: https://containers.dev/ .. _PostgreSQL: https://www.postgresql.org/docs/ @@ -189,4 +197,6 @@ debug: .. _GitLab CI: https://docs.gitlab.com/ee/ci/ +.. _Drone CI: https://docs.drone.io/pipeline/overview/ + .. _Github Actions: https://docs.github.com/en/actions diff --git a/docs/requirements.txt b/docs/requirements.txt index 1ae530fa7..d06b651b3 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,3 @@ sphinx==6.2.1 sphinx-rtd-theme==1.2.2 -myst-parser==1.0.0 +myst-parser==2.0.0 diff --git a/docs/settings.rst b/docs/settings.rst index 6dacb7404..0880bce95 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -81,3 +81,6 @@ Other Environment Settings DJANGO_ACCOUNT_ALLOW_REGISTRATION (=True) Allow enable or disable user registration through `django-allauth` without disabling other characteristics like authentication and account management. (Django Setting: ACCOUNT_ALLOW_REGISTRATION) + +DJANGO_ADMIN_FORCE_ALLAUTH (=False) + Force the `admin` sign in process to go through the `django-allauth` workflow. diff --git a/docs/testing.rst b/docs/testing.rst index bea45c6dd..6387a6e1e 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -19,7 +19,7 @@ You will get a readout of the `users` app that has already been set up with test If you set up your project to `develop locally with docker`_, run the following command: :: - $ docker-compose -f local.yml run --rm django pytest + $ docker compose -f local.yml run --rm django pytest Targeting particular apps for testing in ``docker`` follows a similar pattern as previously shown above. @@ -36,8 +36,8 @@ Once the tests are complete, in order to see the code coverage, run the followin 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 + $ docker compose -f local.yml run --rm django coverage run -m pytest + $ docker compose -f local.yml run --rm django coverage report .. note:: diff --git a/docs/troubleshooting.rst b/docs/troubleshooting.rst index 293e9b652..80bab2e29 100644 --- a/docs/troubleshooting.rst +++ b/docs/troubleshooting.rst @@ -24,13 +24,13 @@ Examples of logs:: If you recreate the project multiple times with the same name, Docker would preserve the volumes for the postgres container between projects. Here is what happens: #. You generate the project the first time. The .env postgres file is populated with the random password -#. You run the docker-compose and the containers are created. The postgres container creates the database based on the .env file credentials +#. You run the docker compose and the containers are created. The postgres container creates the database based on the .env file credentials #. You "regenerate" the project with the same name, so the postgres .env file is populated with a new random password -#. You run docker-compose. Since the names of the containers are the same, docker will try to start them (not create them from scratch i.e. it won't execute the Dockerfile to recreate the database). When this happens, it tries to start the database based on the new credentials which do not match the ones that the database was created with, and you get the error message above. +#. You run docker compose. Since the names of the containers are the same, docker will try to start them (not create them from scratch i.e. it won't execute the Dockerfile to recreate the database). When this happens, it tries to start the database based on the new credentials which do not match the ones that the database was created with, and you get the error message above. To fix this, you can either: -- Clear your project-related Docker cache with ``docker-compose -f local.yml down --volumes --rmi all``. +- Clear your project-related Docker cache with ``docker compose -f local.yml down --volumes --rmi all``. - Use the Docker volume sub-commands to find volumes (`ls`_) and remove them (`rm`_). - Use the `prune`_ command to clear system-wide (use with care!). diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 927419f8c..37f96efc0 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -74,12 +74,13 @@ def remove_pycharm_files(): def remove_docker_files(): + shutil.rmtree(".devcontainer") shutil.rmtree("compose") file_names = ["local.yml", "production.yml", ".dockerignore"] for file_name in file_names: os.remove(file_name) - if "{{ cookiecutter.use_pycharm }}".lower() == "y": + if "{{ cookiecutter.editor }}" == "PyCharm": file_names = ["docker_compose_up_django.xml", "docker_compose_up_docs.xml"] for file_name in file_names: os.remove(os.path.join(".idea", "runConfigurations", file_name)) @@ -96,10 +97,6 @@ def remove_heroku_files(): # don't remove the file if we are using travisci but not using heroku continue os.remove(file_name) - remove_heroku_build_hooks() - - -def remove_heroku_build_hooks(): shutil.rmtree("bin") @@ -186,6 +183,7 @@ def handle_js_runner(choice, use_docker, use_async): "browser-sync", "cssnano", "gulp", + "gulp-concat", "gulp-imagemin", "gulp-plumber", "gulp-postcss", @@ -210,6 +208,24 @@ def handle_js_runner(choice, use_docker, use_async): remove_gulp_files() +def remove_prettier_pre_commit(): + with open(".pre-commit-config.yaml", "r") as fd: + content = fd.readlines() + + removing = False + new_lines = [] + for line in content: + if removing and "- repo:" in line: + removing = False + if "mirrors-prettier" in line: + removing = True + if not removing: + new_lines.append(line) + + with open(".pre-commit-config.yaml", "w") as fd: + fd.writelines(new_lines) + + def remove_celery_files(): file_names = [ os.path.join("config", "celery_app.py"), @@ -241,6 +257,10 @@ def remove_dotgithub_folder(): shutil.rmtree(".github") +def remove_dotdrone_file(): + os.remove(".drone.yml") + + def generate_random_string(length, using_digits=False, using_ascii_letters=False, using_punctuation=False): """ Example: @@ -431,7 +451,7 @@ def main(): if "{{ cookiecutter.username_type }}" == "username": remove_custom_user_manager_files() - if "{{ cookiecutter.use_pycharm }}".lower() == "n": + if "{{ cookiecutter.editor }}" != "PyCharm": remove_pycharm_files() if "{{ cookiecutter.use_docker }}".lower() == "y": @@ -444,15 +464,13 @@ def main(): if "{{ cookiecutter.use_heroku }}".lower() == "n": remove_heroku_files() - elif "{{ cookiecutter.frontend_pipeline }}" != "Django Compressor": - remove_heroku_build_hooks() if "{{ cookiecutter.use_docker }}".lower() == "n" and "{{ cookiecutter.use_heroku }}".lower() == "n": if "{{ cookiecutter.keep_local_envs_in_vcs }}".lower() == "y": print( INFO + ".env(s) are only utilized when Docker Compose and/or " - "Heroku support is enabled so keeping them does not " - "make sense given your current setup." + TERMINATOR + "Heroku support is enabled so keeping them does not make sense " + "given your current setup." + TERMINATOR ) remove_envs_and_associated_files() else: @@ -466,6 +484,7 @@ def main(): remove_webpack_files() remove_sass_files() remove_packagejson_file() + remove_prettier_pre_commit() if "{{ cookiecutter.use_docker }}".lower() == "y": remove_node_dockerfile() else: @@ -496,6 +515,9 @@ def main(): if "{{ cookiecutter.ci_tool }}" != "Github": remove_dotgithub_folder() + if "{{ cookiecutter.ci_tool }}" != "Drone": + remove_dotdrone_file() + if "{{ cookiecutter.use_drf }}".lower() == "n": remove_drf_starter_files() diff --git a/pyproject.toml b/pyproject.toml index 2b4b98783..2a9f00b29 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,3 +27,24 @@ known_first_party = [ "scripts", "hooks", ] + + +# ==== djLint ==== +[tool.djlint] +blank_line_after_tag = "load,extends" +close_void_tags = true +format_css = true +format_js = true +# TODO: remove T002 when fixed https://github.com/Riverside-Healthcare/djLint/issues/687 +ignore = "H006,H030,H031,T002,T028" +ignore_blocks = "raw" +include = "H017,H035" +indent = 2 +max_line_length = 119 +profile = "jinja" + +[tool.djlint.css] +indent_size = 2 + +[tool.djlint.js] +indent_size = 2 diff --git a/requirements.txt b/requirements.txt index f964e4ac7..fbe8dfdeb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,27 +1,28 @@ -cookiecutter==2.1.1 -sh==2.0.4; sys_platform != "win32" +cookiecutter==2.3.0 +sh==2.0.6; sys_platform != "win32" binaryornot==0.4.4 # Code quality # ------------------------------------------------------------------------------ -black==23.3.0 +black==23.7.0 isort==5.12.0 -flake8==6.0.0 -django-upgrade==1.13.0 -pre-commit==3.3.2 +flake8==6.1.0 +django-upgrade==1.14.0 +djlint==1.32.1 +pre-commit==3.3.3 # Testing # ------------------------------------------------------------------------------ -tox==4.6.0 -pytest==7.3.1 +tox==4.8.0 +pytest==7.4.0 pytest-xdist==3.3.1 pytest-cookies==0.7.0 pytest-instafail==0.5.0 -pyyaml==6.0 +pyyaml==6.0.1 # Scripting # ------------------------------------------------------------------------------ -PyGithub==1.58.2 -gitpython==3.1.31 +PyGithub==1.59.1 +gitpython==3.1.32 jinja2==3.1.2 requests==2.31.0 diff --git a/scripts/create_django_issue.py b/scripts/create_django_issue.py index d3b3c3792..236a126fb 100644 --- a/scripts/create_django_issue.py +++ b/scripts/create_django_issue.py @@ -182,9 +182,8 @@ class GitHubManager: if not matches: continue issue_version = DjVersion.parse(matches.group(1)) - if self.base_dj_version > issue_version: - issue.edit(state="closed") - print(f"Closed issue {issue.title} (ID: [{issue.id}]({issue.url}))") + if self.base_dj_version >= issue_version: + self.close_issue(issue) else: self.existing_issues[issue_version] = issue @@ -269,6 +268,11 @@ class GitHubManager: issue = self.repo.create_issue(f"[Update Django] Django {needed_dj_version}", description) issue.add_to_labels(f"django{needed_dj_version}") + @staticmethod + def close_issue(issue: Issue): + issue.edit(state="closed") + print(f"Closed issue {issue.title} (ID: [{issue.id}]({issue.url}))") + def generate(self): for version in self.needed_dj_versions: print(f"Handling GitHub issue for Django {version}") @@ -280,10 +284,15 @@ class GitHubManager: def main(django_max_version=None) -> None: # Check if there are any djs current_dj, latest_djs = get_all_latest_django_versions(django_max_version=django_max_version) - if not latest_djs: - sys.exit(0) + + # Run the setup, which might close old issues manager = GitHubManager(current_dj, latest_djs) manager.setup() + + if not latest_djs: + print("No new Django versions to update. Exiting...") + sys.exit(0) + manager.generate() diff --git a/setup.py b/setup.py index f9d6222d5..34983e0b7 100644 --- a/setup.py +++ b/setup.py @@ -5,9 +5,9 @@ except ImportError: from distutils.core import setup # We use calendar versioning -version = "2023.06.07" +version = "2023.08.10" -with open("README.rst") as readme_file: +with open("README.md") as readme_file: long_description = readme_file.read() setup( @@ -24,7 +24,7 @@ setup( classifiers=[ "Development Status :: 4 - Beta", "Environment :: Console", - "Framework :: Django :: 4.1", + "Framework :: Django :: 4.2", "Intended Audience :: Developers", "Natural Language :: English", "License :: OSI Approved :: BSD License", diff --git a/tests/test_cookiecutter_generation.py b/tests/test_cookiecutter_generation.py index 778e3411f..87e52ef01 100755 --- a/tests/test_cookiecutter_generation.py +++ b/tests/test_cookiecutter_generation.py @@ -52,10 +52,12 @@ SUPPORTED_COMBINATIONS = [ {"open_source_license": "Not open source"}, {"windows": "y"}, {"windows": "n"}, - {"use_pycharm": "y"}, - {"use_pycharm": "n"}, + {"editor": "None"}, + {"editor": "PyCharm"}, + {"editor": "VS Code"}, {"use_docker": "y"}, {"use_docker": "n"}, + {"postgresql_version": "15"}, {"postgresql_version": "14"}, {"postgresql_version": "13"}, {"postgresql_version": "12"}, @@ -125,6 +127,7 @@ SUPPORTED_COMBINATIONS = [ {"ci_tool": "Travis"}, {"ci_tool": "Gitlab"}, {"ci_tool": "Github"}, + {"ci_tool": "Drone"}, {"keep_local_envs_in_vcs": "y"}, {"keep_local_envs_in_vcs": "n"}, {"debug": "y"}, @@ -231,7 +234,7 @@ def test_django_upgrade_passes(cookies, context_override): try: sh.django_upgrade( "--target-version", - "4.1", + "4.2", *python_files, _cwd=str(result.project_path), ) @@ -239,11 +242,37 @@ def test_django_upgrade_passes(cookies, context_override): pytest.fail(e.stdout.decode()) +@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id) +def test_djlint_lint_passes(cookies, context_override): + """Check whether generated project passes djLint --lint.""" + result = cookies.bake(extra_context=context_override) + + autofixable_rules = "H014,T001" + # TODO: remove T002 when fixed https://github.com/Riverside-Healthcare/djLint/issues/687 + ignored_rules = "H006,H030,H031,T002" + try: + sh.djlint("--lint", "--ignore", f"{autofixable_rules},{ignored_rules}", ".", _cwd=str(result.project_path)) + except sh.ErrorReturnCode as e: + pytest.fail(e.stdout.decode()) + + +@auto_fixable +@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id) +def test_djlint_check_passes(cookies, context_override): + """Check whether generated project passes djLint --check.""" + result = cookies.bake(extra_context=context_override) + + try: + sh.djlint("--check", ".", _cwd=str(result.project_path)) + except sh.ErrorReturnCode as e: + pytest.fail(e.stdout.decode()) + + @pytest.mark.parametrize( ["use_docker", "expected_test_script"], [ ("n", "pytest"), - ("y", "docker-compose -f local.yml run django pytest"), + ("y", "docker compose -f local.yml run django pytest"), ], ) def test_travis_invokes_pytest(cookies, context, use_docker, expected_test_script): @@ -268,7 +297,7 @@ def test_travis_invokes_pytest(cookies, context, use_docker, expected_test_scrip ["use_docker", "expected_test_script"], [ ("n", "pytest"), - ("y", "docker-compose -f local.yml run django pytest"), + ("y", "docker compose -f local.yml run django pytest"), ], ) def test_gitlab_invokes_precommit_and_pytest(cookies, context, use_docker, expected_test_script): @@ -295,7 +324,7 @@ def test_gitlab_invokes_precommit_and_pytest(cookies, context, use_docker, expec ["use_docker", "expected_test_script"], [ ("n", "pytest"), - ("y", "docker-compose -f local.yml run django pytest"), + ("y", "docker compose -f local.yml run django pytest"), ], ) def test_github_invokes_linter_and_pytest(cookies, context, use_docker, expected_test_script): @@ -347,14 +376,15 @@ def test_error_if_incompatible(cookies, context, invalid_context): @pytest.mark.parametrize( - ["use_pycharm", "pycharm_docs_exist"], + ["editor", "pycharm_docs_exist"], [ - ("n", False), - ("y", True), + ("None", False), + ("PyCharm", True), + ("VS Code", False), ], ) -def test_pycharm_docs_removed(cookies, context, use_pycharm, pycharm_docs_exist): - context.update({"use_pycharm": use_pycharm}) +def test_pycharm_docs_removed(cookies, context, editor, pycharm_docs_exist): + context.update({"editor": editor}) result = cookies.bake(extra_context=context) with open(f"{result.project_path}/docs/index.rst") as f: diff --git a/tests/test_docker.sh b/tests/test_docker.sh index 5c60d20d3..8e4055e20 100755 --- a/tests/test_docker.sh +++ b/tests/test_docker.sh @@ -15,28 +15,28 @@ cookiecutter ../../ --no-input --overwrite-if-exists use_docker=y "$@" cd my_awesome_project # make sure all images build -docker-compose -f local.yml build +docker compose -f local.yml build # run the project's type checks -docker-compose -f local.yml run django mypy my_awesome_project +docker compose -f local.yml run django mypy my_awesome_project # run the project's tests -docker-compose -f local.yml run django pytest +docker compose -f local.yml run django pytest # return non-zero status code if there are migrations that have not been created -docker-compose -f local.yml run django python manage.py makemigrations --dry-run --check || { echo "ERROR: there were changes in the models, but migration listed above have not been created and are not saved in version control"; exit 1; } +docker compose -f local.yml run django python manage.py makemigrations --dry-run --check || { echo "ERROR: there were changes in the models, but migration listed above have not been created and are not saved in version control"; exit 1; } # Test support for translations -docker-compose -f local.yml run django python manage.py makemessages --all +docker compose -f local.yml run django python manage.py makemessages --all # Make sure the check doesn't raise any warnings -docker-compose -f local.yml run django python manage.py check --fail-level WARNING +docker compose -f local.yml run django python manage.py check --fail-level WARNING # Generate the HTML for the documentation -docker-compose -f local.yml run docs make html +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 + docker compose -f local.yml run node npm run build fi diff --git a/{{cookiecutter.project_slug}}/.devcontainer/bash_history b/{{cookiecutter.project_slug}}/.devcontainer/bash_history new file mode 100644 index 000000000..e69de29bb diff --git a/{{cookiecutter.project_slug}}/.devcontainer/bashrc.override.sh b/{{cookiecutter.project_slug}}/.devcontainer/bashrc.override.sh new file mode 100644 index 000000000..bedddf64b --- /dev/null +++ b/{{cookiecutter.project_slug}}/.devcontainer/bashrc.override.sh @@ -0,0 +1,20 @@ + +# +# .bashrc.override.sh +# + +# persistent bash history +HISTFILE=~/.bash_history +PROMPT_COMMAND="history -a; $PROMPT_COMMAND" + +# set some django env vars +source /entrypoint + +# restore default shell options +set +o errexit +set +o pipefail +set +o nounset + +# start ssh-agent +# https://code.visualstudio.com/docs/remote/troubleshooting +eval "$(ssh-agent -s)" diff --git a/{{cookiecutter.project_slug}}/.devcontainer/devcontainer.json b/{{cookiecutter.project_slug}}/.devcontainer/devcontainer.json new file mode 100644 index 000000000..393408582 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.devcontainer/devcontainer.json @@ -0,0 +1,87 @@ +// For format details, see https://containers.dev/implementors/json_reference/ +{ + "name": "{{cookiecutter.project_slug}}_dev", + "dockerComposeFile": [ + "../local.yml" + ], + "init": true, + "mounts": [ + { + "source": "./.devcontainer/bash_history", + "target": "/home/dev-user/.bash_history", + "type": "bind" + }, + { + "source": "/tmp", + "target": "/tmp", + "type": "bind" + }, + { + "source": "~/.ssh", + "target": "/home/dev-user/.ssh", + "type": "bind" + } + ], + // Tells devcontainer.json supporting services / tools whether they should run + // /bin/sh -c "while sleep 1000; do :; done" when starting the container instead of the container’s default command + "overrideCommand": false, + "service": "django", + // "remoteEnv": {"PATH": "/home/dev-user/.local/bin:${containerEnv:PATH}"}, + "remoteUser": "dev-user", + "workspaceFolder": "/app", + // Set *default* container specific settings.json values on container create. + "customizations": { + {%- if cookiecutter.editor == "VS Code" %} + "vscode": { + "settings": { + "editor.formatOnSave": true, + "[python]": { + "analysis.autoImportCompletions": true, + "analysis.typeCheckingMode": "basic", + "defaultInterpreterPath": "/usr/local/bin/python", + "editor.codeActionsOnSave": { + "source.organizeImports": true + }, + // Uncomment when fixed + // https://github.com/microsoft/vscode-remote-release/issues/8474 + // "editor.defaultFormatter": "ms-python.black-formatter", + "formatting.blackPath": "/usr/local/bin/black", + "formatting.provider": "black", + "languageServer": "Pylance", + // "linting.banditPath": "/usr/local/py-utils/bin/bandit", + "linting.enabled": true, + "linting.flake8Enabled": true, + "linting.flake8Path": "/usr/local/bin/flake8", + "linting.mypyEnabled": true, + "linting.mypyPath": "/usr/local/bin/mypy", + "linting.pycodestylePath": "/usr/local/bin/pycodestyle", + // "linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", + "linting.pylintEnabled": true, + "linting.pylintPath": "/usr/local/bin/pylint" + } + }, + // https://code.visualstudio.com/docs/remote/devcontainerjson-reference#_vs-code-specific-properties + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "davidanson.vscode-markdownlint", + "mrmlnc.vscode-duplicate", + "visualstudioexptteam.vscodeintellicode", + "visualstudioexptteam.intellicode-api-usage-examples", + // python + "ms-python.python", + "ms-python.vscode-pylance", + "ms-python.isort", + "ms-python.black-formatter", + // django + "batisteo.vscode-django" + ] + } + {%- endif %} + }, + // Uncomment the next line if you want start specific services in your Docker Compose config. + // "runServices": [], + // Uncomment the next line if you want to keep your containers running after VS Code shuts down. + // "shutdownAction": "none", + // Uncomment the next line to run commands after the container is created. + "postCreateCommand": "cat .devcontainer/bashrc.override.sh >> ~/.bashrc" +} diff --git a/{{cookiecutter.project_slug}}/.dockerignore b/{{cookiecutter.project_slug}}/.dockerignore index 7369480e3..a602416cd 100644 --- a/{{cookiecutter.project_slug}}/.dockerignore +++ b/{{cookiecutter.project_slug}}/.dockerignore @@ -9,3 +9,4 @@ .travis.yml venv .git +.envs/ diff --git a/{{cookiecutter.project_slug}}/.drone.yml b/{{cookiecutter.project_slug}}/.drone.yml new file mode 100644 index 000000000..dc08bfbab --- /dev/null +++ b/{{cookiecutter.project_slug}}/.drone.yml @@ -0,0 +1,48 @@ +kind: pipeline +name: default + +environment: + POSTGRES_USER: '{{ cookiecutter.project_slug }}' + POSTGRES_PASSWORD: '' + POSTGRES_DB: 'test_{{ cookiecutter.project_slug }}' + POSTGRES_HOST_AUTH_METHOD: trust + {%- if cookiecutter.use_celery == 'y' %} + CELERY_BROKER_URL: 'redis://redis:6379/0' + {%- endif %} + +steps: +- name: lint + pull: if-not-exists + image: python:3.11 + environment: + PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit + volumes: + - name: pre-commit cache + path: ${PRE_COMMIT_HOME} + commands: + - export PRE_COMMIT_HOME=$CI_PROJECT_DIR/.cache/pre-commit + - pip install -q pre-commit + - pre-commit run --show-diff-on-failure --color=always --all-files + +- name: test + pull: if-not-exists + {%- if cookiecutter.use_docker == 'y' %} + image: docker/compose:1.29.2 + environment: + DATABASE_URL: pgsql://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres/$POSTGRES_DB + commands: + - docker-compose -f local.yml build + - docker-compose -f local.yml run --rm django python manage.py migrate + - docker-compose -f local.yml up -d + - docker-compose -f local.yml run django pytest + {%- else %} + image: python:3.11 + commands: + - pip install -r requirements/local.txt + - pytest + {%- endif%} + +volumes: +- name: pre-commit cache + host: + path: /tmp/drone/cache/pre-commit diff --git a/{{cookiecutter.project_slug}}/.github/workflows/ci.yml b/{{cookiecutter.project_slug}}/.github/workflows/ci.yml index ac231ee72..3a863ccb9 100644 --- a/{{cookiecutter.project_slug}}/.github/workflows/ci.yml +++ b/{{cookiecutter.project_slug}}/.github/workflows/ci.yml @@ -49,7 +49,7 @@ jobs: - 6379:6379 {%- endif %} postgres: - image: postgres:12 + image: postgres:{{ cookiecutter.postgresql_version }} ports: - 5432:5432 env: @@ -69,16 +69,16 @@ jobs: {%- if cookiecutter.use_docker == 'y' %} - name: Build the Stack - run: docker-compose -f local.yml build + run: docker compose -f local.yml build - name: Run DB Migrations - run: docker-compose -f local.yml run --rm django python manage.py migrate + run: docker compose -f local.yml run --rm django python manage.py migrate - name: Run Django Tests - run: docker-compose -f local.yml run django pytest + run: docker compose -f local.yml run django pytest - name: Tear down the Stack - run: docker-compose -f local.yml down + run: docker compose -f local.yml down {%- else %} - name: Set up Python diff --git a/{{cookiecutter.project_slug}}/.gitignore b/{{cookiecutter.project_slug}}/.gitignore index 19bb2bc07..541f40846 100644 --- a/{{cookiecutter.project_slug}}/.gitignore +++ b/{{cookiecutter.project_slug}}/.gitignore @@ -161,11 +161,10 @@ typings/ !.vscode/extensions.json *.code-workspace -# Local History for Visual Studio Code -.history/ +# Local History for devcontainer +.devcontainer/bash_history - -{% if cookiecutter.use_pycharm == 'y' -%} +{% if cookiecutter.editor == 'PyCharm' -%} # Provided default Pycharm Run/Debug Configurations should be tracked by git # In case of local modifications made by Pycharm, use update-index command # for each changed file, like this: diff --git a/{{cookiecutter.project_slug}}/.gitlab-ci.yml b/{{cookiecutter.project_slug}}/.gitlab-ci.yml index a312a41ae..350212003 100644 --- a/{{cookiecutter.project_slug}}/.gitlab-ci.yml +++ b/{{cookiecutter.project_slug}}/.gitlab-ci.yml @@ -33,12 +33,12 @@ pytest: services: - docker:dind before_script: - - docker-compose -f local.yml build + - docker compose -f local.yml build # Ensure celerybeat does not crash due to non-existent tables - - docker-compose -f local.yml run --rm django python manage.py migrate - - docker-compose -f local.yml up -d + - docker compose -f local.yml run --rm django python manage.py migrate + - docker compose -f local.yml up -d script: - - docker-compose -f local.yml run django pytest + - docker compose -f local.yml run django pytest {%- else %} image: python:3.11 tags: diff --git a/{{cookiecutter.project_slug}}/.pre-commit-config.yaml b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml index 3a0411d81..dcf5ec209 100644 --- a/{{cookiecutter.project_slug}}/.pre-commit-config.yaml +++ b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml @@ -18,26 +18,26 @@ repos: - id: detect-private-key - repo: https://github.com/pre-commit/mirrors-prettier - rev: v3.0.0-alpha.9-for-vscode + rev: v3.0.1 hooks: - id: prettier args: ['--tab-width', '2', '--single-quote'] exclude: '{{cookiecutter.project_slug}}/templates/' - repo: https://github.com/adamchainz/django-upgrade - rev: '1.13.0' + rev: '1.14.0' hooks: - id: django-upgrade - args: ['--target-version', '4.1'] + args: ['--target-version', '4.2'] - repo: https://github.com/asottile/pyupgrade - rev: v3.4.0 + rev: v3.10.1 hooks: - id: pyupgrade args: [--py311-plus] - repo: https://github.com/psf/black - rev: 23.3.0 + rev: 23.7.0 hooks: - id: black @@ -47,10 +47,16 @@ repos: - id: isort - repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 + rev: 6.1.0 hooks: - id: flake8 + - repo: https://github.com/Riverside-Healthcare/djLint + rev: v1.32.1 + hooks: + - id: djlint-reformat-django + - id: djlint-django + # sets up .pre-commit-ci.yaml to ensure pre-commit dependencies stay up to date ci: autoupdate_schedule: weekly diff --git a/{{cookiecutter.project_slug}}/.travis.yml b/{{cookiecutter.project_slug}}/.travis.yml index 5e5f92ff5..cd703d3ad 100644 --- a/{{cookiecutter.project_slug}}/.travis.yml +++ b/{{cookiecutter.project_slug}}/.travis.yml @@ -17,16 +17,16 @@ jobs: - name: "Django Test" {%- if cookiecutter.use_docker == 'y' %} before_script: - - docker-compose -v + - docker compose -v - docker -v - - docker-compose -f local.yml build + - docker compose -f local.yml build # Ensure celerybeat does not crash due to non-existent tables - - docker-compose -f local.yml run --rm django python manage.py migrate - - docker-compose -f local.yml up -d + - docker compose -f local.yml run --rm django python manage.py migrate + - docker compose -f local.yml up -d script: - - "docker-compose -f local.yml run django pytest" + - "docker compose -f local.yml run django pytest" after_failure: - - docker-compose -f local.yml logs + - docker compose -f local.yml logs {%- else %} before_install: - sudo apt-get update -qq diff --git a/{{cookiecutter.project_slug}}/bin/post_compile b/{{cookiecutter.project_slug}}/bin/post_compile index a9c94b39a..16719f493 100644 --- a/{{cookiecutter.project_slug}}/bin/post_compile +++ b/{{cookiecutter.project_slug}}/bin/post_compile @@ -1,4 +1,5 @@ #!/usr/bin/env bash +{%- if cookiecutter.frontend_pipeline == "Django Compressor" %} compress_enabled() { python << END @@ -19,4 +20,7 @@ if compress_enabled then python manage.py compress fi +{%- endif %} + python manage.py collectstatic --noinput +python manage.py compilemessages -i site-packages diff --git a/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile b/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile index b1f459ba5..3636ce1ef 100644 --- a/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile @@ -33,6 +33,18 @@ ENV BUILD_ENV ${BUILD_ENVIRONMENT} WORKDIR ${APP_HOME} +{% if cookiecutter.use_docker == "y" %} +# devcontainer dependencies and utils +RUN apt-get update && apt-get install --no-install-recommends -y \ + sudo git bash-completion nano ssh + +# Create devcontainer user and add it to sudoers +RUN groupadd --gid 1000 dev-user \ + && useradd --uid 1000 --gid dev-user --shell /bin/bash --create-home dev-user \ + && echo dev-user ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/dev-user \ + && chmod 0440 /etc/sudoers.d/dev-user +{% endif %} + # Install required system dependencies RUN apt-get update && apt-get install --no-install-recommends -y \ # psycopg2 dependencies @@ -49,7 +61,7 @@ COPY --from=python-build-stage /usr/src/app/wheels /wheels/ # use wheels to install python dependencies RUN pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* \ - && rm -rf /wheels/ + && rm -rf /wheels/ COPY ./compose/production/django/entrypoint /entrypoint RUN sed -i 's/\r$//g' /entrypoint diff --git a/{{cookiecutter.project_slug}}/compose/production/aws/maintenance/download b/{{cookiecutter.project_slug}}/compose/production/aws/maintenance/download index 0c515935f..9561d917a 100644 --- a/{{cookiecutter.project_slug}}/compose/production/aws/maintenance/download +++ b/{{cookiecutter.project_slug}}/compose/production/aws/maintenance/download @@ -3,7 +3,7 @@ ### Download a file from your Amazon S3 bucket to the postgres /backups folder ### ### Usage: -### $ docker-compose -f production.yml run --rm awscli <1> +### $ docker compose -f production.yml run --rm awscli <1> set -o errexit set -o pipefail diff --git a/{{cookiecutter.project_slug}}/compose/production/aws/maintenance/upload b/{{cookiecutter.project_slug}}/compose/production/aws/maintenance/upload index 9446b9304..73c1b9bec 100644 --- a/{{cookiecutter.project_slug}}/compose/production/aws/maintenance/upload +++ b/{{cookiecutter.project_slug}}/compose/production/aws/maintenance/upload @@ -3,7 +3,7 @@ ### Upload the /backups folder to Amazon S3 ### ### Usage: -### $ docker-compose -f production.yml run --rm awscli upload +### $ docker compose -f production.yml run --rm awscli upload set -o errexit set -o pipefail diff --git a/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile b/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile index bbe459839..a48cbc4af 100644 --- a/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile @@ -121,4 +121,11 @@ RUN chown django:django ${APP_HOME} USER django +RUN DATABASE_URL="" \ + {%- if cookiecutter.use_celery == "y" %} + CELERY_BROKER_URL="" \ + {%- endif %} + DJANGO_SETTINGS_MODULE="config.settings.test" \ + python manage.py compilemessages + ENTRYPOINT ["/entrypoint"] diff --git a/{{cookiecutter.project_slug}}/compose/production/django/entrypoint b/{{cookiecutter.project_slug}}/compose/production/django/entrypoint index 2fbcad955..dd07f2d2a 100644 --- a/{{cookiecutter.project_slug}}/compose/production/django/entrypoint +++ b/{{cookiecutter.project_slug}}/compose/production/django/entrypoint @@ -20,14 +20,14 @@ python << END import sys import time -import psycopg2 +import psycopg suggest_unrecoverable_after = 30 start = time.time() while True: try: - psycopg2.connect( + psycopg.connect( dbname="${POSTGRES_DB}", user="${POSTGRES_USER}", password="${POSTGRES_PASSWORD}", @@ -35,7 +35,7 @@ while True: port="${POSTGRES_PORT}", ) break - except psycopg2.OperationalError as error: + except psycopg.OperationalError as error: sys.stderr.write("Waiting for PostgreSQL to become available...\n") if time.time() - start > suggest_unrecoverable_after: diff --git a/{{cookiecutter.project_slug}}/compose/production/postgres/maintenance/backup b/{{cookiecutter.project_slug}}/compose/production/postgres/maintenance/backup index ee0c9d63c..f72304c05 100644 --- a/{{cookiecutter.project_slug}}/compose/production/postgres/maintenance/backup +++ b/{{cookiecutter.project_slug}}/compose/production/postgres/maintenance/backup @@ -4,7 +4,7 @@ ### Create a database backup. ### ### Usage: -### $ docker-compose -f .yml (exec |run --rm) postgres backup +### $ docker compose -f .yml (exec |run --rm) postgres backup set -o errexit diff --git a/{{cookiecutter.project_slug}}/compose/production/postgres/maintenance/backups b/{{cookiecutter.project_slug}}/compose/production/postgres/maintenance/backups index 0484ccff5..a18937d62 100644 --- a/{{cookiecutter.project_slug}}/compose/production/postgres/maintenance/backups +++ b/{{cookiecutter.project_slug}}/compose/production/postgres/maintenance/backups @@ -4,7 +4,7 @@ ### View backups. ### ### Usage: -### $ docker-compose -f .yml (exec |run --rm) postgres backups +### $ docker compose -f .yml (exec |run --rm) postgres backups set -o errexit diff --git a/{{cookiecutter.project_slug}}/compose/production/postgres/maintenance/restore b/{{cookiecutter.project_slug}}/compose/production/postgres/maintenance/restore index 9661ca7f1..c68f17d71 100644 --- a/{{cookiecutter.project_slug}}/compose/production/postgres/maintenance/restore +++ b/{{cookiecutter.project_slug}}/compose/production/postgres/maintenance/restore @@ -7,7 +7,7 @@ ### <1> filename of an existing backup. ### ### Usage: -### $ docker-compose -f .yml (exec |run --rm) postgres restore <1> +### $ docker compose -f .yml (exec |run --rm) postgres restore <1> set -o errexit diff --git a/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile b/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile index 581bbfebd..e547dfbb8 100644 --- a/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/production/traefik/Dockerfile @@ -1,4 +1,4 @@ -FROM traefik:2.10.1 +FROM traefik:2.10.4 RUN mkdir -p /etc/traefik/acme \ && touch /etc/traefik/acme/acme.json \ && chmod 600 /etc/traefik/acme/acme.json diff --git a/{{cookiecutter.project_slug}}/config/settings/base.py b/{{cookiecutter.project_slug}}/config/settings/base.py index 487669e0a..f02d476c1 100644 --- a/{{cookiecutter.project_slug}}/config/settings/base.py +++ b/{{cookiecutter.project_slug}}/config/settings/base.py @@ -250,6 +250,9 @@ ADMIN_URL = "admin/" ADMINS = [("""{{cookiecutter.author_name}}""", "{{cookiecutter.email}}")] # https://docs.djangoproject.com/en/dev/ref/settings/#managers MANAGERS = ADMINS +# https://cookiecutter-django.readthedocs.io/en/latest/settings.html#other-environment-settings +# Force the `admin` sign in process to go through the `django-allauth` workflow +DJANGO_ADMIN_FORCE_ALLAUTH = env.bool("DJANGO_ADMIN_FORCE_ALLAUTH", default=False) # LOGGING # ------------------------------------------------------------------------------ diff --git a/{{cookiecutter.project_slug}}/config/settings/production.py b/{{cookiecutter.project_slug}}/config/settings/production.py index e76b63993..87340b88a 100644 --- a/{{cookiecutter.project_slug}}/config/settings/production.py +++ b/{{cookiecutter.project_slug}}/config/settings/production.py @@ -146,7 +146,7 @@ SERVER_EMAIL = env("DJANGO_SERVER_EMAIL", default=DEFAULT_FROM_EMAIL) # https://docs.djangoproject.com/en/dev/ref/settings/#email-subject-prefix EMAIL_SUBJECT_PREFIX = env( "DJANGO_EMAIL_SUBJECT_PREFIX", - default="[{{cookiecutter.project_name}}]", + default="[{{cookiecutter.project_name}}] ", ) # ADMIN diff --git a/{{cookiecutter.project_slug}}/docs/howto.rst b/{{cookiecutter.project_slug}}/docs/howto.rst index 7f2d26a1e..7d86351cf 100644 --- a/{{cookiecutter.project_slug}}/docs/howto.rst +++ b/{{cookiecutter.project_slug}}/docs/howto.rst @@ -8,14 +8,14 @@ Documentation can be written as rst files in `{{cookiecutter.project_slug}}/docs {% if cookiecutter.use_docker == 'n' %} To build and serve docs, use the command:: - - make livehtml - -from inside the `{{cookiecutter.project_slug}}/docs` directory. + + make livehtml + +from inside the `{{cookiecutter.project_slug}}/docs` directory. {% else %} To build and serve docs, use the commands:: - - docker-compose -f local.yml up docs + + docker compose -f local.yml up docs {% endif %} @@ -34,12 +34,12 @@ For an in-use example, see the `page source <_sources/users.rst.txt>`_ for :ref: To compile all docstrings automatically into documentation source files, use the command: :: - + make apidocs {% if cookiecutter.use_docker == 'y' %} This can be done in the docker container: - :: - + :: + docker run --rm docs make apidocs {% endif -%} diff --git a/{{cookiecutter.project_slug}}/docs/index.rst b/{{cookiecutter.project_slug}}/docs/index.rst index cb4cbaeda..10b1c936f 100644 --- a/{{cookiecutter.project_slug}}/docs/index.rst +++ b/{{cookiecutter.project_slug}}/docs/index.rst @@ -10,7 +10,7 @@ Welcome to {{ cookiecutter.project_name }}'s documentation! :maxdepth: 2 :caption: Contents: - howto{% if cookiecutter.use_pycharm == 'y' %} + howto{% if cookiecutter.editor == 'PyCharm' %} pycharm/configuration{% endif %} users diff --git a/{{cookiecutter.project_slug}}/locale/README.md b/{{cookiecutter.project_slug}}/locale/README.md new file mode 100644 index 000000000..a514ad10c --- /dev/null +++ b/{{cookiecutter.project_slug}}/locale/README.md @@ -0,0 +1,32 @@ +# Translations + +Start by configuring the `LANGUAGES` settings in `base.py`, by uncommenting languages you are willing to support. Then, translations strings will be placed in this folder when running: + +```bash +{% if cookiecutter.use_docker == 'y' %}docker compose -f local.yml run --rm django {% endif %}python manage.py makemessages -all --no-location +``` + +This should generate `django.po` (stands for Portable Object) files under each locale `/LC_MESSAGES/django.po`. Each translatable string in the codebase is collected with its `msgid` and need to be translated as `msgstr`, for example: + +```po +msgid "users" +msgstr "utilisateurs" +``` + +Once all translations are done, they need to be compiled into `.mo` files (stands for Machine Object), which are the actual binary files used by the application: + +```bash +{% if cookiecutter.use_docker == 'y' %}docker compose -f local.yml run --rm django {% endif %}python manage.py compilemessages +``` + +Note that the `.po` files are NOT used by the application directly, so if the `.mo` files are out of dates, the content won't appear as translated even if the `.po` files are up-to-date. + +## Production + +The production image runs `compilemessages` automatically at build time, so as long as your translated source files (PO) are up-to-date, you're good to go. + +## Add a new language + +1. Update the [`LANGUAGES` setting](https://docs.djangoproject.com/en/stable/ref/settings/#std-setting-LANGUAGES) to your project's base settings. +2. Create the locale folder for the language next to this file, e.g. `fr_FR` for French. Make sure the case is correct. +3. Run `makemessages` (as instructed above) to generate the PO files for the new language. diff --git a/{{cookiecutter.project_slug}}/locale/README.rst b/{{cookiecutter.project_slug}}/locale/README.rst deleted file mode 100644 index a501b7a18..000000000 --- a/{{cookiecutter.project_slug}}/locale/README.rst +++ /dev/null @@ -1,14 +0,0 @@ -Translations -============ - -Start by configuring `LANGUAGES` at settings, by uncommenting languages you are willing to support. - -Translations will be placed in this folder when running: - - python manage.py makemessages --all - -Then you should edit the .po files providing proper translations and then run the following for compiling the messages: - - python manage.py compilemessages - -Note: You may need to restart the django server for changes to take effect. diff --git a/{{cookiecutter.project_slug}}/locale/en_US/LC_MESSAGES/django.po b/{{cookiecutter.project_slug}}/locale/en_US/LC_MESSAGES/django.po new file mode 100644 index 000000000..6a4aa2e0b --- /dev/null +++ b/{{cookiecutter.project_slug}}/locale/en_US/LC_MESSAGES/django.po @@ -0,0 +1,12 @@ +# Translations for the {{ cookiecutter.project_name }} project +# Copyright (C) {% now 'utc', '%Y' %} {{ cookiecutter.author_name }} +# {{ cookiecutter.author_name }} <{{ cookiecutter.email }}>, {% now 'utc', '%Y' %}. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: {{ cookiecutter.version }}\n" +"Language: en-US\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" diff --git a/{{cookiecutter.project_slug}}/locale/pt_BR/LC_MESSAGES/django.po b/{{cookiecutter.project_slug}}/locale/pt_BR/LC_MESSAGES/django.po index fc17c6a61..1681e0f57 100644 --- a/{{cookiecutter.project_slug}}/locale/pt_BR/LC_MESSAGES/django.po +++ b/{{cookiecutter.project_slug}}/locale/pt_BR/LC_MESSAGES/django.po @@ -1,18 +1,12 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. +# Translations for the {{ cookiecutter.project_name }} project +# Copyright (C) {% now 'utc', '%Y' %} {{ cookiecutter.author_name }} +# {{ cookiecutter.author_name }} <{{ cookiecutter.email }}>, {% now 'utc', '%Y' %}. # #, fuzzy msgid "" msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-06-04 21:42+0000\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" +"Project-Id-Version: {{ cookiecutter.version }}\n" +"Language: pt-BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -133,7 +127,7 @@ msgstr "Ou, cadastre-se para uma conta em %(site_ #: {{cookiecutter.project_slug}}/templates/account/login.html:32 msgid "or" -msgstr "or" +msgstr "ou" #: {{cookiecutter.project_slug}}/templates/account/login.html:41 #, python-format diff --git a/{{cookiecutter.project_slug}}/package.json b/{{cookiecutter.project_slug}}/package.json index fdfd26bf3..99b984da6 100644 --- a/{{cookiecutter.project_slug}}/package.json +++ b/{{cookiecutter.project_slug}}/package.json @@ -25,7 +25,7 @@ "pixrem": "^5.0.0", "postcss": "^8.3.11", "postcss-loader": "^7.0.2", - "postcss-preset-env": "^8.0.1", + "postcss-preset-env": "^9.0.0", "sass": "^1.43.4", "sass-loader": "^13.2.0", "webpack": "^5.65.0", diff --git a/{{cookiecutter.project_slug}}/pyproject.toml b/{{cookiecutter.project_slug}}/pyproject.toml index 6acac9b2c..7e4c9aa9c 100644 --- a/{{cookiecutter.project_slug}}/pyproject.toml +++ b/{{cookiecutter.project_slug}}/pyproject.toml @@ -90,3 +90,23 @@ generated-members = [ "save", "delete", ] + + +# ==== djLint ==== +[tool.djlint] +blank_line_after_tag = "load,extends" +close_void_tags = true +format_css = true +format_js = true +# TODO: remove T002 when fixed https://github.com/Riverside-Healthcare/djLint/issues/687 +ignore = "H006,H030,H031,T002" +include = "H017,H035" +indent = 2 +max_line_length = 119 +profile = "django" + +[tool.djlint.css] +indent_size = 2 + +[tool.djlint.js] +indent_size = 2 diff --git a/{{cookiecutter.project_slug}}/requirements/base.txt b/{{cookiecutter.project_slug}}/requirements/base.txt index 9b2159edf..c2ef23933 100644 --- a/{{cookiecutter.project_slug}}/requirements/base.txt +++ b/{{cookiecutter.project_slug}}/requirements/base.txt @@ -1,5 +1,5 @@ python-slugify==8.0.1 # https://github.com/un33k/python-slugify -Pillow==9.5.0 # https://github.com/python-pillow/Pillow +Pillow==10.0.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 @@ -9,42 +9,42 @@ rcssmin==1.1.1 # https://github.com/ndparker/rcssmin {%- endif %} argon2-cffi==21.3.0 # https://github.com/hynek/argon2_cffi {%- if cookiecutter.use_whitenoise == 'y' %} -whitenoise==6.4.0 # https://github.com/evansd/whitenoise +whitenoise==6.5.0 # https://github.com/evansd/whitenoise {%- endif %} -redis==4.5.5 # https://github.com/redis/redis-py +redis==4.6.0 # https://github.com/redis/redis-py {%- if cookiecutter.use_docker == "y" or cookiecutter.windows == "n" %} hiredis==2.2.3 # https://github.com/redis/hiredis-py {%- endif %} {%- if cookiecutter.use_celery == "y" %} -celery==5.3.0 # pyup: < 6.0 # https://github.com/celery/celery +celery==5.3.1 # pyup: < 6.0 # https://github.com/celery/celery django-celery-beat==2.5.0 # https://github.com/celery/django-celery-beat {%- if cookiecutter.use_docker == 'y' %} -flower==1.2.0 # https://github.com/mher/flower +flower==2.0.1 # https://github.com/mher/flower {%- endif %} {%- endif %} {%- if cookiecutter.use_async == 'y' %} -uvicorn[standard]==0.22.0 # https://github.com/encode/uvicorn +uvicorn[standard]==0.23.2 # https://github.com/encode/uvicorn {%- endif %} # Django # ------------------------------------------------------------------------------ -django==4.1.9 # pyup: < 4.2 # https://www.djangoproject.com/ +django==4.2.4 # pyup: < 5.0 # 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.54.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.3.1 # https://github.com/django-compressor/django-compressor +django-compressor==4.4 # https://github.com/django-compressor/django-compressor {%- endif %} -django-redis==5.2.0 # https://github.com/jazzband/django-redis +django-redis==5.3.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==4.0.0 # https://github.com/adamchainz/django-cors-headers +django-cors-headers==4.2.0 # https://github.com/adamchainz/django-cors-headers # DRF-spectacular for api documentation -drf-spectacular==0.26.2 # https://github.com/tfranzel/drf-spectacular +drf-spectacular==0.26.4 # https://github.com/tfranzel/drf-spectacular {%- endif %} {%- if cookiecutter.frontend_pipeline == 'Webpack' %} -django-webpack-loader==2.0.0 # https://github.com/django-webpack/django-webpack-loader +django-webpack-loader==2.0.1 # https://github.com/django-webpack/django-webpack-loader {%- endif %} diff --git a/{{cookiecutter.project_slug}}/requirements/local.txt b/{{cookiecutter.project_slug}}/requirements/local.txt index ea6f5b61f..96bd10bf5 100644 --- a/{{cookiecutter.project_slug}}/requirements/local.txt +++ b/{{cookiecutter.project_slug}}/requirements/local.txt @@ -1,11 +1,11 @@ -r base.txt -Werkzeug[watchdog]==2.3.5 # https://github.com/pallets/werkzeug +Werkzeug[watchdog]==2.3.6 # https://github.com/pallets/werkzeug ipdb==0.13.13 # https://github.com/gotcha/ipdb {%- if cookiecutter.use_docker == 'y' %} -psycopg2==2.9.6 # https://github.com/psycopg/psycopg2 +psycopg[c]==3.1.9 # https://github.com/psycopg/psycopg {%- else %} -psycopg2-binary==2.9.6 # https://github.com/psycopg/psycopg2 +psycopg[binary]==3.1.9 # https://github.com/psycopg/psycopg {%- endif %} {%- if cookiecutter.use_async == 'y' or cookiecutter.use_celery == 'y' %} watchfiles==0.19.0 # https://github.com/samuelcolvin/watchfiles @@ -13,12 +13,12 @@ watchfiles==0.19.0 # https://github.com/samuelcolvin/watchfiles # Testing # ------------------------------------------------------------------------------ -mypy==1.3.0 # https://github.com/python/mypy -django-stubs[compatible-mypy]==4.2.1 # https://github.com/typeddjango/django-stubs -pytest==7.3.1 # https://github.com/pytest-dev/pytest +mypy==1.4.1 # https://github.com/python/mypy +django-stubs[compatible-mypy]==4.2.3 # https://github.com/typeddjango/django-stubs +pytest==7.4.0 # https://github.com/pytest-dev/pytest pytest-sugar==0.9.7 # https://github.com/Frozenball/pytest-sugar {%- if cookiecutter.use_drf == "y" %} -djangorestframework-stubs[compatible-mypy]==3.14.1 # https://github.com/typeddjango/djangorestframework-stubs +djangorestframework-stubs[compatible-mypy]==3.14.2 # https://github.com/typeddjango/djangorestframework-stubs {%- endif %} # Documentation @@ -28,21 +28,22 @@ sphinx-autobuild==2021.3.14 # https://github.com/GaretJax/sphinx-autobuild # Code quality # ------------------------------------------------------------------------------ -flake8==6.0.0 # https://github.com/PyCQA/flake8 +flake8==6.1.0 # https://github.com/PyCQA/flake8 flake8-isort==6.0.0 # https://github.com/gforcada/flake8-isort -coverage==7.2.7 # https://github.com/nedbat/coveragepy -black==23.3.0 # https://github.com/psf/black +coverage==7.3.0 # https://github.com/nedbat/coveragepy +black==23.7.0 # https://github.com/psf/black +djlint==1.32.1 # https://github.com/Riverside-Healthcare/djLint 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==3.3.2 # https://github.com/pre-commit/pre-commit +pre-commit==3.3.3 # https://github.com/pre-commit/pre-commit # Django # ------------------------------------------------------------------------------ -factory-boy==3.2.1 # https://github.com/FactoryBoy/factory_boy +factory-boy==3.3.0 # https://github.com/FactoryBoy/factory_boy -django-debug-toolbar==4.1.0 # https://github.com/jazzband/django-debug-toolbar +django-debug-toolbar==4.2.0 # https://github.com/jazzband/django-debug-toolbar django-extensions==3.2.3 # https://github.com/django-extensions/django-extensions -django-coverage-plugin==3.0.0 # https://github.com/nedbat/django_coverage_plugin +django-coverage-plugin==3.1.0 # https://github.com/nedbat/django_coverage_plugin pytest-django==4.5.2 # https://github.com/pytest-dev/pytest-django diff --git a/{{cookiecutter.project_slug}}/requirements/production.txt b/{{cookiecutter.project_slug}}/requirements/production.txt index aec79b3d3..da827e7e0 100644 --- a/{{cookiecutter.project_slug}}/requirements/production.txt +++ b/{{cookiecutter.project_slug}}/requirements/production.txt @@ -2,13 +2,13 @@ -r base.txt -gunicorn==20.1.0 # https://github.com/benoitc/gunicorn -psycopg2==2.9.6 # https://github.com/psycopg/psycopg2 +gunicorn==21.2.0 # https://github.com/benoitc/gunicorn +psycopg[c]==3.1.9 # https://github.com/psycopg/psycopg {%- if cookiecutter.use_whitenoise == 'n' %} Collectfast==2.2.0 # https://github.com/antonagestam/collectfast {%- endif %} {%- if cookiecutter.use_sentry == "y" %} -sentry-sdk==1.25.1 # https://github.com/getsentry/sentry-python +sentry-sdk==1.29.2 # https://github.com/getsentry/sentry-python {%- endif %} {%- if cookiecutter.use_docker == "n" and cookiecutter.windows == "y" %} hiredis==2.2.3 # https://github.com/redis/hiredis-py @@ -24,21 +24,21 @@ django-storages[google]==1.13.2 # https://github.com/jschneier/django-storages django-storages[azure]==1.13.2 # https://github.com/jschneier/django-storages {%- endif %} {%- if cookiecutter.mail_service == 'Mailgun' %} -django-anymail[mailgun]==10.0 # https://github.com/anymail/django-anymail +django-anymail[mailgun]==10.1 # https://github.com/anymail/django-anymail {%- elif cookiecutter.mail_service == 'Amazon SES' %} -django-anymail[amazon-ses]==10.0 # https://github.com/anymail/django-anymail +django-anymail[amazon-ses]==10.1 # https://github.com/anymail/django-anymail {%- elif cookiecutter.mail_service == 'Mailjet' %} -django-anymail[mailjet]==10.0 # https://github.com/anymail/django-anymail +django-anymail[mailjet]==10.1 # https://github.com/anymail/django-anymail {%- elif cookiecutter.mail_service == 'Mandrill' %} -django-anymail[mandrill]==10.0 # https://github.com/anymail/django-anymail +django-anymail[mandrill]==10.1 # https://github.com/anymail/django-anymail {%- elif cookiecutter.mail_service == 'Postmark' %} -django-anymail[postmark]==10.0 # https://github.com/anymail/django-anymail +django-anymail[postmark]==10.1 # https://github.com/anymail/django-anymail {%- elif cookiecutter.mail_service == 'Sendgrid' %} -django-anymail[sendgrid]==10.0 # https://github.com/anymail/django-anymail +django-anymail[sendgrid]==10.1 # https://github.com/anymail/django-anymail {%- elif cookiecutter.mail_service == 'SendinBlue' %} -django-anymail[sendinblue]==10.0 # https://github.com/anymail/django-anymail +django-anymail[sendinblue]==10.1 # https://github.com/anymail/django-anymail {%- elif cookiecutter.mail_service == 'SparkPost' %} -django-anymail[sparkpost]==10.0 # https://github.com/anymail/django-anymail +django-anymail[sparkpost]==10.1 # https://github.com/anymail/django-anymail {%- elif cookiecutter.mail_service == 'Other SMTP' %} -django-anymail==10.0 # https://github.com/anymail/django-anymail +django-anymail==10.1 # https://github.com/anymail/django-anymail {%- endif %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/403.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/403.html index 4c4745f7d..d90b33f9b 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/403.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/403.html @@ -1,10 +1,14 @@ {% raw %}{% extends "base.html" %} -{% block title %}Forbidden (403){% endblock %} - +{% block title %}Forbidden (403){% endblock title %} {% block content %} -

Forbidden (403)

- -

{% if exception %}{{ exception }}{% else %}You're not allowed to access this page.{% endif %}

+

Forbidden (403)

+

+ {% if exception %} + {{ exception }} + {% else %} + You're not allowed to access this page. + {% endif %} +

{% endblock content %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/403_csrf.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/403_csrf.html new file mode 100644 index 000000000..d90b33f9b --- /dev/null +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/403_csrf.html @@ -0,0 +1,14 @@ +{% raw %}{% extends "base.html" %} + +{% block title %}Forbidden (403){% endblock title %} +{% block content %} +

Forbidden (403)

+

+ {% if exception %} + {{ exception }} + {% else %} + You're not allowed to access this page. + {% endif %} +

+{% endblock content %} +{%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/404.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/404.html index d98241858..621596412 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/404.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/404.html @@ -1,10 +1,14 @@ {% raw %}{% extends "base.html" %} -{% block title %}Page not found{% endblock %} - +{% block title %}Page not found{% endblock title %} {% block content %} -

Page not found

- -

{% if exception %}{{ exception }}{% else %}This is not the page you were looking for.{% endif %}

+

Page not found

+

+ {% if exception %} + {{ exception }} + {% else %} + This is not the page you were looking for. + {% endif %} +

{% endblock content %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/500.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/500.html index 481bb2d0b..890320164 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/500.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/500.html @@ -1,12 +1,11 @@ {% raw %}{% extends "base.html" %} -{% block title %}Server Error{% endblock %} - +{% block title %}Server Error{% endblock title %} {% block content %} -

Ooops!!! 500

- -

Looks like something went wrong!

- -

We track these errors automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.

+

Ooops!!! 500

+

Looks like something went wrong!

+

+ We track these errors automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing. +

{% endblock content %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/account_inactive.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/account_inactive.html index ab910820e..a9112cf09 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/account_inactive.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/account_inactive.html @@ -2,11 +2,11 @@ {% load i18n %} -{% block head_title %}{% translate "Account Inactive" %}{% endblock %} - +{% block head_title %} + {% translate "Account Inactive" %} +{% endblock head_title %} {% block inner %} -

{% translate "Account Inactive" %}

- -

{% translate "This account is inactive." %}

-{% endblock %} +

{% translate "Account Inactive" %}

+

{% translate "This account is inactive." %}

+{% endblock inner %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/base.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/base.html index 03c86724b..057618257 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/base.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/base.html @@ -1,11 +1,14 @@ {% raw %}{% extends "base.html" %} -{% block title %}{% block head_title %}{% endblock head_title %}{% endblock title %} +{% block title %} + {% block head_title %} + {% endblock head_title %} +{% endblock title %} {% block content %} -
-
- {% block inner %}{% endblock %} +
+
+ {% block inner %}{% endblock inner %} +
-
-{% endblock %} +{% endblock content %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/email.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/email.html index 1faa2b9fd..37770f00c 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/email.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/email.html @@ -4,76 +4,77 @@ {% load i18n %} {% load crispy_forms_tags %} -{% block head_title %}{% translate "Account" %}{% endblock %} - +{% block head_title %} + {% translate "Account" %} +{% endblock head_title %} {% block inner %} -

{% translate "E-mail Addresses" %}

- -{% if user.emailaddress_set.all %} -

{% translate 'The following e-mail addresses are associated with your account:' %}

- - - -{% else %} -

{% translate 'Warning:'%} {% translate "You currently do not have any e-mail address set up. You should really add an e-mail address so you can receive notifications, reset your password, etc." %}

- -{% endif %} - - -

{% translate "Add E-mail Address" %}

- -
- {% csrf_token %} - {{ form|crispy }} - +

{% translate "E-mail Addresses" %}

+ {% if user.emailaddress_set.all %} +

{% translate "The following e-mail addresses are associated with your account:" %}

+ + {% csrf_token %} +
+ {% for emailaddress in user.emailaddress_set.all %} +
+ +
+ {% endfor %} +
+ + + +
+
- -{% endblock %} - - + {% else %} +

+ {% translate "Warning:" %} {% translate "You currently do not have any e-mail address set up. You should really add an e-mail address so you can receive notifications, reset your password, etc." %} +

+ {% endif %} +

{% translate "Add E-mail Address" %}

+
+ {% csrf_token %} + {{ form|crispy }} + +
+{% endblock inner %} {% block inline_javascript %} -{{ block.super }} - -{% endblock %} + +{% endblock inline_javascript %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/email_confirm.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/email_confirm.html index 5e4924c83..40ca4a47b 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/email_confirm.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/email_confirm.html @@ -3,30 +3,26 @@ {% load i18n %} {% load account %} -{% block head_title %}{% translate "Confirm E-mail Address" %}{% endblock %} - - +{% block head_title %} + {% translate "Confirm E-mail Address" %} +{% endblock head_title %} {% block inner %} -

{% translate "Confirm E-mail Address" %}

- -{% if confirmation %} - -{% user_display confirmation.email_address.user as user_display %} - -

{% blocktranslate with confirmation.email_address.email as email %}Please confirm that {{ email }} is an e-mail address for user {{ user_display }}.{% endblocktranslate %}

- -
-{% csrf_token %} - -
- -{% else %} - -{% url 'account_email' as email_url %} - -

{% blocktranslate %}This e-mail confirmation link expired or is invalid. Please issue a new e-mail confirmation request.{% endblocktranslate %}

- -{% endif %} - -{% endblock %} +

{% translate "Confirm E-mail Address" %}

+ {% if confirmation %} + {% user_display confirmation.email_address.user as user_display %} +

+ {% blocktranslate with confirmation.email_address.email as email %}Please confirm that {{ email }} is an e-mail address for user {{ user_display }}.{% endblocktranslate %} +

+
+ {% csrf_token %} + +
+ {% else %} + {% url 'account_email' as email_url %} +

+ {% blocktranslate %}This e-mail confirmation link expired or is invalid. Please issue a new e-mail confirmation request.{% endblocktranslate %} +

+ {% endif %} +{% endblock inner %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/login.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/login.html index 25a292eda..5737afc06 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/login.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/login.html @@ -4,57 +4,50 @@ {% load account socialaccount %} {% load crispy_forms_tags %} -{% block head_title %}{% translate "Sign In" %}{% endblock %} - +{% block head_title %} + {% translate "Sign In" %} +{% endblock head_title %} {% block inner %} - -

{% translate "Sign In" %}

- -{% get_providers as socialaccount_providers %} - -{% if socialaccount_providers %} -

- {% translate "Please sign in with one of your existing third party accounts:" %} - {% if ACCOUNT_ALLOW_REGISTRATION %} - {% blocktranslate trimmed %} - Or, sign up - for a {{ site_name }} account and sign in below: - {% endblocktranslate %} - {% endif %} -

- -
- -
    - {% include "socialaccount/snippets/provider_list.html" with process="login" %} -
- - - -
- - {% include "socialaccount/snippets/login_extra.html" %} - -{% else %} - {% if ACCOUNT_ALLOW_REGISTRATION %} +

{% translate "Sign In" %}

+ {% get_providers as socialaccount_providers %} + {% if socialaccount_providers %}

- {% blocktranslate trimmed %} - If you have not created an account yet, then please - sign up first. - {% endblocktranslate %} + {% translate "Please sign in with one of your existing third party accounts:" %} + {% if ACCOUNT_ALLOW_REGISTRATION %} + {% blocktranslate trimmed %} + Or, sign up + for a {{ site_name }} account and sign in below: + {% endblocktranslate %} + {% endif %}

+
+
    + {% include "socialaccount/snippets/provider_list.html" with process="login" %} +
+ +
+ {% include "socialaccount/snippets/login_extra.html" %} + {% else %} + {% if ACCOUNT_ALLOW_REGISTRATION %} +

+ {% blocktranslate trimmed %} + If you have not created an account yet, then please + sign up first. + {% endblocktranslate %} +

+ {% endif %} {% endif %} -{% endif %} - - - -{% endblock %} + +{% endblock inner %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/logout.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/logout.html index 5edc60478..43ae9ed38 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/logout.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/logout.html @@ -2,19 +2,20 @@ {% load i18n %} -{% block head_title %}{% translate "Sign Out" %}{% endblock %} - +{% block head_title %} + {% translate "Sign Out" %} +{% endblock head_title %} {% block inner %} -

{% translate "Sign Out" %}

- -

{% translate 'Are you sure you want to sign out?' %}

- -
- {% csrf_token %} - {% if redirect_field_value %} - - {% endif %} - -
-{% endblock %} +

{% translate "Sign Out" %}

+

{% translate "Are you sure you want to sign out?" %}

+
+ {% csrf_token %} + {% if redirect_field_value %} + + {% endif %} + +
+{% endblock inner %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_change.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_change.html index b8dd7ac53..2e6110d5d 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_change.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_change.html @@ -3,15 +3,17 @@ {% load i18n %} {% load crispy_forms_tags %} -{% block head_title %}{% translate "Change Password" %}{% endblock %} - +{% block head_title %} + {% translate "Change Password" %} +{% endblock head_title %} {% block inner %} -

{% translate "Change Password" %}

- -
- {% csrf_token %} - {{ form|crispy }} - -
-{% endblock %} +

{% translate "Change Password" %}

+
+ {% csrf_token %} + {{ form|crispy }} + +
+{% endblock inner %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_reset.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_reset.html index f424b2111..0c184269a 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_reset.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_reset.html @@ -4,23 +4,26 @@ {% load account %} {% load crispy_forms_tags %} -{% block head_title %}{% translate "Password Reset" %}{% endblock %} - +{% block head_title %} + {% translate "Password Reset" %} +{% endblock head_title %} {% block inner %} - -

{% translate "Password Reset" %}

- {% if user.is_authenticated %} +

{% translate "Password Reset" %}

+ {% if user.is_authenticated %} {% include "account/snippets/already_logged_in.html" %} - {% endif %} - -

{% translate "Forgotten your password? Enter your e-mail address below, and we'll send you an e-mail allowing you to reset it." %}

- -
- {% csrf_token %} - {{ form|crispy }} - -
- -

{% blocktranslate %}Please contact us if you have any trouble resetting your password.{% endblocktranslate %}

-{% endblock %} + {% endif %} +

+ {% translate "Forgotten your password? Enter your e-mail address below, and we'll send you an e-mail allowing you to reset it." %} +

+
+ {% csrf_token %} + {{ form|crispy }} + +
+

{% blocktranslate %}Please contact us if you have any trouble resetting your password.{% endblocktranslate %}

+{% endblock inner %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_reset_done.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_reset_done.html index 76d07eb21..a596425bb 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_reset_done.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_reset_done.html @@ -3,15 +3,16 @@ {% load i18n %} {% load account %} -{% block head_title %}{% translate "Password Reset" %}{% endblock %} - +{% block head_title %} + {% translate "Password Reset" %} +{% endblock head_title %} {% block inner %} -

{% translate "Password Reset" %}

- - {% if user.is_authenticated %} +

{% translate "Password Reset" %}

+ {% if user.is_authenticated %} {% include "account/snippets/already_logged_in.html" %} - {% endif %} - -

{% blocktranslate %}We have sent you an e-mail. Please contact us if you do not receive it within a few minutes.{% endblocktranslate %}

-{% endblock %} + {% endif %} +

+ {% blocktranslate %}We have sent you an e-mail. Please contact us if you do not receive it within a few minutes.{% endblocktranslate %} +

+{% endblock inner %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_reset_from_key.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_reset_from_key.html index ce5d72a6d..a958ba089 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_reset_from_key.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_reset_from_key.html @@ -2,24 +2,36 @@ {% load i18n %} {% load crispy_forms_tags %} -{% block head_title %}{% translate "Change Password" %}{% endblock %} +{% block head_title %} + {% translate "Change Password" %} +{% endblock head_title %} {% block inner %} -

{% if token_fail %}{% translate "Bad Token" %}{% else %}{% translate "Change Password" %}{% endif %}

- +

{% if token_fail %} - {% url 'account_reset_password' as passwd_reset_url %} -

{% blocktranslate %}The password reset link was invalid, possibly because it has already been used. Please request a new password reset.{% endblocktranslate %}

+ {% translate "Bad Token" %} {% else %} - {% if form %} -
- {% csrf_token %} - {{ form|crispy }} - -
- {% else %} -

{% translate 'Your password is now changed.' %}

- {% endif %} + {% translate "Change Password" %} {% endif %} -{% endblock %} +

+ {% if token_fail %} + {% url 'account_reset_password' as passwd_reset_url %} +

+ {% blocktranslate %}The password reset link was invalid, possibly because it has already been used. Please request a new password reset.{% endblocktranslate %} +

+ {% else %} + {% if form %} +
+ {% csrf_token %} + {{ form|crispy }} + +
+ {% else %} +

{% translate "Your password is now changed." %}

+ {% endif %} + {% endif %} +{% endblock inner %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_reset_from_key_done.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_reset_from_key_done.html index 34123fd53..ee399b404 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_reset_from_key_done.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_reset_from_key_done.html @@ -1,10 +1,12 @@ {% raw %}{% extends "account/base.html" %} {% load i18n %} -{% block head_title %}{% translate "Change Password" %}{% endblock %} +{% block head_title %} + {% translate "Change Password" %} +{% endblock head_title %} {% block inner %} -

{% translate "Change Password" %}

-

{% translate 'Your password is now changed.' %}

-{% endblock %} +

{% translate "Change Password" %}

+

{% translate "Your password is now changed." %}

+{% endblock inner %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_set.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_set.html index 812410fc0..3efc30874 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_set.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/password_set.html @@ -3,15 +3,20 @@ {% load i18n %} {% load crispy_forms_tags %} -{% block head_title %}{% translate "Set Password" %}{% endblock %} - +{% block head_title %} + {% translate "Set Password" %} +{% endblock head_title %} {% block inner %} -

{% translate "Set Password" %}

- -
- {% csrf_token %} - {{ form|crispy }} - -
-{% endblock %} +

{% translate "Set Password" %}

+
+ {% csrf_token %} + {{ form|crispy }} + +
+{% endblock inner %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/signup.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/signup.html index 8c1c11aca..54150a474 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/signup.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/signup.html @@ -3,21 +3,26 @@ {% load i18n %} {% load crispy_forms_tags %} -{% block head_title %}{% translate "Signup" %}{% endblock %} - +{% block head_title %} + {% translate "Signup" %} +{% endblock head_title %} {% block inner %} -

{% translate "Sign Up" %}

- -

{% blocktranslate %}Already have an account? Then please sign in.{% endblocktranslate %}

- - - -{% endblock %} +

{% translate "Sign Up" %}

+

+ {% blocktranslate %}Already have an account? Then please sign in.{% endblocktranslate %} +

+ +{% endblock inner %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/signup_closed.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/signup_closed.html index c2e64d14f..b3472ed6d 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/signup_closed.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/signup_closed.html @@ -2,11 +2,11 @@ {% load i18n %} -{% block head_title %}{% translate "Sign Up Closed" %}{% endblock %} - +{% block head_title %} + {% translate "Sign Up Closed" %} +{% endblock head_title %} {% block inner %} -

{% translate "Sign Up Closed" %}

- -

{% translate "We are sorry, but the sign up is currently closed." %}

-{% endblock %} +

{% translate "Sign Up Closed" %}

+

{% translate "We are sorry, but the sign up is currently closed." %}

+{% endblock inner %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/verification_sent.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/verification_sent.html index be8f1cef9..d71bbc41a 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/verification_sent.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/verification_sent.html @@ -2,12 +2,13 @@ {% load i18n %} -{% block head_title %}{% translate "Verify Your E-mail Address" %}{% endblock %} - +{% block head_title %} + {% translate "Verify Your E-mail Address" %} +{% endblock head_title %} {% block inner %} -

{% translate "Verify Your E-mail Address" %}

- -

{% blocktranslate %}We have sent an e-mail to you for verification. Follow the link provided to finalize the signup process. Please contact us if you do not receive it within a few minutes.{% endblocktranslate %}

- -{% endblock %} +

{% translate "Verify Your E-mail Address" %}

+

+ {% blocktranslate %}We have sent an e-mail to you for verification. Follow the link provided to finalize the signup process. Please contact us if you do not receive it within a few minutes.{% endblocktranslate %} +

+{% endblock inner %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/verified_email_required.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/verified_email_required.html index 2148a1804..b736581ce 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/verified_email_required.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/account/verified_email_required.html @@ -2,21 +2,24 @@ {% load i18n %} -{% block head_title %}{% translate "Verify Your E-mail Address" %}{% endblock %} - +{% block head_title %} + {% translate "Verify Your E-mail Address" %} +{% endblock head_title %} {% block inner %} -

{% translate "Verify Your E-mail Address" %}

- -{% url 'account_email' as email_url %} - -

{% blocktranslate %}This part of the site requires us to verify that +

{% translate "Verify Your E-mail Address" %}

+ {% url 'account_email' as email_url %} +

+ {% blocktranslate %}This part of the site requires us to verify that you are who you claim to be. For this purpose, we require that you -verify ownership of your e-mail address. {% endblocktranslate %}

- -

{% blocktranslate %}We have sent an e-mail to you for +verify ownership of your e-mail address. {% endblocktranslate %} +

+

+ {% blocktranslate %}We have sent an e-mail to you for verification. Please click on the link inside this e-mail. Please -contact us if you do not receive it within a few minutes.{% endblocktranslate %}

- -

{% blocktranslate %}Note: you can still change your e-mail address.{% endblocktranslate %}

-{% endblock %} +contact us if you do not receive it within a few minutes.{% endblocktranslate %} +

+

+ {% blocktranslate %}Note: you can still change your e-mail address.{% endblocktranslate %} +

+{% endblock inner %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/base.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/base.html index 44f0d5c52..421973e57 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/base.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/base.html @@ -1,150 +1,194 @@ -{% 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 %} +{% 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 %} {% get_current_language as LANGUAGE_CODE %} - - - {% block title %}{% endraw %}{{ cookiecutter.project_name }}{% raw %}{% endblock title %} - - - - - - - {% block css %} - {%- endraw %} - {%- if cookiecutter.frontend_pipeline in ['None', 'Django Compressor'] %} + + + + {% block title %} + {% endraw %}{{ cookiecutter.project_name }}{% raw %} + {% endblock title %} + + + + + + {% block css %} + {%- endraw %} + {%- if cookiecutter.frontend_pipeline in ['None', 'Django Compressor'] %} {%- raw %} - - {%- endraw %} - {%- endif %} - {%- raw %} - - - - {%- endraw %}{% if cookiecutter.frontend_pipeline == 'None' %}{% raw %} - - {%- endraw %}{% elif cookiecutter.frontend_pipeline == 'Django Compressor' %}{% raw %} - {% compress css %} - - {% endcompress %} - {%- endraw %}{% elif cookiecutter.frontend_pipeline == 'Gulp' %}{% raw %} - - {%- endraw %}{% elif cookiecutter.frontend_pipeline == "Webpack" %}{% raw %} - {% render_bundle 'project' 'css' %} - {%- endraw %}{% endif %}{% raw %} - {% endblock %} - + +{%- endraw %} +{% if cookiecutter.frontend_pipeline == 'None' %} + {% raw %} + +{%- endraw %} +{% elif cookiecutter.frontend_pipeline == 'Django Compressor' %} +{% raw %} +{% compress css %} + +{% endcompress %} +{%- endraw %} +{% elif cookiecutter.frontend_pipeline == 'Gulp' %} +{% raw %} + +{%- endraw %} +{% elif cookiecutter.frontend_pipeline == "Webpack" %} +{% raw %} +{% render_bundle 'project' 'css' %} +{%- endraw %} +{% endif %} +{% raw %} +{% endblock css %} + - {# Placed at the top of the document so pages load faster with defer #} - {% block javascript %} - {%- endraw %}{% if cookiecutter.frontend_pipeline == 'Gulp' %}{% raw %} - - - {%- endraw %}{% elif cookiecutter.frontend_pipeline == "Webpack" %}{% raw %} - - {% render_bundle 'vendors' 'js' attrs='defer' %} - {%- endraw %}{% else %}{% raw %} - - - - {%- endraw %}{% endif %}{% raw %} - - - {%- endraw %}{% if cookiecutter.frontend_pipeline == 'None' %}{% raw %} - - {%- endraw %}{% elif cookiecutter.frontend_pipeline == 'Django Compressor' %}{% raw %} - {% compress js %} - - {% endcompress %} - {%- endraw %}{% elif cookiecutter.frontend_pipeline == 'Gulp' %}{% raw %} - - {%- endraw %}{% elif cookiecutter.frontend_pipeline == "Webpack" %}{% raw %} - {% render_bundle 'project' 'js' attrs='defer' %} - {%- endraw %}{% endif %}{% raw %} - - {% endblock javascript %} - - - - - -
- - -
- -
- - {% if messages %} - {% for message in messages %} -
- {{ message }} - -
- {% endfor %} - {% endif %} - - {% block content %} -

Use this document as a way to quick start any new project.

- {% endblock content %} - -
- - {% block modal %}{% endblock modal %} - - {% block inline_javascript %} +
+ + +
+ {% if messages %} + {% for message in messages %} +
+ {{ message }} + +
+ {% endfor %} + {% endif %} + {% block content %} +

Use this document as a way to quick start any new project.

+ {% endblock content %} +
+ + {% block modal %} + {% endblock modal %} + {% block inline_javascript %} {% comment %} Script tags with only code, no src (defer by default). To run with a "defer" so that you run inline code: {% endcomment %} - {% endblock inline_javascript %} - + {% endblock inline_javascript %} + {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/pages/about.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/pages/about.html index 8968a3d4f..3d301eead 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/pages/about.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/pages/about.html @@ -1 +1,3 @@ -{% raw %}{% extends "base.html" %}{% endraw %} +{% raw %}{% extends "base.html" %} + +{% endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/pages/home.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/pages/home.html index 8968a3d4f..3d301eead 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/pages/home.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/pages/home.html @@ -1 +1,3 @@ -{% raw %}{% extends "base.html" %}{% endraw %} +{% raw %}{% extends "base.html" %} + +{% endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/users/user_detail.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/users/user_detail.html index ee2c4aee9..ab36ba56b 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/users/user_detail.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/users/user_detail.html @@ -1,35 +1,51 @@ {% raw %}{% extends "base.html" %} + {% load static %} -{% block title %}User: {% endraw %}{% if cookiecutter.username_type == "email" %}{% raw %}{{ object.name }}{% endraw %}{% else %}{% raw %}{{ object.username }}{% endraw %}{% endif %}{% raw %}{% endblock %} - +{% block title %} + User: {% endraw %} + {% if cookiecutter.username_type == "email" %} + {% raw %}{{ object.name }}{% endraw %} + {% else %} + {% raw %}{{ object.username }}{% endraw %} + {% endif %} + {% raw %} +{% endblock title %} {% block content %} -
- -
-
- -

{% endraw %}{% if cookiecutter.username_type == "email" %}{% raw %}{{ object.name }}{% endraw %}{% else %}{% raw %}{{ object.username }}{% endraw %}{% endif %}{% raw %}

+
+
+
+

+ {% endraw %} + {% if cookiecutter.username_type == "email" %} + {% raw %}{{ object.name }}{% endraw %} + {% else %} + {% raw %}{{ object.username }}{% endraw %} + {% endif %} +

+ {%- if cookiecutter.username_type == "username" %} + {%- raw %} {% if object.name %}

{{ object.name }}

{% endif %} + {%- endraw %} + {%- endif %}
- -{% if object == request.user %} - -
- -
- My Info - E-Mail - -
- -
- -{% endif %} - + {%- raw %} + {% if object == request.user %} + +
+
+ My Info + E-Mail + +
+
+ + {% endif %}
{% endblock content %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/users/user_form.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/users/user_form.html index 65401624a..bd1299a6d 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/users/user_form.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/users/user_form.html @@ -1,18 +1,36 @@ {% raw %}{% extends "base.html" %} + {% load crispy_forms_tags %} -{% block title %}{% endraw %}{% if cookiecutter.username_type == "email" %}{% raw %}{{ user.name }}{% endraw %}{% else %}{% raw %}{{ user.username }}{% endraw %}{% endif %}{% raw %}{% endblock %} - +{% block title %} +{% endraw %} +{% if cookiecutter.username_type == "email" %} + {% raw %}{{ user.name }}{% endraw %} +{% else %} + {% raw %}{{ user.username }}{% endraw %} +{% endif %} +{% raw %} +{% endblock title %} {% block content %} -

{% endraw %}{% if cookiecutter.username_type == "email" %}{% raw %}{{ user.name }}{% endraw %}{% else %}{% raw %}{{ user.username }}{% endraw %}{% endif %}{% raw %}

-
- {% csrf_token %} - {{ form|crispy }} -
-
- -
+

+ {% endraw %} + {% if cookiecutter.username_type == "email" %} + {% raw %}{{ user.name }}{% endraw %} + {% else %} + {% raw %}{{ user.username }}{% endraw %} + {% endif %} + {% raw %} +

+ + {% csrf_token %} + {{ form|crispy }} +
+
+
- -{% endblock %} +
+ +{% endblock content %} {%- endraw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/adapters.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/adapters.py index 0d206fae4..c5c824bda 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/adapters.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/adapters.py @@ -1,16 +1,37 @@ -from typing import Any +from __future__ import annotations + +import typing from allauth.account.adapter import DefaultAccountAdapter from allauth.socialaccount.adapter import DefaultSocialAccountAdapter from django.conf import settings from django.http import HttpRequest +if typing.TYPE_CHECKING: + from allauth.socialaccount.models import SocialLogin + from {{cookiecutter.project_slug}}.users.models import User + class AccountAdapter(DefaultAccountAdapter): - def is_open_for_signup(self, request: HttpRequest): + def is_open_for_signup(self, request: HttpRequest) -> bool: return getattr(settings, "ACCOUNT_ALLOW_REGISTRATION", True) class SocialAccountAdapter(DefaultSocialAccountAdapter): - def is_open_for_signup(self, request: HttpRequest, sociallogin: Any): + def is_open_for_signup(self, request: HttpRequest, sociallogin: SocialLogin) -> bool: return getattr(settings, "ACCOUNT_ALLOW_REGISTRATION", True) + + def populate_user(self, request: HttpRequest, sociallogin: SocialLogin, data: dict[str, typing.Any]) -> User: + """ + Populates user information from social provider info. + + See: https://django-allauth.readthedocs.io/en/latest/advanced.html?#creating-and-populating-user-instances + """ + user = sociallogin.user + if name := data.get("name"): + user.name = name + elif first_name := data.get("first_name"): + user.name = first_name + if last_name := data.get("last_name"): + user.name += f" {last_name}" + return super().populate_user(request, sociallogin, data) diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/admin.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/admin.py index d81c0a3b0..a5f89dd67 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/admin.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/admin.py @@ -1,12 +1,18 @@ +from django.conf import settings from django.contrib import admin from django.contrib.auth import admin as auth_admin -from django.contrib.auth import get_user_model +from django.contrib.auth import get_user_model, decorators from django.utils.translation import gettext_lazy as _ from {{ cookiecutter.project_slug }}.users.forms import UserAdminChangeForm, UserAdminCreationForm User = get_user_model() +if settings.DJANGO_ADMIN_FORCE_ALLAUTH: + # Force the `admin` sign in process to go through the `django-allauth` workflow: + # https://django-allauth.readthedocs.io/en/stable/advanced.html#admin + admin.site.login = decorators.login_required(admin.site.login) # type: ignore[method-assign] + @admin.register(User) class UserAdmin(auth_admin.UserAdmin): diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/serializers.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/serializers.py index 6b26367d0..0872d06f4 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/serializers.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/api/serializers.py @@ -1,10 +1,13 @@ from django.contrib.auth import get_user_model from rest_framework import serializers +from {{ cookiecutter.project_slug }}.users.models import User as UserType + + User = get_user_model() -class UserSerializer(serializers.ModelSerializer): +class UserSerializer(serializers.ModelSerializer[UserType]): class Meta: model = User {%- if cookiecutter.username_type == "email" %}