mirror of
https://github.com/cookiecutter/cookiecutter-django.git
synced 2025-08-14 00:44:53 +03:00
Merge branch 'master' into masavini-master
This commit is contained in:
commit
a5b14d71e9
10
.github/CONTRIBUTORS-template.md
vendored
10
.github/CONTRIBUTORS-template.md
vendored
|
@ -22,8 +22,8 @@ accept and merge pull requests.
|
|||
{%- endfor %}
|
||||
</table>
|
||||
|
||||
*Audrey is also the creator of Cookiecutter. Audrey and Daniel are on
|
||||
the Cookiecutter core team.*
|
||||
_Audrey is also the creator of Cookiecutter. Audrey and Daniel are on
|
||||
the Cookiecutter core team._
|
||||
|
||||
## Other Contributors
|
||||
|
||||
|
@ -51,6 +51,6 @@ Listed in alphabetical order.
|
|||
The following haven't provided code directly, but have provided
|
||||
guidance and advice.
|
||||
|
||||
- Jannis Leidel
|
||||
- Nate Aune
|
||||
- Barry Morrison
|
||||
- Jannis Leidel
|
||||
- Nate Aune
|
||||
- Barry Morrison
|
||||
|
|
68
.github/ISSUE_TEMPLATE/bug.md
vendored
68
.github/ISSUE_TEMPLATE/bug.md
vendored
|
@ -12,41 +12,47 @@ labels: bug
|
|||
|
||||
<!-- To assist you best, please include commands that you've run, options you've selected and any relevant logs -->
|
||||
|
||||
* Host system configuration:
|
||||
* Version of cookiecutter CLI (get it with `cookiecutter --version`):
|
||||
* OS name and version:
|
||||
- Host system configuration:
|
||||
|
||||
On Linux, run
|
||||
```bash
|
||||
lsb_release -a 2> /dev/null || cat /etc/redhat-release 2> /dev/null || cat /etc/*-release 2> /dev/null || cat /etc/issue 2> /dev/null
|
||||
```
|
||||
- Version of cookiecutter CLI (get it with `cookiecutter --version`):
|
||||
- OS name and version:
|
||||
|
||||
On MacOs, run
|
||||
```bash
|
||||
sw_vers
|
||||
```
|
||||
On Linux, run
|
||||
|
||||
On Windows, via CMD, run
|
||||
```
|
||||
systeminfo | findstr /B /C:"OS Name" /C:"OS Version"
|
||||
```
|
||||
|
||||
|
||||
```bash
|
||||
# Insert here the OS name and version
|
||||
|
||||
```
|
||||
|
||||
* Python version, run `python3 -V`:
|
||||
* Docker version (if using Docker), run `docker --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):
|
||||
On Linux and MacOS: `cat ${HOME}/.cookiecutter_replay/cookiecutter-django.json`
|
||||
(Please, take care to remove sensitive information)
|
||||
```json
|
||||
# Insert here the replay file content
|
||||
```bash
|
||||
lsb_release -a 2> /dev/null || cat /etc/redhat-release 2> /dev/null || cat /etc/*-release 2> /dev/null || cat /etc/issue 2> /dev/null
|
||||
```
|
||||
|
||||
On MacOs, run
|
||||
|
||||
```bash
|
||||
sw_vers
|
||||
```
|
||||
|
||||
On Windows, via CMD, run
|
||||
|
||||
```
|
||||
systeminfo | findstr /B /C:"OS Name" /C:"OS Version"
|
||||
```
|
||||
|
||||
```bash
|
||||
# Insert here the OS name and version
|
||||
|
||||
```
|
||||
|
||||
- Python version, run `python3 -V`:
|
||||
- Docker version (if using Docker), run `docker --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):
|
||||
On Linux and macOS: `cat ${HOME}/.cookiecutter_replay/cookiecutter-django.json`
|
||||
(Please, take care to remove sensitive information)
|
||||
|
||||
```json
|
||||
|
||||
```
|
||||
|
||||
<summary>
|
||||
Logs:
|
||||
<details>
|
||||
|
|
6
.github/ISSUE_TEMPLATE/paid-support.md
vendored
6
.github/ISSUE_TEMPLATE/paid-support.md
vendored
|
@ -5,8 +5,8 @@ about: Ask Core Team members to help you out
|
|||
|
||||
Provided your question goes beyond [regular support](https://github.com/cookiecutter/cookiecutter-django/issues/new?template=question.md), and/or the task at hand is of timely/high priority nature use the below information to reach out for contributors directly.
|
||||
|
||||
* Daniel Roy Greenfeld, Project Lead ([GitHub](https://github.com/pydanny), [Patreon](https://www.patreon.com/danielroygreenfeld)): expertise in Django and AWS ELB.
|
||||
- Bruno Alla, Core Developer ([GitHub](https://github.com/sponsors/browniebroke)).
|
||||
|
||||
* Nikita Shupeyko, Core Developer ([GitHub](https://github.com/webyneter)): expertise in Python/Django, hands-on DevOps and frontend experience.
|
||||
- Daniel Roy Greenfeld, Project Lead ([GitHub](https://github.com/pydanny), [Patreon](https://www.patreon.com/danielroygreenfeld)): expertise in Django and AWS ELB.
|
||||
|
||||
* Bruno Alla, Core Developer ([GitHub](https://github.com/sponsors/browniebroke)).
|
||||
- Nikita Shupeyko, Core Developer ([GitHub](https://github.com/webyneter)): expertise in Python/Django, hands-on DevOps and frontend experience.
|
||||
|
|
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -1,6 +1,5 @@
|
|||
<!-- Thank you for helping us out: your efforts mean a great deal to the project and the community as a whole! -->
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
<!-- What's it you're proposing? -->
|
||||
|
|
9
.github/changelog-template.md
vendored
9
.github/changelog-template.md
vendored
|
@ -1,8 +1,11 @@
|
|||
{%- for change_type, pulls in grouped_pulls.items() %}
|
||||
{%- if pulls %}
|
||||
|
||||
### {{ change_type }}
|
||||
|
||||
{%- for pull_request in pulls %}
|
||||
|
||||
- {{ pull_request.title }} ([#{{ pull_request.number }}]({{ pull_request.html_url }}))
|
||||
{%- endfor -%}
|
||||
{% endif -%}
|
||||
{% endfor -%}
|
||||
{%- endfor -%}
|
||||
{% endif -%}
|
||||
{% endfor -%}
|
||||
|
|
10
.github/contributors.json
vendored
10
.github/contributors.json
vendored
|
@ -1377,5 +1377,15 @@
|
|||
"name": "Arkadiusz Michał Ryś",
|
||||
"github_login": "arrys",
|
||||
"twitter_username": ""
|
||||
},
|
||||
{
|
||||
"name": "mpsantos",
|
||||
"github_login": "mpsantos",
|
||||
"twitter_username": ""
|
||||
},
|
||||
{
|
||||
"name": "Morten Kaae",
|
||||
"github_login": "MortenKaae",
|
||||
"twitter_username": ""
|
||||
}
|
||||
]
|
15
.github/dependabot.yml
vendored
15
.github/dependabot.yml
vendored
|
@ -27,6 +27,11 @@ updates:
|
|||
directory: "{{cookiecutter.project_slug}}/compose/local/django/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types:
|
||||
- "version-update:semver-major"
|
||||
- "version-update:semver-minor"
|
||||
labels:
|
||||
- "update"
|
||||
|
||||
|
@ -34,6 +39,11 @@ updates:
|
|||
directory: "{{cookiecutter.project_slug}}/compose/local/docs/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types:
|
||||
- "version-update:semver-major"
|
||||
- "version-update:semver-minor"
|
||||
labels:
|
||||
- "update"
|
||||
|
||||
|
@ -55,6 +65,11 @@ updates:
|
|||
directory: "{{cookiecutter.project_slug}}/compose/production/django/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types:
|
||||
- "version-update:semver-major"
|
||||
- "version-update:semver-minor"
|
||||
labels:
|
||||
- "update"
|
||||
|
||||
|
|
14
.github/workflows/ci.yml
vendored
14
.github/workflows/ci.yml
vendored
|
@ -2,7 +2,7 @@ name: CI
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master", "main" ]
|
||||
branches: ["master", "main"]
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
|
@ -25,7 +25,7 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: "3.11"
|
||||
cache: pip
|
||||
- name: Install dependencies
|
||||
run: pip install -r requirements.txt
|
||||
|
@ -38,7 +38,7 @@ jobs:
|
|||
matrix:
|
||||
script:
|
||||
- name: Basic
|
||||
args: ""
|
||||
args: "ci_tool=Gitlab"
|
||||
- name: Celery & DRF
|
||||
args: "use_celery=y use_drf=y"
|
||||
- name: Gulp
|
||||
|
@ -56,7 +56,7 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: "3.11"
|
||||
cache: pip
|
||||
- name: Install dependencies
|
||||
run: pip install -r requirements.txt
|
||||
|
@ -73,7 +73,9 @@ jobs:
|
|||
- name: Gulp
|
||||
args: "frontend_pipeline=Gulp"
|
||||
- name: Webpack
|
||||
args: "frontend_pipeline=Webpack"
|
||||
args: "frontend_pipeline=Webpack use_heroku=y"
|
||||
- name: Email Username
|
||||
args: "username_type=email ci_tool=Github"
|
||||
|
||||
name: "Bare metal ${{ matrix.script.name }}"
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -98,7 +100,7 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: "3.11"
|
||||
cache: pip
|
||||
cache-dependency-path: |
|
||||
requirements.txt
|
||||
|
|
2
.github/workflows/django-issue-checker.yml
vendored
2
.github/workflows/django-issue-checker.yml
vendored
|
@ -19,7 +19,7 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: "3.11"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
|
|
8
.github/workflows/pre-commit-autoupdate.yml
vendored
8
.github/workflows/pre-commit-autoupdate.yml
vendored
|
@ -16,15 +16,15 @@ jobs:
|
|||
# Disables this workflow from running in a repository that is not part of the indicated organization/user
|
||||
if: github.repository_owner == 'cookiecutter'
|
||||
permissions:
|
||||
contents: write # for peter-evans/create-pull-request to create branch
|
||||
pull-requests: write # for peter-evans/create-pull-request to create a PR
|
||||
contents: write # for peter-evans/create-pull-request to create branch
|
||||
pull-requests: write # for peter-evans/create-pull-request to create a PR
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Install pre-commit
|
||||
run: pip install pre-commit
|
||||
|
@ -37,7 +37,7 @@ jobs:
|
|||
run: pre-commit autoupdate
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
uses: peter-evans/create-pull-request@v5
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
branch: update/pre-commit-autoupdate
|
||||
|
|
2
.github/workflows/update-changelog.yml
vendored
2
.github/workflows/update-changelog.yml
vendored
|
@ -19,7 +19,7 @@ jobs:
|
|||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: "3.11"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
|
|
4
.github/workflows/update-contributors.yml
vendored
4
.github/workflows/update-contributors.yml
vendored
|
@ -13,7 +13,7 @@ jobs:
|
|||
# Disables this workflow from running in a repository that is not part of the indicated organization/user
|
||||
if: github.repository_owner == 'cookiecutter'
|
||||
permissions:
|
||||
contents: write # for stefanzweifel/git-auto-commit-action to push code in repo
|
||||
contents: write # for stefanzweifel/git-auto-commit-action to push code in repo
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
@ -22,7 +22,7 @@ jobs:
|
|||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: "3.11"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
exclude: "{{cookiecutter.project_slug}}"
|
||||
exclude: "{{cookiecutter.project_slug}}|.github/contributors.json|CHANGELOG.md|CONTRIBUTORS.md"
|
||||
default_stages: [commit]
|
||||
|
||||
repos:
|
||||
|
@ -6,17 +6,31 @@ repos:
|
|||
rev: v4.4.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-json
|
||||
- id: check-toml
|
||||
- id: check-xml
|
||||
- id: check-yaml
|
||||
- id: debug-statements
|
||||
- id: check-builtin-literals
|
||||
- id: check-case-conflict
|
||||
- id: detect-private-key
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: "v3.0.0-alpha.6"
|
||||
hooks:
|
||||
- id: prettier
|
||||
args: ["--tab-width", "2"]
|
||||
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.3.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py310-plus]
|
||||
args: [--py311-plus]
|
||||
exclude: hooks/
|
||||
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.1.0
|
||||
rev: 23.3.0
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
|
|
139
CHANGELOG.md
139
CHANGELOG.md
|
@ -3,6 +3,145 @@ All enhancements and patches to Cookiecutter Django will be documented in this f
|
|||
|
||||
<!-- GENERATOR_PLACEHOLDER -->
|
||||
|
||||
## 2023.04.13
|
||||
|
||||
### Updated
|
||||
- Update tox to 4.4.12 ([#4271](https://github.com/cookiecutter/cookiecutter-django/pull/4271))
|
||||
|
||||
## 2023.04.10
|
||||
|
||||
### Updated
|
||||
- Update pytest-sugar to 0.9.7 ([#4269](https://github.com/cookiecutter/cookiecutter-django/pull/4269))
|
||||
- Update pytest to 7.3.0 ([#4268](https://github.com/cookiecutter/cookiecutter-django/pull/4268))
|
||||
|
||||
## 2023.04.07
|
||||
|
||||
### Updated
|
||||
- Upgrade traefik to 2.9.10 ([#4267](https://github.com/cookiecutter/cookiecutter-django/pull/4267))
|
||||
|
||||
## 2023.04.05
|
||||
|
||||
### Changed
|
||||
- Update indent for nginx config file ([#4260](https://github.com/cookiecutter/cookiecutter-django/pull/4260))
|
||||
### Updated
|
||||
- Update tox to 4.4.11 ([#4262](https://github.com/cookiecutter/cookiecutter-django/pull/4262))
|
||||
- Update django to 4.1.8 ([#4258](https://github.com/cookiecutter/cookiecutter-django/pull/4258))
|
||||
- Update pre-commit to 3.2.2 ([#4259](https://github.com/cookiecutter/cookiecutter-django/pull/4259))
|
||||
|
||||
## 2023.04.04
|
||||
|
||||
### Changed
|
||||
- Upgrade to Django 4.1 ([#4028](https://github.com/cookiecutter/cookiecutter-django/pull/4028))
|
||||
- Remove deprecated security setting ([#4247](https://github.com/cookiecutter/cookiecutter-django/pull/4247))
|
||||
### Fixed
|
||||
- Replace `runserver_plus` with `runserver` ([#4255](https://github.com/cookiecutter/cookiecutter-django/pull/4255))
|
||||
- Fix traefik rule priority for media router ([#4244](https://github.com/cookiecutter/cookiecutter-django/pull/4244))
|
||||
### Updated
|
||||
- Update sentry-sdk to 1.19.0 ([#4254](https://github.com/cookiecutter/cookiecutter-django/pull/4254))
|
||||
- Update django-debug-toolbar to 4.0.0 ([#4251](https://github.com/cookiecutter/cookiecutter-django/pull/4251))
|
||||
|
||||
## 2023.04.03
|
||||
|
||||
### Changed
|
||||
- fix: Syntax for ignoring specific noqa errors ([#4250](https://github.com/cookiecutter/cookiecutter-django/pull/4250))
|
||||
### Updated
|
||||
- Update psycopg2-binary to 2.9.6 ([#4249](https://github.com/cookiecutter/cookiecutter-django/pull/4249))
|
||||
- Update psycopg2 to 2.9.6 ([#4248](https://github.com/cookiecutter/cookiecutter-django/pull/4248))
|
||||
|
||||
## 2023.04.01
|
||||
|
||||
### Updated
|
||||
- Update pytest-instafail to 0.5.0 ([#4240](https://github.com/cookiecutter/cookiecutter-django/pull/4240))
|
||||
- Update pillow to 9.5.0 ([#4242](https://github.com/cookiecutter/cookiecutter-django/pull/4242))
|
||||
- Update django-allauth to 0.54.0 ([#4241](https://github.com/cookiecutter/cookiecutter-django/pull/4241))
|
||||
|
||||
## 2023.03.29
|
||||
|
||||
### Updated
|
||||
- Update redis to 4.5.4 ([#4239](https://github.com/cookiecutter/cookiecutter-django/pull/4239))
|
||||
- Update pytz to 2023.3 ([#4238](https://github.com/cookiecutter/cookiecutter-django/pull/4238))
|
||||
- Update black to 23.3.0 ([#4236](https://github.com/cookiecutter/cookiecutter-django/pull/4236))
|
||||
|
||||
## 2023.03.27
|
||||
|
||||
### Updated
|
||||
- Update watchfiles to 0.19.0 ([#4232](https://github.com/cookiecutter/cookiecutter-django/pull/4232))
|
||||
|
||||
## 2023.03.26
|
||||
|
||||
### Updated
|
||||
- Update pre-commit to 3.2.1 ([#4229](https://github.com/cookiecutter/cookiecutter-django/pull/4229))
|
||||
|
||||
## 2023.03.25
|
||||
|
||||
### Updated
|
||||
- Update pytz to 2023.2 ([#4228](https://github.com/cookiecutter/cookiecutter-django/pull/4228))
|
||||
|
||||
## 2023.03.23
|
||||
|
||||
### Updated
|
||||
- Bump traefik from 2.9.8 to 2.9.9 ([#4225](https://github.com/cookiecutter/cookiecutter-django/pull/4225))
|
||||
|
||||
## 2023.03.22
|
||||
|
||||
### Updated
|
||||
- Update redis to 4.5.3 ([#4227](https://github.com/cookiecutter/cookiecutter-django/pull/4227))
|
||||
|
||||
## 2023.03.20
|
||||
|
||||
### Updated
|
||||
- Update django-allauth to 0.53.1 ([#4223](https://github.com/cookiecutter/cookiecutter-django/pull/4223))
|
||||
- Update redis to 4.5.2 ([#4222](https://github.com/cookiecutter/cookiecutter-django/pull/4222))
|
||||
|
||||
## 2023.03.18
|
||||
|
||||
### Updated
|
||||
- Update drf-spectacular to 0.26.1 ([#4221](https://github.com/cookiecutter/cookiecutter-django/pull/4221))
|
||||
- Update pygithub to 1.58.1 ([#4220](https://github.com/cookiecutter/cookiecutter-django/pull/4220))
|
||||
- Update pre-commit to 3.2.0 ([#4219](https://github.com/cookiecutter/cookiecutter-django/pull/4219))
|
||||
|
||||
## 2023.03.16
|
||||
|
||||
### Changed
|
||||
- Pin base Python Docker images to bugfix ([#4194](https://github.com/cookiecutter/cookiecutter-django/pull/4194))
|
||||
### Fixed
|
||||
- Trim leading and trailing space in `domain_name` and `email` ([#4163](https://github.com/cookiecutter/cookiecutter-django/pull/4163))
|
||||
### Updated
|
||||
- Update djangorestframework-stubs to 1.10.0 ([#4217](https://github.com/cookiecutter/cookiecutter-django/pull/4217))
|
||||
- Update django-stubs to 1.16.0 ([#4216](https://github.com/cookiecutter/cookiecutter-django/pull/4216))
|
||||
- Update coverage to 7.2.2 ([#4218](https://github.com/cookiecutter/cookiecutter-django/pull/4218))
|
||||
- Update sentry-sdk to 1.17.0 ([#4215](https://github.com/cookiecutter/cookiecutter-django/pull/4215))
|
||||
- Bump Docker python image from 3.10.9 to 3.10.10 on production Django ([#4214](https://github.com/cookiecutter/cookiecutter-django/pull/4214))
|
||||
- Bump Docker python image from 3.10.9-slim-bullseye to 3.10.10-slim-bullseye for docs ([#4213](https://github.com/cookiecutter/cookiecutter-django/pull/4213))
|
||||
- Bump Docker python image from 3.10.9-slim-bullseye to 3.10.10-slim-bullseye for local Django service ([#4212](https://github.com/cookiecutter/cookiecutter-django/pull/4212))
|
||||
- Update uvicorn to 0.21.1 ([#4211](https://github.com/cookiecutter/cookiecutter-django/pull/4211))
|
||||
- Update django-allauth to 0.53.0 ([#4210](https://github.com/cookiecutter/cookiecutter-django/pull/4210))
|
||||
|
||||
## 2023.03.14
|
||||
|
||||
### Updated
|
||||
- Update django-celery-beat to 2.5.0 ([#4208](https://github.com/cookiecutter/cookiecutter-django/pull/4208))
|
||||
|
||||
## 2023.03.13
|
||||
|
||||
### Updated
|
||||
- Update uvicorn to 0.21.0 ([#4203](https://github.com/cookiecutter/cookiecutter-django/pull/4203))
|
||||
- Update django-anymail to 9.1 ([#4206](https://github.com/cookiecutter/cookiecutter-django/pull/4206))
|
||||
- Update tox to 4.4.7 ([#4207](https://github.com/cookiecutter/cookiecutter-django/pull/4207))
|
||||
|
||||
## 2023.03.09
|
||||
|
||||
### Fixed
|
||||
- Fix the omit configuration for coverage ([#4201](https://github.com/cookiecutter/cookiecutter-django/pull/4201))
|
||||
### Updated
|
||||
- Update ipdb to 0.13.13 ([#4202](https://github.com/cookiecutter/cookiecutter-django/pull/4202))
|
||||
|
||||
## 2023.03.07
|
||||
|
||||
### Updated
|
||||
- Update mypy to 1.1.1 ([#4196](https://github.com/cookiecutter/cookiecutter-django/pull/4196))
|
||||
- Update django-environ to 0.10.0 ([#4195](https://github.com/cookiecutter/cookiecutter-django/pull/4195))
|
||||
|
||||
## 2023.03.04
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
## Code of Conduct
|
||||
|
||||
Everyone who interacts in the Cookiecutter project's codebase, issue trackers, chat rooms, and mailing lists is expected to follow the [PyPA Code of Conduct](https://www.pypa.io/en/latest/code-of-conduct/).
|
||||
Everyone who interacts in the Cookiecutter project's codebase, issue trackers, chat rooms, and mailing lists is expected to follow the [PSF Code of Conduct](https://www.python.org/psf/conduct/)
|
||||
|
|
|
@ -2,41 +2,81 @@
|
|||
|
||||
Always happy to get issues identified and pull requests!
|
||||
|
||||
## Getting your pull request merged in
|
||||
## General considerations
|
||||
|
||||
1. Keep it small. The smaller the pull request, the more likely we are to accept.
|
||||
2. Pull requests that fix a current issue get priority for review.
|
||||
1. Keep it small. The smaller the change, the more likely we are to accept.
|
||||
2. Changes that fix a current issue get priority for review.
|
||||
3. Check out [GitHub guide][submit-a-pr] if you've never created a pull request before.
|
||||
|
||||
## Getting started
|
||||
|
||||
1. Fork the repo
|
||||
2. Clone your fork
|
||||
3. Create a branch for your changes
|
||||
|
||||
This last step is very important, don't start developing from master, it'll cause pain if you need to send another change later.
|
||||
|
||||
## Testing
|
||||
|
||||
You'll need to run the tests using Python 3.11. We recommend using [tox](https://tox.readthedocs.io/en/latest/) to run the tests. It will automatically create a fresh virtual environment and install our test dependencies, such as [pytest-cookies](https://pypi.python.org/pypi/pytest-cookies/) and [flake8](https://pypi.python.org/pypi/flake8/).
|
||||
|
||||
We'll also run the tests on GitHub actions when you send your pull request, but it's a good idea to run them locally before you send it.
|
||||
|
||||
### Installation
|
||||
|
||||
Please install [tox](https://tox.readthedocs.io/en/latest/), which is a generic virtualenv management and test command line tool.
|
||||
First, make sure that your version of Python is 3.11:
|
||||
|
||||
[tox](https://tox.readthedocs.io/en/latest/) is available for download from [PyPI](https://pypi.python.org/pypi) via [pip](https://pypi.python.org/pypi/pip/):
|
||||
```bash
|
||||
$ python --version
|
||||
Python 3.11.3
|
||||
```
|
||||
|
||||
$ pip install tox
|
||||
Any version that starts with 3.11 will do. If you need to install it, you can get it from [python.org](https://www.python.org/downloads/).
|
||||
|
||||
It will automatically create a fresh virtual environment and install our test dependencies,
|
||||
such as [pytest-cookies](https://pypi.python.org/pypi/pytest-cookies/) and [flake8](https://pypi.python.org/pypi/flake8/).
|
||||
Then install `tox`, if not already installed:
|
||||
|
||||
### Run the Tests
|
||||
```bash
|
||||
$ python -m pip install tox
|
||||
```
|
||||
|
||||
Tox uses pytest under the hood, hence it supports the same syntax for selecting tests.
|
||||
### Run the template's test suite
|
||||
|
||||
For further information please consult the [pytest usage docs](https://pytest.org/latest/usage.html#specifying-tests-selecting-tests).
|
||||
To run the tests of the template using the current Python version:
|
||||
|
||||
To run all tests using various versions of python in virtualenvs defined in tox.ini, just run tox.:
|
||||
```bash
|
||||
$ tox -e py
|
||||
```
|
||||
|
||||
$ tox
|
||||
This uses `pytest `under the hood, and you can pass options to it after a `--`. So to run a particular test:
|
||||
|
||||
It is possible to test with a specific version of python. To do this, the command
|
||||
is:
|
||||
```bash
|
||||
$ tox -e py -- -k test_default_configuration
|
||||
```
|
||||
|
||||
$ tox -e py310
|
||||
For further information, please consult the [pytest usage docs](https://pytest.org/en/latest/how-to/usage.html#specifying-which-tests-to-run).
|
||||
|
||||
This will run pytest with the python3.10 interpreter, for example.
|
||||
### Run the generated project tests
|
||||
|
||||
To run a particular test with tox for against your current Python version:
|
||||
The template tests are checking that the generated project is fully rendered and that it passes `flake8`. We also have some test scripts which generate a specific project combination, install the dependencies, run the tests of the generated project, install FE dependencies and generate the docs. They will install the template dependencies, so make sure you create and activate a virtual environment first.
|
||||
|
||||
$ tox -e py -- -k test_default_configuration
|
||||
```bash
|
||||
$ python -m venv venv
|
||||
$ source venv/bin/activate
|
||||
```
|
||||
|
||||
These tests are slower and can be run with or without Docker:
|
||||
|
||||
- Without Docker: `scripts/test_bare.sh` (for bare metal)
|
||||
- With Docker: `scripts/test_docker.sh`
|
||||
|
||||
All arguments to these scripts will be passed to the `cookiecutter` CLI, letting you set options, for example:
|
||||
|
||||
```bash
|
||||
$ scripts/test_bare.sh use_celery=y
|
||||
```
|
||||
|
||||
## Submitting a pull request
|
||||
|
||||
Once you're happy with your changes and they look ok locally, push and send send [a pull request][submit-a-pr] to the main repo, which will trigger the tests on GitHub actions. If they fail, try to fix them. A maintainer should take a look at your change and give you feedback or merge it.
|
||||
|
||||
[submit-a-pr]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request
|
||||
|
|
|
@ -76,8 +76,8 @@ accept and merge pull requests.
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
*Audrey is also the creator of Cookiecutter. Audrey and Daniel are on
|
||||
the Cookiecutter core team.*
|
||||
_Audrey is also the creator of Cookiecutter. Audrey and Daniel are on
|
||||
the Cookiecutter core team._
|
||||
|
||||
## Other Contributors
|
||||
|
||||
|
@ -1426,6 +1426,13 @@ Listed in alphabetical order.
|
|||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Morten Kaae</td>
|
||||
<td>
|
||||
<a href="https://github.com/MortenKaae">MortenKaae</a>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>mozillazg</td>
|
||||
<td>
|
||||
|
@ -1440,6 +1447,13 @@ Listed in alphabetical order.
|
|||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>mpsantos</td>
|
||||
<td>
|
||||
<a href="https://github.com/mpsantos">mpsantos</a>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Naveen</td>
|
||||
<td>
|
||||
|
@ -1951,6 +1965,6 @@ Listed in alphabetical order.
|
|||
The following haven't provided code directly, but have provided
|
||||
guidance and advice.
|
||||
|
||||
- Jannis Leidel
|
||||
- Nate Aune
|
||||
- Barry Morrison
|
||||
- Jannis Leidel
|
||||
- Nate Aune
|
||||
- Barry Morrison
|
117
README.md
117
README.md
|
@ -12,58 +12,58 @@
|
|||
Powered by [Cookiecutter](https://github.com/cookiecutter/cookiecutter), Cookiecutter Django is a framework for jumpstarting
|
||||
production-ready Django projects quickly.
|
||||
|
||||
- Documentation: <https://cookiecutter-django.readthedocs.io/en/latest/>
|
||||
- See [Troubleshooting](https://cookiecutter-django.readthedocs.io/en/latest/troubleshooting.html) for common errors and obstacles
|
||||
- If you have problems with Cookiecutter Django, please open [issues](https://github.com/cookiecutter/cookiecutter-django/issues/new) don't send
|
||||
emails to the maintainers.
|
||||
- Documentation: <https://cookiecutter-django.readthedocs.io/en/latest/>
|
||||
- See [Troubleshooting](https://cookiecutter-django.readthedocs.io/en/latest/troubleshooting.html) for common errors and obstacles
|
||||
- If you have problems with Cookiecutter Django, please open [issues](https://github.com/cookiecutter/cookiecutter-django/issues/new) don't send
|
||||
emails to the maintainers.
|
||||
|
||||
## Features
|
||||
|
||||
- For Django 4.0
|
||||
- Works with Python 3.10
|
||||
- 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)
|
||||
- Secure by default. We believe in SSL.
|
||||
- Optimized development and production settings
|
||||
- Registration via [django-allauth](https://github.com/pennersr/django-allauth)
|
||||
- Comes with custom user model ready to go
|
||||
- Optional basic ASGI setup for Websockets
|
||||
- Optional custom static build using Gulp or Webpack
|
||||
- Send emails via [Anymail](https://github.com/anymail/django-anymail) (using [Mailgun](http://www.mailgun.com/) by default or Amazon SES if AWS is selected cloud provider, but switchable)
|
||||
- Media storage using Amazon S3, Google Cloud Storage, Azure Storage or nginx
|
||||
- Docker support using [docker-compose](https://github.com/docker/compose) for development and production (using [Traefik](https://traefik.io/) with [LetsEncrypt](https://letsencrypt.org/) support)
|
||||
- [Procfile](https://devcenter.heroku.com/articles/procfile) for deploying to Heroku
|
||||
- Instructions for deploying to [PythonAnywhere](https://www.pythonanywhere.com/)
|
||||
- Run tests with unittest or pytest
|
||||
- Customizable PostgreSQL version
|
||||
- Default integration with [pre-commit](https://github.com/pre-commit/pre-commit) for identifying simple issues before submission to code review
|
||||
- For Django 4.1
|
||||
- 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)
|
||||
- Secure by default. We believe in SSL.
|
||||
- Optimized development and production settings
|
||||
- Registration via [django-allauth](https://github.com/pennersr/django-allauth)
|
||||
- Comes with custom user model ready to go
|
||||
- Optional basic ASGI setup for Websockets
|
||||
- Optional custom static build using Gulp or Webpack
|
||||
- Send emails via [Anymail](https://github.com/anymail/django-anymail) (using [Mailgun](http://www.mailgun.com/) by default or Amazon SES if AWS is selected cloud provider, but switchable)
|
||||
- Media storage using Amazon S3, Google Cloud Storage, Azure Storage or nginx
|
||||
- Docker support using [docker-compose](https://github.com/docker/compose) for development and production (using [Traefik](https://traefik.io/) with [LetsEncrypt](https://letsencrypt.org/) support)
|
||||
- [Procfile](https://devcenter.heroku.com/articles/procfile) for deploying to Heroku
|
||||
- Instructions for deploying to [PythonAnywhere](https://www.pythonanywhere.com/)
|
||||
- Run tests with unittest or pytest
|
||||
- Customizable PostgreSQL version
|
||||
- Default integration with [pre-commit](https://github.com/pre-commit/pre-commit) for identifying simple issues before submission to code review
|
||||
|
||||
## Optional Integrations
|
||||
|
||||
*These features can be enabled during initial project setup.*
|
||||
_These features can be enabled during initial project setup._
|
||||
|
||||
- Serve static files from Amazon S3, Google Cloud Storage, Azure Storage or [Whitenoise](https://whitenoise.readthedocs.io/)
|
||||
- Configuration for [Celery](https://docs.celeryq.dev) and [Flower](https://github.com/mher/flower) (the latter in Docker setup only)
|
||||
- Integration with [MailHog](https://github.com/mailhog/MailHog) for local email testing
|
||||
- Integration with [Sentry](https://sentry.io/welcome/) for error logging
|
||||
- Serve static files from Amazon S3, Google Cloud Storage, Azure Storage or [Whitenoise](https://whitenoise.readthedocs.io/)
|
||||
- Configuration for [Celery](https://docs.celeryq.dev) and [Flower](https://github.com/mher/flower) (the latter in Docker setup only)
|
||||
- Integration with [MailHog](https://github.com/mailhog/MailHog) for local email testing
|
||||
- Integration with [Sentry](https://sentry.io/welcome/) for error logging
|
||||
|
||||
## 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).
|
||||
- Environment variables for configuration (This won't work with Apache/mod_wsgi).
|
||||
- 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).
|
||||
- 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:
|
||||
|
||||
- Daniel Roy Greenfeld, Project Lead ([GitHub](https://github.com/pydanny), [Patreon](https://www.patreon.com/danielroygreenfeld)): expertise in Django and AWS ELB.
|
||||
- Nikita Shupeyko, Core Developer ([GitHub](https://github.com/webyneter)): expertise in Python/Django, hands-on DevOps and frontend experience.
|
||||
- Daniel Roy Greenfeld, Project Lead ([GitHub](https://github.com/pydanny), [Patreon](https://www.patreon.com/danielroygreenfeld)): expertise in Django and AWS ELB.
|
||||
- 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:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.feldroy.com/products//two-scoops-of-django-3-x"><img src="https://cdn.shopify.com/s/files/1/0304/6901/products/Two-Scoops-of-Django-3-Alpha-Cover_540x_26507b15-e489-470b-8a97-02773dd498d1_1080x.jpg"></a>
|
||||
|
@ -118,6 +118,10 @@ Answer the prompts with your own desired [options](http://cookiecutter-django.re
|
|||
4 - Apache Software License 2.0
|
||||
5 - Not open source
|
||||
Choose from 1, 2, 3, 4, 5 [1]: 1
|
||||
Select username_type:
|
||||
1 - username
|
||||
2 - email
|
||||
Choose from 1, 2 [1]: 1
|
||||
timezone [UTC]: America/Los_Angeles
|
||||
windows [n]: n
|
||||
Select an editor to use. The choices are:
|
||||
|
@ -188,14 +192,14 @@ Now take a look at your repo. Don't forget to carefully look at the generated RE
|
|||
|
||||
For local development, see the following:
|
||||
|
||||
- [Developing locally](http://cookiecutter-django.readthedocs.io/en/latest/developing-locally.html)
|
||||
- [Developing locally using docker](http://cookiecutter-django.readthedocs.io/en/latest/developing-locally-docker.html)
|
||||
- [Developing locally](http://cookiecutter-django.readthedocs.io/en/latest/developing-locally.html)
|
||||
- [Developing locally using docker](http://cookiecutter-django.readthedocs.io/en/latest/developing-locally-docker.html)
|
||||
|
||||
## Community
|
||||
|
||||
- Have questions? **Before you ask questions anywhere else**, please post your question on [Stack Overflow](http://stackoverflow.com/questions/tagged/cookiecutter-django) under the *cookiecutter-django* tag. We check there periodically for questions.
|
||||
- If you think you found a bug or want to request a feature, please open an [issue](https://github.com/cookiecutter/cookiecutter-django/issues).
|
||||
- For anything else, you can chat with us on [Discord](https://discord.gg/uFXweDQc5a).
|
||||
- Have questions? **Before you ask questions anywhere else**, please post your question on [Stack Overflow](http://stackoverflow.com/questions/tagged/cookiecutter-django) under the _cookiecutter-django_ tag. We check there periodically for questions.
|
||||
- If you think you found a bug or want to request a feature, please open an [issue](https://github.com/cookiecutter/cookiecutter-django/issues).
|
||||
- For anything else, you can chat with us on [Discord](https://discord.gg/uFXweDQc5a).
|
||||
|
||||
## For Readers of Two Scoops of Django
|
||||
|
||||
|
@ -203,13 +207,14 @@ You may notice that some elements of this project do not exactly match what we d
|
|||
|
||||
## For PyUp Users
|
||||
|
||||
If you are using [PyUp](https://pyup.io) to keep your dependencies updated and secure, use the code *cookiecutter* during checkout to get 15% off every month.
|
||||
If you are using [PyUp](https://pyup.io) to keep your dependencies updated and secure, use the code _cookiecutter_ during checkout to get 15% off every month.
|
||||
|
||||
## "Your Stuff"
|
||||
|
||||
Scattered throughout the Python and HTML of this project are places marked with "your stuff". This is where third-party libraries are to be integrated with your project.
|
||||
|
||||
## For MySQL users
|
||||
|
||||
To get full MySQL support in addition to the default Postgresql, you can use this fork of the cookiecutter-django:
|
||||
https://github.com/mabdullahadeel/cookiecutter-django-mysql
|
||||
|
||||
|
@ -219,18 +224,18 @@ Need a stable release? You can find them at <https://github.com/cookiecutter/coo
|
|||
|
||||
## Not Exactly What You Want?
|
||||
|
||||
This is what I want. *It might not be what you want.* Don't worry, you have options:
|
||||
This is what I want. _It might not be what you want._ Don't worry, you have options:
|
||||
|
||||
### Fork This
|
||||
|
||||
If you have differences in your preferred setup, I encourage you to fork this to create your own version.
|
||||
Once you have your fork working, let me know and I'll add it to a '*Similar Cookiecutter Templates*' list here.
|
||||
Once you have your fork working, let me know and I'll add it to a '_Similar Cookiecutter Templates_' list here.
|
||||
It's up to you whether to rename your fork.
|
||||
|
||||
If you do rename your fork, I encourage you to submit it to the following places:
|
||||
|
||||
- [cookiecutter](https://github.com/cookiecutter/cookiecutter) so it gets listed in the README as a template.
|
||||
- The cookiecutter [grid](https://www.djangopackages.com/grids/g/cookiecutters/) on Django Packages.
|
||||
- [cookiecutter](https://github.com/cookiecutter/cookiecutter) so it gets listed in the README as a template.
|
||||
- The cookiecutter [grid](https://www.djangopackages.com/grids/g/cookiecutters/) on Django Packages.
|
||||
|
||||
### Submit a Pull Request
|
||||
|
||||
|
@ -239,17 +244,17 @@ experience better.
|
|||
|
||||
## Articles
|
||||
|
||||
- [Cookiecutter Django With Amazon RDS](https://haseeburrehman.com/posts/cookiecutter-django-with-amazon-rds/) - Apr, 2, 2021
|
||||
- [Complete Walkthrough: Blue/Green Deployment to AWS ECS using GitHub actions](https://github.com/Andrew-Chen-Wang/cookiecutter-django-ecs-github) - June 10, 2020
|
||||
- [Using cookiecutter-django with Google Cloud Storage](https://ahhda.github.io/cloud/gce/django/2019/03/12/using-django-cookiecutter-cloud-storage.html) - Mar. 12, 2019
|
||||
- [cookiecutter-django with Nginx, Route 53 and ELB](https://msaizar.com/blog/cookiecutter-django-nginx-route-53-and-elb/) - Feb. 12, 2018
|
||||
- [cookiecutter-django and Amazon RDS](https://msaizar.com/blog/cookiecutter-django-and-amazon-rds/) - Feb. 7, 2018
|
||||
- [Using Cookiecutter to Jumpstart a Django Project on Windows with PyCharm](https://joshuahunter.com/posts/using-cookiecutter-to-jumpstart-a-django-project-on-windows-with-pycharm/) - May 19, 2017
|
||||
- [Exploring with Cookiecutter](http://www.snowboardingcoder.com/django/2016/12/03/exploring-with-cookiecutter/) - Dec. 3, 2016
|
||||
- [Introduction to Cookiecutter-Django](http://krzysztofzuraw.com/blog/2016/django-cookiecutter.html) - Feb. 19, 2016
|
||||
- [Django and GitLab - Running Continuous Integration and tests with your FREE account](http://dezoito.github.io/2016/05/11/django-gitlab-continuous-integration-phantomjs.html) - May. 11, 2016
|
||||
- [Development and Deployment of Cookiecutter-Django on Fedora](https://realpython.com/blog/python/development-and-deployment-of-cookiecutter-django-on-fedora/) - Jan. 18, 2016
|
||||
- [Development and Deployment of Cookiecutter-Django via Docker](https://realpython.com/blog/python/development-and-deployment-of-cookiecutter-django-via-docker/) - Dec. 29, 2015
|
||||
- [How to create a Django Application using Cookiecutter and Django 1.8](https://www.swapps.io/blog/how-to-create-a-django-application-using-cookiecutter-and-django-1-8/) - Sept. 12, 2015
|
||||
- [Cookiecutter Django With Amazon RDS](https://haseeburrehman.com/posts/cookiecutter-django-with-amazon-rds/) - Apr, 2, 2021
|
||||
- [Complete Walkthrough: Blue/Green Deployment to AWS ECS using GitHub actions](https://github.com/Andrew-Chen-Wang/cookiecutter-django-ecs-github) - June 10, 2020
|
||||
- [Using cookiecutter-django with Google Cloud Storage](https://ahhda.github.io/cloud/gce/django/2019/03/12/using-django-cookiecutter-cloud-storage.html) - Mar. 12, 2019
|
||||
- [cookiecutter-django with Nginx, Route 53 and ELB](https://msaizar.com/blog/cookiecutter-django-nginx-route-53-and-elb/) - Feb. 12, 2018
|
||||
- [cookiecutter-django and Amazon RDS](https://msaizar.com/blog/cookiecutter-django-and-amazon-rds/) - Feb. 7, 2018
|
||||
- [Using Cookiecutter to Jumpstart a Django Project on Windows with PyCharm](https://joshuahunter.com/posts/using-cookiecutter-to-jumpstart-a-django-project-on-windows-with-pycharm/) - May 19, 2017
|
||||
- [Exploring with Cookiecutter](http://www.snowboardingcoder.com/django/2016/12/03/exploring-with-cookiecutter/) - Dec. 3, 2016
|
||||
- [Introduction to Cookiecutter-Django](http://krzysztofzuraw.com/blog/2016/django-cookiecutter.html) - Feb. 19, 2016
|
||||
- [Django and GitLab - Running Continuous Integration and tests with your FREE account](http://dezoito.github.io/2016/05/11/django-gitlab-continuous-integration-phantomjs.html) - May. 11, 2016
|
||||
- [Development and Deployment of Cookiecutter-Django on Fedora](https://realpython.com/blog/python/development-and-deployment-of-cookiecutter-django-on-fedora/) - Jan. 18, 2016
|
||||
- [Development and Deployment of Cookiecutter-Django via Docker](https://realpython.com/blog/python/development-and-deployment-of-cookiecutter-django-via-docker/) - Dec. 29, 2015
|
||||
- [How to create a Django Application using Cookiecutter and Django 1.8](https://www.swapps.io/blog/how-to-create-a-django-application-using-cookiecutter-and-django-1-8/) - Sept. 12, 2015
|
||||
|
||||
Have a blog or online publication? Write about your cookiecutter-django tips and tricks, then send us a pull request with the link.
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"description": "Behold My Awesome Project!",
|
||||
"author_name": "Daniel Roy Greenfeld",
|
||||
"domain_name": "example.com",
|
||||
"email": "{{ cookiecutter.author_name.lower()|replace(' ', '-') }}@example.com",
|
||||
"email": "{{ cookiecutter.author_name.lower() | trim() |replace(' ', '-') }}@{{ cookiecutter.domain_name.lower() | trim() }}",
|
||||
"version": "0.1.0",
|
||||
"open_source_license": [
|
||||
"MIT",
|
||||
|
@ -13,27 +13,13 @@
|
|||
"Apache Software License 2.0",
|
||||
"Not open source"
|
||||
],
|
||||
"username_type": ["username", "email"],
|
||||
"timezone": "UTC",
|
||||
"windows": "n",
|
||||
"editor": [
|
||||
"None",
|
||||
"PyCharm",
|
||||
"VS Code"
|
||||
],
|
||||
"editor": ["None", "PyCharm", "VS Code"],
|
||||
"use_docker": "n",
|
||||
"postgresql_version": [
|
||||
"14",
|
||||
"13",
|
||||
"12",
|
||||
"11",
|
||||
"10"
|
||||
],
|
||||
"cloud_provider": [
|
||||
"AWS",
|
||||
"GCP",
|
||||
"Azure",
|
||||
"None"
|
||||
],
|
||||
"postgresql_version": ["14", "13", "12", "11", "10"],
|
||||
"cloud_provider": ["AWS", "GCP", "Azure", "None"],
|
||||
"mail_service": [
|
||||
"Mailgun",
|
||||
"Amazon SES",
|
||||
|
@ -47,23 +33,13 @@
|
|||
],
|
||||
"use_async": "n",
|
||||
"use_drf": "n",
|
||||
"frontend_pipeline": [
|
||||
"None",
|
||||
"Django Compressor",
|
||||
"Gulp",
|
||||
"Webpack"
|
||||
],
|
||||
"frontend_pipeline": ["None", "Django Compressor", "Gulp", "Webpack"],
|
||||
"use_celery": "n",
|
||||
"use_mailhog": "n",
|
||||
"use_sentry": "n",
|
||||
"use_whitenoise": "n",
|
||||
"use_heroku": "n",
|
||||
"ci_tool": [
|
||||
"None",
|
||||
"Travis",
|
||||
"Gitlab",
|
||||
"Github"
|
||||
],
|
||||
"ci_tool": ["None", "Travis", "Gitlab", "Github"],
|
||||
"keep_local_envs_in_vcs": "y",
|
||||
"debug": "n"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ Make sure your project is fully committed and pushed up to Bitbucket or Github o
|
|||
mkvirtualenv --python=/usr/bin/python3.10 my-project-name
|
||||
pip install -r requirements/production.txt # may take a few minutes
|
||||
|
||||
.. note:: We're creating the virtualenv using Python 3.10 (``--python=/usr/bin/python3.10```), although Cookiecutter Django generates a project for Python 3.11. This is because, at time of writing, PythonAnywhere only supports Python 3.10. It shouldn't be a problem, but if is, you may try changing the Python version to 3.11 and see if it works. If it does, please let us know, or even better, submit a pull request to update this section.
|
||||
|
||||
Setting environment variables in the console
|
||||
--------------------------------------------
|
||||
|
|
|
@ -9,7 +9,7 @@ Setting Up Development Environment
|
|||
|
||||
Make sure to have the following on your host:
|
||||
|
||||
* Python 3.10
|
||||
* Python 3.11
|
||||
* PostgreSQL_.
|
||||
* Redis_, if using Celery
|
||||
* Cookiecutter_
|
||||
|
@ -18,7 +18,7 @@ First things first.
|
|||
|
||||
#. Create a virtualenv: ::
|
||||
|
||||
$ python3.10 -m venv <virtual env path>
|
||||
$ python3.11 -m venv <virtual env path>
|
||||
|
||||
#. Activate the virtualenv you have just created: ::
|
||||
|
||||
|
|
|
@ -4,4 +4,3 @@ Generate a new cookiecutter-django project: ::
|
|||
|
||||
For more information refer to
|
||||
:ref:`Project Generation Options <template-options>`.
|
||||
|
||||
|
|
|
@ -24,6 +24,13 @@ author_name:
|
|||
email:
|
||||
The email address you want to identify yourself in the project.
|
||||
|
||||
username_type:
|
||||
The type of username you want to use in the project. This can be either
|
||||
``username`` or ``email``. If you choose ``username``, the ``email`` field
|
||||
will be included. If you choose ``email``, the ``username`` field will be
|
||||
excluded. It is best practice to always include an email field, so there is
|
||||
no option for having just the ``username`` field.
|
||||
|
||||
domain_name:
|
||||
The domain name you plan to use for your project once it goes live.
|
||||
Note that it can be safely changed later on whenever you need to.
|
||||
|
|
|
@ -22,7 +22,6 @@ DATABASE_URL DATABASES auto w/ Dock
|
|||
DJANGO_ADMIN_URL n/a 'admin/' raises error
|
||||
DJANGO_DEBUG DEBUG True False
|
||||
DJANGO_SECRET_KEY SECRET_KEY auto-generated raises error
|
||||
DJANGO_SECURE_BROWSER_XSS_FILTER SECURE_BROWSER_XSS_FILTER n/a True
|
||||
DJANGO_SECURE_SSL_REDIRECT SECURE_SSL_REDIRECT n/a True
|
||||
DJANGO_SECURE_CONTENT_TYPE_NOSNIFF SECURE_CONTENT_TYPE_NOSNIFF n/a True
|
||||
DJANGO_SECURE_FRAME_DENY SECURE_FRAME_DENY n/a True
|
||||
|
|
|
@ -45,6 +45,24 @@ def remove_gplv3_files():
|
|||
os.remove(file_name)
|
||||
|
||||
|
||||
def remove_custom_user_manager_files():
|
||||
os.remove(
|
||||
os.path.join(
|
||||
"{{cookiecutter.project_slug}}",
|
||||
"users",
|
||||
"managers.py",
|
||||
)
|
||||
)
|
||||
os.remove(
|
||||
os.path.join(
|
||||
"{{cookiecutter.project_slug}}",
|
||||
"users",
|
||||
"tests",
|
||||
"test_managers.py",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def remove_pycharm_files():
|
||||
idea_dir_path = ".idea"
|
||||
if os.path.exists(idea_dir_path):
|
||||
|
@ -193,7 +211,7 @@ def handle_js_runner(choice, use_docker, use_async):
|
|||
dev_django_cmd = (
|
||||
"uvicorn config.asgi:application --reload"
|
||||
if use_async
|
||||
else "python manage.py runserver_plus"
|
||||
else "python manage.py runserver"
|
||||
)
|
||||
scripts.update(
|
||||
{
|
||||
|
@ -452,6 +470,9 @@ def main():
|
|||
if "{{ cookiecutter.open_source_license}}" != "GPLv3":
|
||||
remove_gplv3_files()
|
||||
|
||||
if "{{ cookiecutter.username_type }}" == "username":
|
||||
remove_custom_user_manager_files()
|
||||
|
||||
if "{{ cookiecutter.editor }}".lower() != "PyCharm":
|
||||
remove_pycharm_files()
|
||||
|
||||
|
@ -461,7 +482,6 @@ def main():
|
|||
else:
|
||||
remove_docker_files()
|
||||
|
||||
|
||||
if (
|
||||
"{{ cookiecutter.use_docker }}".lower() == "y"
|
||||
and "{{ cookiecutter.cloud_provider}}" != "AWS"
|
||||
|
|
|
@ -17,6 +17,14 @@ INFO = "\x1b[1;33m [INFO]: "
|
|||
HINT = "\x1b[3;33m"
|
||||
SUCCESS = "\x1b[1;32m [SUCCESS]: "
|
||||
|
||||
# The content of this string is evaluated by Jinja, and plays an important role.
|
||||
# It updates the cookiecutter context to trim leading and trailing spaces
|
||||
# from domain/email values
|
||||
"""
|
||||
{{ cookiecutter.update({ "domain_name": cookiecutter.domain_name | trim }) }}
|
||||
{{ cookiecutter.update({ "email": cookiecutter.email | trim }) }}
|
||||
"""
|
||||
|
||||
project_slug = "{{ cookiecutter.project_slug }}"
|
||||
if hasattr(project_slug, "isidentifier"):
|
||||
assert (
|
||||
|
@ -36,7 +44,7 @@ if "{{ cookiecutter.use_docker }}".lower() == "n":
|
|||
if python_major_version == 2:
|
||||
print(
|
||||
WARNING + "You're running cookiecutter under Python 2, but the generated "
|
||||
"project requires Python 3.10+. Do you want to proceed (y/n)? " + TERMINATOR
|
||||
"project requires Python 3.11+. Do you want to proceed (y/n)? " + TERMINATOR
|
||||
)
|
||||
yes_options, no_options = frozenset(["y"]), frozenset(["n"])
|
||||
while True:
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
cookiecutter==2.1.1
|
||||
sh==2.0.2; sys_platform != "win32"
|
||||
sh==2.0.3; sys_platform != "win32"
|
||||
binaryornot==0.4.4
|
||||
|
||||
# Code quality
|
||||
# ------------------------------------------------------------------------------
|
||||
black==23.1.0
|
||||
black==23.3.0
|
||||
isort==5.12.0
|
||||
flake8==6.0.0
|
||||
flake8-isort==6.0.0
|
||||
pre-commit==3.1.1
|
||||
pre-commit==3.2.2
|
||||
|
||||
# Testing
|
||||
# ------------------------------------------------------------------------------
|
||||
tox==4.4.6
|
||||
pytest==7.2.2
|
||||
pytest-cookies==0.6.1
|
||||
pytest-instafail==0.4.2
|
||||
tox==4.4.12
|
||||
pytest==7.3.1
|
||||
pytest-cookies==0.7.0
|
||||
pytest-instafail==0.5.0
|
||||
pyyaml==6.0
|
||||
|
||||
# Scripting
|
||||
# ------------------------------------------------------------------------------
|
||||
PyGithub==1.58.0
|
||||
PyGithub==1.58.1
|
||||
gitpython==3.1.31
|
||||
jinja2==3.1.2
|
||||
requests==2.28.2
|
||||
|
|
6
setup.py
6
setup.py
|
@ -5,7 +5,7 @@ except ImportError:
|
|||
from distutils.core import setup
|
||||
|
||||
# We use calendar versioning
|
||||
version = "2023.03.04"
|
||||
version = "2023.04.13"
|
||||
|
||||
with open("README.rst") as readme_file:
|
||||
long_description = readme_file.read()
|
||||
|
@ -27,13 +27,13 @@ setup(
|
|||
classifiers=[
|
||||
"Development Status :: 4 - Beta",
|
||||
"Environment :: Console",
|
||||
"Framework :: Django :: 4.0",
|
||||
"Framework :: Django :: 4.1",
|
||||
"Intended Audience :: Developers",
|
||||
"Natural Language :: English",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Topic :: Software Development",
|
||||
],
|
||||
|
|
|
@ -20,12 +20,6 @@ sudo utility/install_os_dependencies.sh install
|
|||
# Install Python deps
|
||||
pip install -r requirements/local.txt
|
||||
|
||||
# Lint by running pre-commit on all files
|
||||
# Needs a git repo to find the project root
|
||||
git init
|
||||
git add .
|
||||
pre-commit run --show-diff-on-failure -a
|
||||
|
||||
# run the project's tests
|
||||
pytest
|
||||
|
||||
|
|
|
@ -20,6 +20,11 @@ if sys.platform.startswith("win"):
|
|||
elif sys.platform.startswith("darwin") and os.getenv("CI"):
|
||||
pytest.skip("skipping slow macOS tests on CI", allow_module_level=True)
|
||||
|
||||
# Run auto-fixable styles checks - skipped on CI by default. These can be fixed
|
||||
# automatically by running pre-commit after generation however they are tedious
|
||||
# to fix in the template, so we don't insist too much in fixing them.
|
||||
AUTOFIXABLE_STYLES = os.getenv("AUTOFIXABLE_STYLES") == 1
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def context():
|
||||
|
@ -36,6 +41,8 @@ def context():
|
|||
|
||||
|
||||
SUPPORTED_COMBINATIONS = [
|
||||
{"username_type": "username"},
|
||||
{"username_type": "email"},
|
||||
{"open_source_license": "MIT"},
|
||||
{"open_source_license": "BSD"},
|
||||
{"open_source_license": "GPLv3"},
|
||||
|
@ -183,9 +190,10 @@ def test_flake8_passes(cookies, context_override):
|
|||
pytest.fail(e.stdout.decode())
|
||||
|
||||
|
||||
@pytest.mark.skipif(not AUTOFIXABLE_STYLES, reason="Black is auto-fixable")
|
||||
@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id)
|
||||
def test_black_passes(cookies, context_override):
|
||||
"""Generated project should pass black."""
|
||||
"""Check whether generated project passes black style."""
|
||||
result = cookies.bake(extra_context=context_override)
|
||||
|
||||
try:
|
||||
|
@ -321,10 +329,29 @@ def test_error_if_incompatible(cookies, context, invalid_context):
|
|||
],
|
||||
)
|
||||
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:
|
||||
has_pycharm_docs = "pycharm/configuration" in f.read()
|
||||
assert has_pycharm_docs is pycharm_docs_exist
|
||||
|
||||
|
||||
def test_trim_domain_email(cookies, context):
|
||||
"""Check that leading and trailing spaces are trimmed in domain and email."""
|
||||
context.update(
|
||||
{
|
||||
"use_docker": "y",
|
||||
"domain_name": " example.com ",
|
||||
"email": " me@example.com ",
|
||||
}
|
||||
)
|
||||
result = cookies.bake(extra_context=context)
|
||||
|
||||
assert result.exit_code == 0
|
||||
|
||||
prod_django_env = result.project_path / ".envs" / ".production" / ".django"
|
||||
assert "DJANGO_ALLOWED_HOSTS=.example.com" in prod_django_env.read_text()
|
||||
|
||||
base_settings = result.project_path / "config" / "settings" / "base.py"
|
||||
assert '"me@example.com"' in base_settings.read_text()
|
||||
|
|
|
@ -14,13 +14,6 @@ cd .cache/docker
|
|||
cookiecutter ../../ --no-input --overwrite-if-exists use_docker=y "$@"
|
||||
cd my_awesome_project
|
||||
|
||||
# Lint by running pre-commit on all files
|
||||
# Needs a git repo to find the project root
|
||||
# We don't have git inside Docker, so run it outside
|
||||
git init
|
||||
git add .
|
||||
pre-commit run --show-diff-on-failure -a
|
||||
|
||||
# make sure all images build
|
||||
docker-compose -f local.yml build
|
||||
|
||||
|
|
2
tox.ini
2
tox.ini
|
@ -1,6 +1,6 @@
|
|||
[tox]
|
||||
skipsdist = true
|
||||
envlist = py310,black-template
|
||||
envlist = py311,black-template
|
||||
|
||||
[testenv]
|
||||
deps = -rrequirements.txt
|
||||
|
|
|
@ -22,6 +22,6 @@ trim_trailing_whitespace = false
|
|||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
[nginx.conf]
|
||||
[default.conf]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
|
106
{{cookiecutter.project_slug}}/.github/dependabot.yml
vendored
106
{{cookiecutter.project_slug}}/.github/dependabot.yml
vendored
|
@ -4,11 +4,11 @@
|
|||
version: 2
|
||||
updates:
|
||||
# Update GitHub actions in workflows
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
# Check for updates to GitHub Actions every weekday
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: 'daily'
|
||||
|
||||
{%- if cookiecutter.use_docker == 'y' %}
|
||||
|
||||
|
@ -16,80 +16,92 @@ updates:
|
|||
# We need to specify each Dockerfile in a separate entry because Dependabot doesn't
|
||||
# support wildcards or recursively checking subdirectories. Check this issue for updates:
|
||||
# https://github.com/dependabot/dependabot-core/issues/2178
|
||||
- package-ecosystem: "docker"
|
||||
- package-ecosystem: 'docker'
|
||||
# Look for a `Dockerfile` in the `compose/local/django` directory
|
||||
directory: "compose/local/django/"
|
||||
# Check for updates to GitHub Actions every weekday
|
||||
directory: 'compose/local/django/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: 'daily'
|
||||
# Ignore minor version updates (3.10 -> 3.11) but update patch versions
|
||||
ignore:
|
||||
- dependency-name: '*'
|
||||
update-types:
|
||||
- 'version-update:semver-major'
|
||||
- 'version-update:semver-minor'
|
||||
|
||||
# Enable version updates for Docker
|
||||
- package-ecosystem: "docker"
|
||||
- package-ecosystem: 'docker'
|
||||
# Look for a `Dockerfile` in the `compose/local/docs` directory
|
||||
directory: "compose/local/docs/"
|
||||
# Check for updates to GitHub Actions every weekday
|
||||
directory: 'compose/local/docs/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: 'daily'
|
||||
# Ignore minor version updates (3.10 -> 3.11) but update patch versions
|
||||
ignore:
|
||||
- dependency-name: '*'
|
||||
update-types:
|
||||
- 'version-update:semver-major'
|
||||
- 'version-update:semver-minor'
|
||||
|
||||
# Enable version updates for Docker
|
||||
- package-ecosystem: "docker"
|
||||
- package-ecosystem: 'docker'
|
||||
# Look for a `Dockerfile` in the `compose/local/node` directory
|
||||
directory: "compose/local/node/"
|
||||
# Check for updates to GitHub Actions every weekday
|
||||
directory: 'compose/local/node/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: 'daily'
|
||||
|
||||
# Enable version updates for Docker
|
||||
- package-ecosystem: "docker"
|
||||
- package-ecosystem: 'docker'
|
||||
# Look for a `Dockerfile` in the `compose/production/aws` directory
|
||||
directory: "compose/production/aws/"
|
||||
# Check for updates to GitHub Actions every weekday
|
||||
directory: 'compose/production/aws/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: 'daily'
|
||||
|
||||
# Enable version updates for Docker
|
||||
- package-ecosystem: "docker"
|
||||
- package-ecosystem: 'docker'
|
||||
# Look for a `Dockerfile` in the `compose/production/django` directory
|
||||
directory: "compose/production/django/"
|
||||
# Check for updates to GitHub Actions every weekday
|
||||
directory: 'compose/production/django/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: 'daily'
|
||||
# Ignore minor version updates (3.10 -> 3.11) but update patch versions
|
||||
ignore:
|
||||
- dependency-name: '*'
|
||||
update-types:
|
||||
- 'version-update:semver-major'
|
||||
- 'version-update:semver-minor'
|
||||
|
||||
# Enable version updates for Docker
|
||||
- package-ecosystem: "docker"
|
||||
- package-ecosystem: 'docker'
|
||||
# Look for a `Dockerfile` in the `compose/production/postgres` directory
|
||||
directory: "compose/production/postgres/"
|
||||
# Check for updates to GitHub Actions every weekday
|
||||
directory: 'compose/production/postgres/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: 'daily'
|
||||
|
||||
# Enable version updates for Docker
|
||||
- package-ecosystem: "docker"
|
||||
- package-ecosystem: 'docker'
|
||||
# Look for a `Dockerfile` in the `compose/production/traefik` directory
|
||||
directory: "compose/production/traefik/"
|
||||
# Check for updates to GitHub Actions every weekday
|
||||
directory: 'compose/production/traefik/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: 'daily'
|
||||
|
||||
{%- endif %}
|
||||
|
||||
# Enable version updates for Python/Pip - Production
|
||||
- package-ecosystem: "pip"
|
||||
- package-ecosystem: 'pip'
|
||||
# Look for a `requirements.txt` in the `root` directory
|
||||
# also 'setup.cfg', 'runtime.txt' and 'requirements/*.txt'
|
||||
directory: "/"
|
||||
# Check for updates to GitHub Actions every weekday
|
||||
directory: '/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: 'daily'
|
||||
|
||||
{%- if cookiecutter.frontend_pipeline == 'Gulp' %}
|
||||
|
||||
# Enable version updates for javascript/npm
|
||||
- package-ecosystem: "npm"
|
||||
# Look for a `packages.json' in the `root` directory
|
||||
directory: "/"
|
||||
# Check for updates to GitHub Actions every weekday
|
||||
- package-ecosystem: 'npm'
|
||||
# Look for a `packages.json` in the `root` directory
|
||||
directory: '/'
|
||||
# Every weekday
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: 'daily'
|
||||
|
||||
{%- endif %}
|
||||
|
|
|
@ -7,12 +7,12 @@ env:
|
|||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ "master", "main" ]
|
||||
paths-ignore: [ "docs/**" ]
|
||||
branches: ['master', 'main']
|
||||
paths-ignore: ['docs/**']
|
||||
|
||||
push:
|
||||
branches: [ "master", "main" ]
|
||||
paths-ignore: [ "docs/**" ]
|
||||
branches: ['master', 'main']
|
||||
paths-ignore: ['docs/**']
|
||||
|
||||
concurrency:
|
||||
group: {% raw %}${{ github.head_ref || github.run_id }}{% endraw %}
|
||||
|
@ -22,14 +22,13 @@ jobs:
|
|||
linter:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Checkout Code Repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: '3.11'
|
||||
|
||||
{%- if cookiecutter.open_source_license != 'Not open source' %}
|
||||
# Consider using pre-commit.ci for open source project
|
||||
|
@ -58,35 +57,34 @@ jobs:
|
|||
|
||||
env:
|
||||
{%- if cookiecutter.use_celery == 'y' %}
|
||||
CELERY_BROKER_URL: "redis://localhost:6379/0"
|
||||
CELERY_BROKER_URL: 'redis://localhost:6379/0'
|
||||
{%- endif %}
|
||||
# postgres://user:password@host:port/database
|
||||
DATABASE_URL: "postgres://postgres:postgres@localhost:5432/postgres"
|
||||
DATABASE_URL: 'postgres://postgres:postgres@localhost:5432/postgres'
|
||||
{%- endif %}
|
||||
|
||||
steps:
|
||||
|
||||
- name: Checkout Code Repository
|
||||
uses: actions/checkout@v3
|
||||
{%- 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
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: '3.11'
|
||||
cache: pip
|
||||
cache-dependency-path: |
|
||||
requirements/base.txt
|
||||
|
@ -98,5 +96,5 @@ jobs:
|
|||
pip install -r requirements/local.txt
|
||||
|
||||
- name: Test with pytest
|
||||
run: pytest
|
||||
run: pytest
|
||||
{%- endif %}
|
||||
|
|
|
@ -13,7 +13,7 @@ variables:
|
|||
|
||||
precommit:
|
||||
stage: lint
|
||||
image: python:3.10
|
||||
image: python:3.11
|
||||
variables:
|
||||
PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit
|
||||
cache:
|
||||
|
@ -40,7 +40,7 @@ pytest:
|
|||
script:
|
||||
- docker-compose -f local.yml run django pytest
|
||||
{%- else %}
|
||||
image: python:3.10
|
||||
image: python:3.11
|
||||
tags:
|
||||
- python
|
||||
services:
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="runserver_plus" type="Python.DjangoServer" factoryName="Django server" singleton="true">
|
||||
<module name="{{ cookiecutter.project_slug }}" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs>
|
||||
<env name="PYTHONUNBUFFERED" value="1" />
|
||||
<env name="DJANGO_SETTINGS_MODULE" value="config.settings.local" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="IS_MODULE_SDK" value="true" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<PathMappingSettings>
|
||||
<option name="pathMappings">
|
||||
<list>
|
||||
<mapping local-root="$PROJECT_DIR$" remote-root="/app" />
|
||||
</list>
|
||||
</option>
|
||||
</PathMappingSettings>
|
||||
<option name="launchJavascriptDebuger" value="false" />
|
||||
<option name="port" value="8000" />
|
||||
<option name="host" value="0.0.0.0" />
|
||||
<option name="additionalOptions" value="" />
|
||||
<option name="browserUrl" value="" />
|
||||
<option name="runTestServer" value="false" />
|
||||
<option name="runNoReload" value="false" />
|
||||
<option name="useCustomRunCommand" value="true" />
|
||||
<option name="customRunCommand" value="runserver_plus" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
|
@ -1,4 +1,4 @@
|
|||
exclude: "^docs/|/migrations/"
|
||||
exclude: '^docs/|/migrations/'
|
||||
default_stages: [commit]
|
||||
|
||||
repos:
|
||||
|
@ -7,16 +7,31 @@ repos:
|
|||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-json
|
||||
- id: check-toml
|
||||
- id: check-xml
|
||||
- id: check-yaml
|
||||
- id: debug-statements
|
||||
- id: check-builtin-literals
|
||||
- id: check-case-conflict
|
||||
- id: check-docstring-first
|
||||
- id: detect-private-key
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v3.0.0-alpha.6
|
||||
hooks:
|
||||
- id: prettier
|
||||
args: ['--tab-width', '2', '--single-quote']
|
||||
exclude: {{cookiecutter.project_slug}}/templates/
|
||||
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.3.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py310-plus]
|
||||
args: [--py311-plus]
|
||||
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.1.0
|
||||
rev: 23.3.0
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
|
@ -29,7 +44,7 @@ repos:
|
|||
rev: 6.0.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
args: ["--config=setup.cfg"]
|
||||
args: ['--config=setup.cfg']
|
||||
additional_dependencies: [flake8-isort]
|
||||
|
||||
# sets up .pre-commit-ci.yaml to ensure pre-commit dependencies stay up to date
|
||||
|
|
|
@ -8,7 +8,7 @@ version: 2
|
|||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.10"
|
||||
python: '3.11'
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
|
|
|
@ -2,7 +2,7 @@ dist: focal
|
|||
|
||||
language: python
|
||||
python:
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
|
||||
services:
|
||||
- {% if cookiecutter.use_docker == 'y' %}docker{% else %}postgresql{% endif %}
|
||||
|
@ -37,7 +37,7 @@ jobs:
|
|||
- sudo apt-get install -qq libsqlite3-dev libxml2 libxml2-dev libssl-dev libbz2-dev wget curl llvm
|
||||
language: python
|
||||
python:
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
install:
|
||||
- pip install -r requirements/local.txt
|
||||
script:
|
||||
|
|
|
@ -18,11 +18,11 @@ Moved to [settings](http://cookiecutter-django.readthedocs.io/en/latest/settings
|
|||
|
||||
### Setting Up Your Users
|
||||
|
||||
- To create a **normal user account**, just go to Sign Up and fill out the form. Once you submit it, you'll see a "Verify Your E-mail Address" page. Go to your console to see a simulated email verification message. Copy the link into your browser. Now the user's email should be verified and ready to go.
|
||||
- To create a **normal user account**, just go to Sign Up and fill out the form. Once you submit it, you'll see a "Verify Your E-mail Address" page. Go to your console to see a simulated email verification message. Copy the link into your browser. Now the user's email should be verified and ready to go.
|
||||
|
||||
- To create a **superuser account**, use this command:
|
||||
- To create a **superuser account**, use this command:
|
||||
|
||||
$ python manage.py createsuperuser
|
||||
$ python manage.py createsuperuser
|
||||
|
||||
For convenience, you can keep your normal user logged in on Chrome and your superuser logged in on Firefox (or similar), so that you can see how the site behaves for both kinds of users.
|
||||
|
||||
|
@ -56,23 +56,23 @@ This app comes with Celery.
|
|||
|
||||
To run a celery worker:
|
||||
|
||||
``` bash
|
||||
```bash
|
||||
cd {{cookiecutter.project_slug}}
|
||||
celery -A config.celery_app worker -l info
|
||||
```
|
||||
|
||||
Please note: For Celery's import magic to work, it is important *where* the celery commands are run. If you are in the same folder with *manage.py*, you should be right.
|
||||
Please note: For Celery's import magic to work, it is important _where_ the celery commands are run. If you are in the same folder with _manage.py_, you should be right.
|
||||
|
||||
To run [periodic tasks](https://docs.celeryq.dev/en/stable/userguide/periodic-tasks.html), you'll need to start the celery beat scheduler service. You can start it as a standalone process:
|
||||
|
||||
``` bash
|
||||
```bash
|
||||
cd {{cookiecutter.project_slug}}
|
||||
celery -A config.celery_app beat
|
||||
```
|
||||
|
||||
or you can embed the beat service inside a worker with the `-B` option (not recommended for production use):
|
||||
|
||||
``` bash
|
||||
```bash
|
||||
cd {{cookiecutter.project_slug}}
|
||||
celery -A config.celery_app worker -B -l info
|
||||
```
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
ARG PYTHON_VERSION=3.10-slim-bullseye
|
||||
|
||||
# define an alias for the specfic python version used in this file.
|
||||
FROM python:${PYTHON_VERSION} as python
|
||||
FROM python:3.11.3-slim-bullseye as python
|
||||
|
||||
# Python build stage
|
||||
FROM python as python-build-stage
|
||||
|
|
|
@ -9,5 +9,5 @@ python manage.py migrate
|
|||
{%- if cookiecutter.use_async == 'y' %}
|
||||
exec uvicorn config.asgi:application --host 0.0.0.0 --reload --reload-include '*.html'
|
||||
{%- else %}
|
||||
exec python manage.py runserver_plus 0.0.0.0:8000
|
||||
exec python manage.py runserver 0.0.0.0:8000
|
||||
{%- endif %}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
ARG PYTHON_VERSION=3.10-slim-bullseye
|
||||
|
||||
# define an alias for the specfic python version used in this file.
|
||||
FROM python:${PYTHON_VERSION} as python
|
||||
FROM python:3.11.3-slim-bullseye as python
|
||||
|
||||
|
||||
# Python build stage
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
ARG PYTHON_VERSION=3.10-slim-bullseye
|
||||
|
||||
{% if cookiecutter.frontend_pipeline in ['Gulp', 'Webpack'] -%}
|
||||
FROM node:16-bullseye-slim as client-builder
|
||||
|
||||
|
@ -26,9 +24,8 @@ ENV DJANGO_AZURE_ACCOUNT_NAME=${DJANGO_AZURE_ACCOUNT_NAME}
|
|||
RUN npm run build
|
||||
|
||||
{%- endif %}
|
||||
|
||||
# define an alias for the specfic python version used in this file.
|
||||
FROM python:${PYTHON_VERSION} as python
|
||||
FROM python:3.11.3-slim-bullseye as python
|
||||
|
||||
# Python build stage
|
||||
FROM python as python-build-stage
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
location /media/ {
|
||||
alias /usr/share/nginx/media/;
|
||||
}
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
location /media/ {
|
||||
alias /usr/share/nginx/media/;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM traefik:2.9.8
|
||||
FROM traefik:2.9.10
|
||||
RUN mkdir -p /etc/traefik/acme \
|
||||
&& touch /etc/traefik/acme/acme.json \
|
||||
&& chmod 600 /etc/traefik/acme/acme.json
|
||||
|
|
|
@ -4,7 +4,7 @@ log:
|
|||
entryPoints:
|
||||
web:
|
||||
# http
|
||||
address: ":80"
|
||||
address: ':80'
|
||||
http:
|
||||
# https://docs.traefik.io/routing/entrypoints/#entrypoint
|
||||
redirections:
|
||||
|
@ -13,18 +13,18 @@ entryPoints:
|
|||
|
||||
web-secure:
|
||||
# https
|
||||
address: ":443"
|
||||
address: ':443'
|
||||
{%- if cookiecutter.use_celery == 'y' %}
|
||||
|
||||
flower:
|
||||
address: ":5555"
|
||||
address: ':5555'
|
||||
{%- endif %}
|
||||
|
||||
certificatesResolvers:
|
||||
letsencrypt:
|
||||
# https://docs.traefik.io/master/https/acme/#lets-encrypt
|
||||
acme:
|
||||
email: "{{ cookiecutter.email }}"
|
||||
email: '{{ cookiecutter.email }}'
|
||||
storage: /etc/traefik/acme/acme.json
|
||||
# https://docs.traefik.io/master/https/acme/#httpchallenge
|
||||
httpChallenge:
|
||||
|
@ -34,9 +34,9 @@ http:
|
|||
routers:
|
||||
web-secure-router:
|
||||
{%- if cookiecutter.domain_name.count('.') == 1 %}
|
||||
rule: "Host(`{{ cookiecutter.domain_name }}`) || Host(`www.{{ cookiecutter.domain_name }}`)"
|
||||
rule: 'Host(`{{ cookiecutter.domain_name }}`) || Host(`www.{{ cookiecutter.domain_name }}`)'
|
||||
{%- else %}
|
||||
rule: "Host(`{{ cookiecutter.domain_name }}`)"
|
||||
rule: 'Host(`{{ cookiecutter.domain_name }}`)'
|
||||
{%- endif %}
|
||||
entryPoints:
|
||||
- web-secure
|
||||
|
@ -49,7 +49,7 @@ http:
|
|||
{%- if cookiecutter.use_celery == 'y' %}
|
||||
|
||||
flower-secure-router:
|
||||
rule: "Host(`{{ cookiecutter.domain_name }}`)"
|
||||
rule: 'Host(`{{ cookiecutter.domain_name }}`)'
|
||||
entryPoints:
|
||||
- flower
|
||||
service: flower
|
||||
|
@ -60,7 +60,11 @@ http:
|
|||
{%- if cookiecutter.cloud_provider == 'None' %}
|
||||
|
||||
web-media-router:
|
||||
rule: "Host(`{{ cookiecutter.domain_name }}`) && PathPrefix(`/media/`)"
|
||||
{%- if cookiecutter.domain_name.count('.') == 1 %}
|
||||
rule: '(Host(`{{ cookiecutter.domain_name }}`) || Host(`www.{{ cookiecutter.domain_name }}`)) && PathPrefix(`/media/`)'
|
||||
{%- else %}
|
||||
rule: 'Host(`{{ cookiecutter.domain_name }}`) && PathPrefix(`/media/`)'
|
||||
{%- endif %}
|
||||
entryPoints:
|
||||
- web-secure
|
||||
middlewares:
|
||||
|
@ -75,7 +79,7 @@ http:
|
|||
# https://docs.traefik.io/master/middlewares/headers/#hostsproxyheaders
|
||||
# https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
|
||||
headers:
|
||||
hostsProxyHeaders: ["X-CSRFToken"]
|
||||
hostsProxyHeaders: ['X-CSRFToken']
|
||||
|
||||
services:
|
||||
django:
|
||||
|
|
|
@ -225,8 +225,6 @@ FIXTURE_DIRS = (str(APPS_DIR / "fixtures"),)
|
|||
SESSION_COOKIE_HTTPONLY = True
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-httponly
|
||||
CSRF_COOKIE_HTTPONLY = True
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#secure-browser-xss-filter
|
||||
SECURE_BROWSER_XSS_FILTER = True
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#x-frame-options
|
||||
X_FRAME_OPTIONS = "DENY"
|
||||
|
||||
|
@ -314,9 +312,15 @@ CELERY_TASK_SEND_SENT_EVENT = True
|
|||
# ------------------------------------------------------------------------------
|
||||
ACCOUNT_ALLOW_REGISTRATION = env.bool("DJANGO_ACCOUNT_ALLOW_REGISTRATION", True)
|
||||
# https://django-allauth.readthedocs.io/en/latest/configuration.html
|
||||
ACCOUNT_AUTHENTICATION_METHOD = "username"
|
||||
ACCOUNT_AUTHENTICATION_METHOD = "{{cookiecutter.username_type}}"
|
||||
# https://django-allauth.readthedocs.io/en/latest/configuration.html
|
||||
ACCOUNT_EMAIL_REQUIRED = True
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
# https://django-allauth.readthedocs.io/en/latest/configuration.html
|
||||
ACCOUNT_USERNAME_REQUIRED = False
|
||||
# https://django-allauth.readthedocs.io/en/latest/configuration.html
|
||||
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
|
||||
{%- endif %}
|
||||
# https://django-allauth.readthedocs.io/en/latest/configuration.html
|
||||
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
|
||||
# https://django-allauth.readthedocs.io/en/latest/configuration.html
|
||||
|
|
|
@ -47,15 +47,15 @@ EMAIL_BACKEND = env(
|
|||
# WhiteNoise
|
||||
# ------------------------------------------------------------------------------
|
||||
# http://whitenoise.evans.io/en/latest/django.html#using-whitenoise-in-development
|
||||
INSTALLED_APPS = ["whitenoise.runserver_nostatic"] + INSTALLED_APPS # noqa F405
|
||||
INSTALLED_APPS = ["whitenoise.runserver_nostatic"] + INSTALLED_APPS # noqa: F405
|
||||
{% endif %}
|
||||
|
||||
# django-debug-toolbar
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#prerequisites
|
||||
INSTALLED_APPS += ["debug_toolbar"] # noqa F405
|
||||
INSTALLED_APPS += ["debug_toolbar"] # noqa: F405
|
||||
# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#middleware
|
||||
MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"] # noqa F405
|
||||
MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"] # noqa: F405
|
||||
# https://django-debug-toolbar.readthedocs.io/en/latest/configuration.html#debug-toolbar-config
|
||||
DEBUG_TOOLBAR_CONFIG = {
|
||||
"DISABLE_PANELS": ["debug_toolbar.panels.redirects.RedirectsPanel"],
|
||||
|
@ -82,7 +82,7 @@ if env("USE_DOCKER") == "yes":
|
|||
# django-extensions
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://django-extensions.readthedocs.io/en/latest/installation_instructions.html#configuration
|
||||
INSTALLED_APPS += ["django_extensions"] # noqa F405
|
||||
INSTALLED_APPS += ["django_extensions"] # noqa: F405
|
||||
{% if cookiecutter.use_celery == 'y' -%}
|
||||
|
||||
# Celery
|
||||
|
@ -98,7 +98,7 @@ CELERY_TASK_EAGER_PROPAGATES = True
|
|||
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
|
||||
# django-webpack-loader
|
||||
# ------------------------------------------------------------------------------
|
||||
WEBPACK_LOADER["DEFAULT"]["CACHE"] = not DEBUG # noqa F405
|
||||
WEBPACK_LOADER["DEFAULT"]["CACHE"] = not DEBUG # noqa: F405
|
||||
|
||||
{%- endif %}
|
||||
# Your stuff...
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
import logging
|
||||
|
||||
import sentry_sdk
|
||||
|
||||
{%- if cookiecutter.use_celery == 'y' %}
|
||||
from sentry_sdk.integrations.celery import CeleryIntegration
|
||||
|
||||
{%- endif %}
|
||||
from sentry_sdk.integrations.django import DjangoIntegration
|
||||
from sentry_sdk.integrations.logging import LoggingIntegration
|
||||
|
@ -22,7 +24,7 @@ ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=["{{ cookiecutter.domai
|
|||
|
||||
# DATABASES
|
||||
# ------------------------------------------------------------------------------
|
||||
DATABASES["default"]["CONN_MAX_AGE"] = env.int("CONN_MAX_AGE", default=60) # noqa F405
|
||||
DATABASES["default"]["CONN_MAX_AGE"] = env.int("CONN_MAX_AGE", default=60) # noqa: F405
|
||||
|
||||
# CACHES
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -68,7 +70,7 @@ SECURE_CONTENT_TYPE_NOSNIFF = env.bool(
|
|||
# STORAGES
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://django-storages.readthedocs.io/en/latest/#installation
|
||||
INSTALLED_APPS += ["storages"] # noqa F405
|
||||
INSTALLED_APPS += ["storages"] # noqa: F405
|
||||
{%- endif -%}
|
||||
{% if cookiecutter.cloud_provider == 'AWS' %}
|
||||
# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings
|
||||
|
@ -159,7 +161,7 @@ ADMIN_URL = env("DJANGO_ADMIN_URL")
|
|||
# Anymail
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://anymail.readthedocs.io/en/stable/installation/#installing-anymail
|
||||
INSTALLED_APPS += ["anymail"] # noqa F405
|
||||
INSTALLED_APPS += ["anymail"] # noqa: F405
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
|
||||
# https://anymail.readthedocs.io/en/stable/installation/#anymail-settings-reference
|
||||
{%- if cookiecutter.mail_service == 'Mailgun' %}
|
||||
|
@ -241,7 +243,7 @@ COMPRESS_STORAGE = "compressor.storage.GzipCompressorFileStorage"
|
|||
COMPRESS_STORAGE = STATICFILES_STORAGE
|
||||
{%- endif %}
|
||||
# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_URL
|
||||
COMPRESS_URL = STATIC_URL{% if cookiecutter.use_whitenoise == 'y' or cookiecutter.cloud_provider == 'None' %} # noqa F405{% endif %}
|
||||
COMPRESS_URL = STATIC_URL{% if cookiecutter.use_whitenoise == 'y' or cookiecutter.cloud_provider == 'None' %} # noqa: F405{% endif %}
|
||||
{%- if cookiecutter.use_whitenoise == 'y' %}
|
||||
# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_OFFLINE
|
||||
COMPRESS_OFFLINE = True # Offline compression is required when using Whitenoise
|
||||
|
@ -259,7 +261,7 @@ COMPRESS_FILTERS = {
|
|||
# Collectfast
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://github.com/antonagestam/collectfast#installation
|
||||
INSTALLED_APPS = ["collectfast"] + INSTALLED_APPS # noqa F405
|
||||
INSTALLED_APPS = ["collectfast"] + INSTALLED_APPS # noqa: F405
|
||||
{% endif %}
|
||||
# LOGGING
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -373,7 +375,7 @@ sentry_sdk.init(
|
|||
# django-rest-framework
|
||||
# -------------------------------------------------------------------------------
|
||||
# Tools that generate code samples can use SERVERS to point to the correct domain
|
||||
SPECTACULAR_SETTINGS["SERVERS"] = [ # noqa F405
|
||||
SPECTACULAR_SETTINGS["SERVERS"] = [ # noqa: F405
|
||||
{"url": "https://{{ cookiecutter.domain_name }}", "description": "Production server"}
|
||||
]
|
||||
|
||||
|
|
|
@ -27,12 +27,12 @@ EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
|||
|
||||
# DEBUGGING FOR TEMPLATES
|
||||
# ------------------------------------------------------------------------------
|
||||
TEMPLATES[0]["OPTIONS"]["debug"] = True # type: ignore # noqa F405
|
||||
TEMPLATES[0]["OPTIONS"]["debug"] = True # type: ignore # noqa: F405
|
||||
|
||||
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
|
||||
# django-webpack-loader
|
||||
# ------------------------------------------------------------------------------
|
||||
WEBPACK_LOADER["DEFAULT"][ # noqa F405
|
||||
WEBPACK_LOADER["DEFAULT"][ # noqa: F405
|
||||
"LOADER_CLASS"
|
||||
] = "webpack_loader.loader.FakeWebpackLoader"
|
||||
|
||||
|
|
|
@ -3,29 +3,29 @@
|
|||
////////////////////////////////
|
||||
|
||||
// Gulp and package
|
||||
const { src, dest, parallel, series, watch } = require('gulp')
|
||||
const pjson = require('./package.json')
|
||||
const { src, dest, parallel, series, watch } = require('gulp');
|
||||
const pjson = require('./package.json');
|
||||
|
||||
// Plugins
|
||||
const autoprefixer = require('autoprefixer')
|
||||
const browserSync = require('browser-sync').create()
|
||||
const concat = require('gulp-concat')
|
||||
const autoprefixer = require('autoprefixer');
|
||||
const browserSync = require('browser-sync').create();
|
||||
const concat = require('gulp-concat');
|
||||
const tildeImporter = require('node-sass-tilde-importer');
|
||||
const cssnano = require ('cssnano')
|
||||
const imagemin = require('gulp-imagemin')
|
||||
const pixrem = require('pixrem')
|
||||
const plumber = require('gulp-plumber')
|
||||
const postcss = require('gulp-postcss')
|
||||
const reload = browserSync.reload
|
||||
const rename = require('gulp-rename')
|
||||
const sass = require('gulp-sass')(require('sass'))
|
||||
const spawn = require('child_process').spawn
|
||||
const uglify = require('gulp-uglify-es').default
|
||||
const cssnano = require('cssnano');
|
||||
const imagemin = require('gulp-imagemin');
|
||||
const pixrem = require('pixrem');
|
||||
const plumber = require('gulp-plumber');
|
||||
const postcss = require('gulp-postcss');
|
||||
const reload = browserSync.reload;
|
||||
const rename = require('gulp-rename');
|
||||
const sass = require('gulp-sass')(require('sass'));
|
||||
const spawn = require('child_process').spawn;
|
||||
const uglify = require('gulp-uglify-es').default;
|
||||
|
||||
// Relative paths function
|
||||
function pathsConfig(appName) {
|
||||
this.app = `./${pjson.name}`
|
||||
const vendorsRoot = 'node_modules'
|
||||
this.app = `./${pjson.name}`;
|
||||
const vendorsRoot = 'node_modules';
|
||||
|
||||
return {
|
||||
vendorsJs: [
|
||||
|
@ -39,10 +39,10 @@ function pathsConfig(appName) {
|
|||
fonts: `${this.app}/static/fonts`,
|
||||
images: `${this.app}/static/images`,
|
||||
js: `${this.app}/static/js`,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const paths = pathsConfig()
|
||||
const paths = pathsConfig();
|
||||
|
||||
////////////////////////////////
|
||||
// Tasks
|
||||
|
@ -51,27 +51,27 @@ const paths = pathsConfig()
|
|||
// Styles autoprefixing and minification
|
||||
function styles() {
|
||||
const processCss = [
|
||||
autoprefixer(), // adds vendor prefixes
|
||||
pixrem(), // add fallbacks for rem units
|
||||
]
|
||||
autoprefixer(), // adds vendor prefixes
|
||||
pixrem(), // add fallbacks for rem units
|
||||
];
|
||||
|
||||
const minifyCss = [
|
||||
cssnano({ preset: 'default' }) // minify result
|
||||
]
|
||||
cssnano({ preset: 'default' }), // minify result
|
||||
];
|
||||
|
||||
return src(`${paths.sass}/project.scss`)
|
||||
.pipe(sass({
|
||||
importer: tildeImporter,
|
||||
includePaths: [
|
||||
paths.sass
|
||||
]
|
||||
}).on('error', sass.logError))
|
||||
.pipe(
|
||||
sass({
|
||||
importer: tildeImporter,
|
||||
includePaths: [paths.sass],
|
||||
}).on('error', sass.logError),
|
||||
)
|
||||
.pipe(plumber()) // Checks for errors
|
||||
.pipe(postcss(processCss))
|
||||
.pipe(dest(paths.css))
|
||||
.pipe(rename({ suffix: '.min' }))
|
||||
.pipe(postcss(minifyCss)) // Minifies the result
|
||||
.pipe(dest(paths.css))
|
||||
.pipe(dest(paths.css));
|
||||
}
|
||||
|
||||
// Javascript minification
|
||||
|
@ -80,7 +80,7 @@ function scripts() {
|
|||
.pipe(plumber()) // Checks for errors
|
||||
.pipe(uglify()) // Minifies the js
|
||||
.pipe(rename({ suffix: '.min' }))
|
||||
.pipe(dest(paths.js))
|
||||
.pipe(dest(paths.js));
|
||||
}
|
||||
|
||||
// Vendor Javascript minification
|
||||
|
@ -91,97 +91,91 @@ function vendorScripts() {
|
|||
.pipe(plumber()) // Checks for errors
|
||||
.pipe(uglify()) // Minifies the js
|
||||
.pipe(rename({ suffix: '.min' }))
|
||||
.pipe(dest(paths.js, { sourcemaps: '.' }))
|
||||
.pipe(dest(paths.js, { sourcemaps: '.' }));
|
||||
}
|
||||
|
||||
// Image compression
|
||||
function imgCompression() {
|
||||
return src(`${paths.images}/*`)
|
||||
.pipe(imagemin()) // Compresses PNG, JPEG, GIF and SVG images
|
||||
.pipe(dest(paths.images))
|
||||
.pipe(dest(paths.images));
|
||||
}
|
||||
|
||||
{%- if cookiecutter.use_async == 'y' -%}
|
||||
// Run django server
|
||||
function asyncRunServer() {
|
||||
const cmd = spawn('gunicorn', [
|
||||
'config.asgi', '-k', 'uvicorn.workers.UvicornWorker', '--reload'
|
||||
], {stdio: 'inherit'}
|
||||
)
|
||||
cmd.on('close', function(code) {
|
||||
console.log('gunicorn exited with code ' + code)
|
||||
const cmd = spawn(
|
||||
'gunicorn',
|
||||
['config.asgi', '-k', 'uvicorn.workers.UvicornWorker', '--reload'],
|
||||
{stdio: 'inherit'},
|
||||
);
|
||||
cmd.on('close', function (code) {
|
||||
console.log('gunicorn exited with code ' + code);
|
||||
})
|
||||
}
|
||||
{%- else %}
|
||||
// Run django server
|
||||
function runServer(cb) {
|
||||
const cmd = spawn('python', ['manage.py', 'runserver'], {stdio: 'inherit'})
|
||||
cmd.on('close', function(code) {
|
||||
console.log('runServer exited with code ' + code)
|
||||
cb(code)
|
||||
})
|
||||
const cmd = spawn('python', ['manage.py', 'runserver'], { stdio: 'inherit' });
|
||||
cmd.on('close', function (code) {
|
||||
console.log('runServer exited with code ' + code);
|
||||
cb(code);
|
||||
});
|
||||
}
|
||||
{%- endif %}
|
||||
|
||||
// Browser sync server for live reload
|
||||
function initBrowserSync() {
|
||||
browserSync.init(
|
||||
[
|
||||
`${paths.css}/*.css`,
|
||||
`${paths.js}/*.js`,
|
||||
`${paths.templates}/*.html`
|
||||
], {
|
||||
[`${paths.css}/*.css`, `${paths.js}/*.js`, `${paths.templates}/*.html`],
|
||||
{
|
||||
{%- if cookiecutter.use_docker == 'y' %}
|
||||
// https://www.browsersync.io/docs/options/#option-open
|
||||
// Disable as it doesn't work from inside a container
|
||||
open: false,
|
||||
{%- endif %}
|
||||
// https://www.browsersync.io/docs/options/#option-proxy
|
||||
proxy: {
|
||||
proxy: {
|
||||
{%- if cookiecutter.use_docker == 'n' %}
|
||||
target: '127.0.0.1:8000',
|
||||
{%- else %}
|
||||
target: 'django:8000',
|
||||
{%- endif %}
|
||||
proxyReq: [
|
||||
function(proxyReq, req) {
|
||||
// Assign proxy "host" header same as current request at Browsersync server
|
||||
proxyReq.setHeader('Host', req.headers.host)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
)
|
||||
function (proxyReq, req) {
|
||||
// Assign proxy 'host' header same as current request at Browsersync server
|
||||
proxyReq.setHeader('Host', req.headers.host);
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Watch
|
||||
function watchPaths() {
|
||||
watch(`${paths.sass}/*.scss`{% if cookiecutter.windows == 'y' %}, { usePolling: true }{% endif %}, styles)
|
||||
watch(`${paths.templates}/**/*.html`{% if cookiecutter.windows == 'y' %}, { usePolling: true }{% endif %}).on("change", reload)
|
||||
watch([`${paths.js}/*.js`, `!${paths.js}/*.min.js`]{% if cookiecutter.windows == 'y' %}, { usePolling: true }{% endif %}, scripts).on("change", reload)
|
||||
watch(`${paths.sass}/*.scss`{% if cookiecutter.windows == 'y' %}, { usePolling: true }{% endif %}, styles);
|
||||
watch(`${paths.templates}/**/*.html`{% if cookiecutter.windows == 'y' %}, { usePolling: true }{% endif %}).on('change', reload);
|
||||
watch([`${paths.js}/*.js`, `!${paths.js}/*.min.js`]{% if cookiecutter.windows == 'y' %}, { usePolling: true }{% endif %}, scripts).on(
|
||||
'change',
|
||||
reload,
|
||||
);
|
||||
}
|
||||
|
||||
// Generate all assets
|
||||
const generateAssets = parallel(
|
||||
styles,
|
||||
scripts,
|
||||
vendorScripts,
|
||||
imgCompression
|
||||
)
|
||||
const generateAssets = parallel(styles, scripts, vendorScripts, imgCompression);
|
||||
|
||||
// Set up dev environment
|
||||
const dev = parallel(
|
||||
{%- if cookiecutter.use_docker == 'n' %}
|
||||
{%- if cookiecutter.use_async == 'y' %}
|
||||
asyncRunServer,
|
||||
{%- else %}
|
||||
runServer,
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
initBrowserSync,
|
||||
watchPaths
|
||||
)
|
||||
{%- if cookiecutter.use_docker == 'n' %}
|
||||
{%- if cookiecutter.use_async == 'y' %}
|
||||
const dev = parallel(asyncRunServer, initBrowserSync, watchPaths);
|
||||
{%- else %}
|
||||
const dev = parallel(runServer, initBrowserSync, watchPaths);
|
||||
{%- endif %}
|
||||
{%- else %}
|
||||
const dev = parallel(initBrowserSync, watchPaths);
|
||||
{%- endif %}
|
||||
|
||||
exports.default = series(generateAssets, dev)
|
||||
exports["generate-assets"] = generateAssets
|
||||
exports["dev"] = dev
|
||||
exports.default = series(generateAssets, dev);
|
||||
exports['generate-assets'] = generateAssets;
|
||||
exports['dev'] = dev;
|
||||
|
|
|
@ -25,7 +25,7 @@ services:
|
|||
- ./.envs/.local/.django
|
||||
- ./.envs/.local/.postgres
|
||||
ports:
|
||||
- "8000:8000"
|
||||
- '8000:8000'
|
||||
command: /start
|
||||
|
||||
postgres:
|
||||
|
@ -53,7 +53,7 @@ services:
|
|||
- ./config:/app/config:z
|
||||
- ./{{ cookiecutter.project_slug }}:/app/{{ cookiecutter.project_slug }}:z
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- '9000:9000'
|
||||
command: /start-docs
|
||||
{%- if cookiecutter.use_mailhog == 'y' %}
|
||||
|
||||
|
@ -101,7 +101,7 @@ services:
|
|||
image: {{ cookiecutter.project_slug }}_local_flower
|
||||
container_name: {{ cookiecutter.project_slug }}_local_flower
|
||||
ports:
|
||||
- "5555:5555"
|
||||
- '5555:5555'
|
||||
command: /start-flower
|
||||
|
||||
{%- endif %}
|
||||
|
@ -121,10 +121,10 @@ services:
|
|||
- /app/node_modules
|
||||
command: npm run dev
|
||||
ports:
|
||||
- "3000:3000"
|
||||
- '3000:3000'
|
||||
{%- if cookiecutter.frontend_pipeline == 'Gulp' %}
|
||||
# Expose browsersync UI: https://www.browsersync.io/docs/options/#option-ui
|
||||
- "3001:3001"
|
||||
- '3001:3001'
|
||||
{%- endif %}
|
||||
|
||||
{%- endif %}
|
||||
|
|
|
@ -60,10 +60,10 @@ services:
|
|||
volumes:
|
||||
- production_traefik:/etc/traefik/acme
|
||||
ports:
|
||||
- "0.0.0.0:80:80"
|
||||
- "0.0.0.0:443:443"
|
||||
- '0.0.0.0:80:80'
|
||||
- '0.0.0.0:443:443'
|
||||
{%- if cookiecutter.use_celery == 'y' %}
|
||||
- "0.0.0.0:5555:5555"
|
||||
- '0.0.0.0:5555:5555'
|
||||
{%- endif %}
|
||||
|
||||
redis:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
pytz==2022.7.1 # https://github.com/stub42/pytz
|
||||
pytz==2023.3 # https://github.com/stub42/pytz
|
||||
python-slugify==8.0.1 # https://github.com/un33k/python-slugify
|
||||
Pillow==9.4.0 # https://github.com/python-pillow/Pillow
|
||||
Pillow==9.5.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
|
||||
|
@ -12,27 +12,27 @@ 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
|
||||
{%- endif %}
|
||||
redis==4.5.1 # https://github.com/redis/redis-py
|
||||
redis==4.5.4 # https://github.com/redis/redis-py
|
||||
{%- if cookiecutter.use_docker == "y" or cookiecutter.windows == "n" %}
|
||||
hiredis==2.2.2 # https://github.com/redis/hiredis-py
|
||||
{%- endif %}
|
||||
{%- if cookiecutter.use_celery == "y" %}
|
||||
celery==5.2.7 # pyup: < 6.0 # https://github.com/celery/celery
|
||||
django-celery-beat==2.4.0 # https://github.com/celery/django-celery-beat
|
||||
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
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- if cookiecutter.use_async == 'y' %}
|
||||
uvicorn[standard]==0.20.0 # https://github.com/encode/uvicorn
|
||||
uvicorn[standard]==0.21.1 # https://github.com/encode/uvicorn
|
||||
{%- endif %}
|
||||
|
||||
# Django
|
||||
# ------------------------------------------------------------------------------
|
||||
django==4.0.10 # pyup: < 4.1 # https://www.djangoproject.com/
|
||||
django==4.1.8 # pyup: < 4.2 # https://www.djangoproject.com/
|
||||
django-environ==0.10.0 # https://github.com/joke2k/django-environ
|
||||
django-model-utils==4.3.1 # https://github.com/jazzband/django-model-utils
|
||||
django-allauth==0.52.0 # https://github.com/pennersr/django-allauth
|
||||
django-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' %}
|
||||
|
@ -44,7 +44,7 @@ django-redis==5.2.0 # https://github.com/jazzband/django-redis
|
|||
djangorestframework==3.14.0 # https://github.com/encode/django-rest-framework
|
||||
django-cors-headers==3.14.0 # https://github.com/adamchainz/django-cors-headers
|
||||
# DRF-spectacular for api documentation
|
||||
drf-spectacular==0.26.0 # https://github.com/tfranzel/drf-spectacular
|
||||
drf-spectacular==0.26.1 # https://github.com/tfranzel/drf-spectacular
|
||||
{%- endif %}
|
||||
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
|
||||
django-webpack-loader==1.8.1 # https://github.com/django-webpack/django-webpack-loader
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
-r base.txt
|
||||
|
||||
Werkzeug[watchdog]==2.2.3 # https://github.com/pallets/werkzeug
|
||||
ipdb==0.13.11 # https://github.com/gotcha/ipdb
|
||||
ipdb==0.13.13 # https://github.com/gotcha/ipdb
|
||||
{%- if cookiecutter.use_docker == 'y' %}
|
||||
psycopg2==2.9.5 # https://github.com/psycopg/psycopg2
|
||||
psycopg2==2.9.6 # https://github.com/psycopg/psycopg2
|
||||
{%- else %}
|
||||
psycopg2-binary==2.9.5 # https://github.com/psycopg/psycopg2
|
||||
psycopg2-binary==2.9.6 # https://github.com/psycopg/psycopg2
|
||||
{%- endif %}
|
||||
{%- if cookiecutter.use_async == 'y' or cookiecutter.use_celery == 'y' %}
|
||||
watchfiles==0.18.1 # https://github.com/samuelcolvin/watchfiles
|
||||
watchfiles==0.19.0 # https://github.com/samuelcolvin/watchfiles
|
||||
{%- endif %}
|
||||
|
||||
# Testing
|
||||
# ------------------------------------------------------------------------------
|
||||
mypy==1.1.1 # https://github.com/python/mypy
|
||||
django-stubs==1.15.0 # https://github.com/typeddjango/django-stubs
|
||||
pytest==7.2.2 # https://github.com/pytest-dev/pytest
|
||||
pytest-sugar==0.9.6 # https://github.com/Frozenball/pytest-sugar
|
||||
django-stubs==1.16.0 # https://github.com/typeddjango/django-stubs
|
||||
pytest==7.3.1 # https://github.com/pytest-dev/pytest
|
||||
pytest-sugar==0.9.7 # https://github.com/Frozenball/pytest-sugar
|
||||
{%- if cookiecutter.use_drf == "y" %}
|
||||
djangorestframework-stubs==1.9.1 # https://github.com/typeddjango/djangorestframework-stubs
|
||||
djangorestframework-stubs==1.10.0 # https://github.com/typeddjango/djangorestframework-stubs
|
||||
{%- endif %}
|
||||
|
||||
# Documentation
|
||||
|
@ -30,19 +30,19 @@ sphinx-autobuild==2021.3.14 # https://github.com/GaretJax/sphinx-autobuild
|
|||
# ------------------------------------------------------------------------------
|
||||
flake8==6.0.0 # https://github.com/PyCQA/flake8
|
||||
flake8-isort==6.0.0 # https://github.com/gforcada/flake8-isort
|
||||
coverage==7.2.1 # https://github.com/nedbat/coveragepy
|
||||
black==23.1.0 # https://github.com/psf/black
|
||||
coverage==7.2.2 # https://github.com/nedbat/coveragepy
|
||||
black==23.3.0 # https://github.com/psf/black
|
||||
pylint-django==2.5.3 # https://github.com/PyCQA/pylint-django
|
||||
{%- if cookiecutter.use_celery == 'y' %}
|
||||
pylint-celery==0.3 # https://github.com/PyCQA/pylint-celery
|
||||
{%- endif %}
|
||||
pre-commit==3.1.1 # https://github.com/pre-commit/pre-commit
|
||||
pre-commit==3.2.2 # https://github.com/pre-commit/pre-commit
|
||||
|
||||
# Django
|
||||
# ------------------------------------------------------------------------------
|
||||
factory-boy==3.2.1 # https://github.com/FactoryBoy/factory_boy
|
||||
|
||||
django-debug-toolbar==3.8.1 # https://github.com/jazzband/django-debug-toolbar
|
||||
django-debug-toolbar==4.0.0 # https://github.com/jazzband/django-debug-toolbar
|
||||
django-extensions==3.2.1 # https://github.com/django-extensions/django-extensions
|
||||
django-coverage-plugin==3.0.0 # https://github.com/nedbat/django_coverage_plugin
|
||||
pytest-django==4.5.2 # https://github.com/pytest-dev/pytest-django
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
-r base.txt
|
||||
|
||||
gunicorn==20.1.0 # https://github.com/benoitc/gunicorn
|
||||
psycopg2==2.9.5 # https://github.com/psycopg/psycopg2
|
||||
psycopg2==2.9.6 # https://github.com/psycopg/psycopg2
|
||||
{%- if cookiecutter.use_whitenoise == 'n' %}
|
||||
Collectfast==2.2.0 # https://github.com/antonagestam/collectfast
|
||||
{%- endif %}
|
||||
{%- if cookiecutter.use_sentry == "y" %}
|
||||
sentry-sdk==1.16.0 # https://github.com/getsentry/sentry-python
|
||||
sentry-sdk==1.19.0 # https://github.com/getsentry/sentry-python
|
||||
{%- endif %}
|
||||
{%- if cookiecutter.use_docker == "n" and cookiecutter.windows == "y" %}
|
||||
hiredis==2.2.2 # 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]==9.0 # https://github.com/anymail/django-anymail
|
||||
django-anymail[mailgun]==9.1 # https://github.com/anymail/django-anymail
|
||||
{%- elif cookiecutter.mail_service == 'Amazon SES' %}
|
||||
django-anymail[amazon_ses]==9.0 # https://github.com/anymail/django-anymail
|
||||
django-anymail[amazon_ses]==9.1 # https://github.com/anymail/django-anymail
|
||||
{%- elif cookiecutter.mail_service == 'Mailjet' %}
|
||||
django-anymail[mailjet]==9.0 # https://github.com/anymail/django-anymail
|
||||
django-anymail[mailjet]==9.1 # https://github.com/anymail/django-anymail
|
||||
{%- elif cookiecutter.mail_service == 'Mandrill' %}
|
||||
django-anymail[mandrill]==9.0 # https://github.com/anymail/django-anymail
|
||||
django-anymail[mandrill]==9.1 # https://github.com/anymail/django-anymail
|
||||
{%- elif cookiecutter.mail_service == 'Postmark' %}
|
||||
django-anymail[postmark]==9.0 # https://github.com/anymail/django-anymail
|
||||
django-anymail[postmark]==9.1 # https://github.com/anymail/django-anymail
|
||||
{%- elif cookiecutter.mail_service == 'Sendgrid' %}
|
||||
django-anymail[sendgrid]==9.0 # https://github.com/anymail/django-anymail
|
||||
django-anymail[sendgrid]==9.1 # https://github.com/anymail/django-anymail
|
||||
{%- elif cookiecutter.mail_service == 'SendinBlue' %}
|
||||
django-anymail[sendinblue]==9.0 # https://github.com/anymail/django-anymail
|
||||
django-anymail[sendinblue]==9.1 # https://github.com/anymail/django-anymail
|
||||
{%- elif cookiecutter.mail_service == 'SparkPost' %}
|
||||
django-anymail[sparkpost]==9.0 # https://github.com/anymail/django-anymail
|
||||
django-anymail[sparkpost]==9.1 # https://github.com/anymail/django-anymail
|
||||
{%- elif cookiecutter.mail_service == 'Other SMTP' %}
|
||||
django-anymail==9.0 # https://github.com/anymail/django-anymail
|
||||
django-anymail==9.1 # https://github.com/anymail/django-anymail
|
||||
{%- endif %}
|
||||
|
|
|
@ -1 +1 @@
|
|||
python-3.10.8
|
||||
python-3.11.3
|
||||
|
|
|
@ -18,7 +18,7 @@ force_grid_wrap = 0
|
|||
use_parentheses = true
|
||||
|
||||
[mypy]
|
||||
python_version = 3.10
|
||||
python_version = 3.11
|
||||
check_untyped_defs = True
|
||||
ignore_missing_imports = True
|
||||
warn_unused_ignores = True
|
||||
|
@ -35,6 +35,6 @@ ignore_errors = True
|
|||
|
||||
[coverage:run]
|
||||
include = {{cookiecutter.project_slug}}/**
|
||||
omit = *migrations*, *tests*
|
||||
omit = */migrations/*, */tests/*
|
||||
plugins =
|
||||
django_coverage_plugin
|
||||
|
|
|
@ -3,20 +3,25 @@ const BundleTracker = require('webpack-bundle-tracker');
|
|||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
|
||||
module.exports = {
|
||||
target: "web",
|
||||
target: 'web',
|
||||
context: path.join(__dirname, '../'),
|
||||
entry: {
|
||||
'project': path.resolve(__dirname, '../{{cookiecutter.project_slug}}/static/js/project'),
|
||||
'vendors': path.resolve(__dirname, '../{{cookiecutter.project_slug}}/static/js/vendors'),
|
||||
project: path.resolve(__dirname, '../{{cookiecutter.project_slug}}/static/js/project'),
|
||||
vendors: path.resolve(__dirname, '../{{cookiecutter.project_slug}}/static/js/vendors'),
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, '../{{cookiecutter.project_slug}}/static/webpack_bundles/'),
|
||||
path: path.resolve(
|
||||
__dirname,
|
||||
'../{{cookiecutter.project_slug}}/static/webpack_bundles/',
|
||||
),
|
||||
publicPath: '/static/webpack_bundles/',
|
||||
filename: 'js/[name]-[fullhash].js',
|
||||
chunkFilename: 'js/[name]-[hash].js',
|
||||
},
|
||||
plugins: [
|
||||
new BundleTracker({filename: path.resolve(__dirname, '../webpack-stats.json')}),
|
||||
new BundleTracker({
|
||||
filename: path.resolve(__dirname, '../webpack-stats.json'),
|
||||
}),
|
||||
new MiniCssExtractPlugin({ filename: 'css/[name].[contenthash].css' }),
|
||||
],
|
||||
module: {
|
||||
|
@ -35,11 +40,7 @@ module.exports = {
|
|||
loader: 'postcss-loader',
|
||||
options: {
|
||||
postcssOptions: {
|
||||
plugins: [
|
||||
'postcss-preset-env',
|
||||
'autoprefixer',
|
||||
'pixrem',
|
||||
],
|
||||
plugins: ['postcss-preset-env', 'autoprefixer', 'pixrem'],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -5,9 +5,9 @@ const commonConfig = require('./common.config');
|
|||
{%- if cookiecutter.use_whitenoise == 'n' %}
|
||||
{%- if cookiecutter.cloud_provider == 'AWS' %}
|
||||
const s3BucketName = process.env.DJANGO_AWS_STORAGE_BUCKET_NAME;
|
||||
const awsS3Domain = process.env.DJANGO_AWS_S3_CUSTOM_DOMAIN ?
|
||||
process.env.DJANGO_AWS_S3_CUSTOM_DOMAIN
|
||||
: `${s3BucketName}.s3.amazonaws.com`;
|
||||
const awsS3Domain = process.env.DJANGO_AWS_S3_CUSTOM_DOMAIN
|
||||
? process.env.DJANGO_AWS_S3_CUSTOM_DOMAIN
|
||||
: `${s3BucketName}.s3.amazonaws.com`;
|
||||
const staticUrl = `https://${awsS3Domain}/static/`;
|
||||
{%- elif cookiecutter.cloud_provider == 'GCP' %}
|
||||
const staticUrl = `https://storage.googleapis.com/${process.env.DJANGO_GCP_STORAGE_BUCKET_NAME}/static/`;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{%- if cookiecutter.frontend_pipeline == 'Webpack' %}
|
||||
{%- if cookiecutter.frontend_pipeline == 'Webpack' -%}
|
||||
import '../sass/project.scss';
|
||||
{%- endif %}
|
||||
|
||||
{% endif -%}
|
||||
/* Project specific Javascript goes here. */
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
@import "custom_bootstrap_vars";
|
||||
@import "~bootstrap/scss/bootstrap";
|
||||
|
||||
@import 'custom_bootstrap_vars';
|
||||
@import '~bootstrap/scss/bootstrap';
|
||||
|
||||
// project specific CSS goes here
|
||||
|
||||
////////////////////////////////
|
||||
//Variables//
|
||||
////////////////////////////////
|
||||
///////////////
|
||||
// Variables //
|
||||
///////////////
|
||||
|
||||
// Alert colors
|
||||
|
||||
|
@ -17,9 +16,9 @@ $pink: #f2dede;
|
|||
$dark-pink: #eed3d7;
|
||||
$red: #b94a48;
|
||||
|
||||
////////////////////////////////
|
||||
//Alerts//
|
||||
////////////////////////////////
|
||||
////////////
|
||||
// Alerts //
|
||||
////////////
|
||||
|
||||
// bootstrap alert CSS, translated to the django-standard levels of
|
||||
// debug, info, success, warning, error
|
||||
|
|
|
@ -92,8 +92,7 @@
|
|||
</li>
|
||||
{% if request.user.is_authenticated %}
|
||||
<li class="nav-item">
|
||||
{# URL provided by django-allauth/account/urls.py #}
|
||||
<a class="nav-link" href="{% url 'users:detail' request.user.username %}">{% translate "My Profile" %}</a>
|
||||
<a class="nav-link" href="{% endraw %}{% if cookiecutter.username_type == "email" %}{% raw %}{% url 'users:detail' request.user.pk %}{% endraw %}{% else %}{% raw %}{% url 'users:detail' request.user.username %}{% endraw %}{% endif %}{% raw %}">{% translate "My Profile" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
{# URL provided by django-allauth/account/urls.py #}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% raw %}{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}User: {{ object.username }}{% endblock %}
|
||||
{% block title %}User: {% endraw %}{% if cookiecutter.username_type == "email" %}{% raw %}{{ object.name }}{% endraw %}{% else %}{% raw %}{{ object.username }}{% endraw %}{% endif %}{% raw %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
|
@ -9,7 +9,7 @@
|
|||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
|
||||
<h2>{{ object.username }}</h2>
|
||||
<h2>{% endraw %}{% if cookiecutter.username_type == "email" %}{% raw %}{{ object.name }}{% endraw %}{% else %}{% raw %}{{ object.username }}{% endraw %}{% endif %}{% raw %}</h2>
|
||||
{% if object.name %}
|
||||
<p>{{ object.name }}</p>
|
||||
{% endif %}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
{% raw %}{% extends "base.html" %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}{{ user.username }}{% endblock %}
|
||||
{% block title %}{% endraw %}{% if cookiecutter.username_type == "email" %}{% raw %}{{ user.name }}{% endraw %}{% else %}{% raw %}{{ user.username }}{% endraw %}{% endif %}{% raw %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ user.username }}</h1>
|
||||
<h1>{% endraw %}{% if cookiecutter.username_type == "email" %}{% raw %}{{ user.name }}{% endraw %}{% else %}{% raw %}{{ user.username }}{% endraw %}{% endif %}{% raw %}</h1>
|
||||
<form class="form-horizontal" method="post" action="{% url 'users:update' %}">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
|
|
|
@ -13,8 +13,13 @@ class UserAdmin(auth_admin.UserAdmin):
|
|||
form = UserAdminChangeForm
|
||||
add_form = UserAdminCreationForm
|
||||
fieldsets = (
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
(None, {"fields": ("email", "password")}),
|
||||
(_("Personal info"), {"fields": ("name",)}),
|
||||
{%- else %}
|
||||
(None, {"fields": ("username", "password")}),
|
||||
(_("Personal info"), {"fields": ("name", "email")}),
|
||||
{%- endif %}
|
||||
(
|
||||
_("Permissions"),
|
||||
{
|
||||
|
@ -29,5 +34,17 @@ class UserAdmin(auth_admin.UserAdmin):
|
|||
),
|
||||
(_("Important dates"), {"fields": ("last_login", "date_joined")}),
|
||||
)
|
||||
list_display = ["username", "name", "is_superuser"]
|
||||
list_display = ["{{cookiecutter.username_type}}", "name", "is_superuser"]
|
||||
search_fields = ["name"]
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
ordering = ["id"]
|
||||
add_fieldsets = (
|
||||
(
|
||||
None,
|
||||
{
|
||||
"classes": ("wide",),
|
||||
"fields": ("email", "password1", "password2"),
|
||||
},
|
||||
),
|
||||
)
|
||||
{%- endif %}
|
||||
|
|
|
@ -7,8 +7,16 @@ User = get_user_model()
|
|||
class UserSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = User
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
fields = ["name", "url"]
|
||||
|
||||
extra_kwargs = {
|
||||
"url": {"view_name": "api:user-detail", "lookup_field": "pk"}
|
||||
}
|
||||
{%- else %}
|
||||
fields = ["username", "name", "url"]
|
||||
|
||||
extra_kwargs = {
|
||||
"url": {"view_name": "api:user-detail", "lookup_field": "username"}
|
||||
}
|
||||
{%- endif %}
|
||||
|
|
|
@ -13,7 +13,11 @@ User = get_user_model()
|
|||
class UserViewSet(RetrieveModelMixin, ListModelMixin, UpdateModelMixin, GenericViewSet):
|
||||
serializer_class = UserSerializer
|
||||
queryset = User.objects.all()
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
lookup_field = "pk"
|
||||
{%- else %}
|
||||
lookup_field = "username"
|
||||
{%- endif %}
|
||||
|
||||
def get_queryset(self, *args, **kwargs):
|
||||
assert isinstance(self.request.user.id, int)
|
||||
|
|
|
@ -8,6 +8,6 @@ class UsersConfig(AppConfig):
|
|||
|
||||
def ready(self):
|
||||
try:
|
||||
import {{ cookiecutter.project_slug }}.users.signals # noqa F401
|
||||
import {{ cookiecutter.project_slug }}.users.signals # noqa: F401
|
||||
except ImportError:
|
||||
pass
|
||||
|
|
|
@ -2,6 +2,9 @@ from allauth.account.forms import SignupForm
|
|||
from allauth.socialaccount.forms import SignupForm as SocialSignupForm
|
||||
from django.contrib.auth import forms as admin_forms
|
||||
from django.contrib.auth import get_user_model
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
from django.forms import EmailField
|
||||
{%- endif %}
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
User = get_user_model()
|
||||
|
@ -10,6 +13,9 @@ User = get_user_model()
|
|||
class UserAdminChangeForm(admin_forms.UserChangeForm):
|
||||
class Meta(admin_forms.UserChangeForm.Meta):
|
||||
model = User
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
field_classes = {"email": EmailField}
|
||||
{%- endif %}
|
||||
|
||||
|
||||
class UserAdminCreationForm(admin_forms.UserCreationForm):
|
||||
|
@ -20,10 +26,17 @@ class UserAdminCreationForm(admin_forms.UserCreationForm):
|
|||
|
||||
class Meta(admin_forms.UserCreationForm.Meta):
|
||||
model = User
|
||||
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
fields = ("email",)
|
||||
field_classes = {"email": EmailField}
|
||||
error_messages = {
|
||||
"username": {"unique": _("This username has already been taken.")}
|
||||
"email": {"unique": _("This email has already been taken.")},
|
||||
}
|
||||
{%- else %}
|
||||
error_messages = {
|
||||
"username": {"unique": _("This username has already been taken.")},
|
||||
}
|
||||
{%- endif %}
|
||||
|
||||
|
||||
class UserSignupForm(SignupForm):
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
from django.contrib.auth.hashers import make_password
|
||||
from django.contrib.auth.models import UserManager as DjangoUserManager
|
||||
|
||||
|
||||
class UserManager(DjangoUserManager):
|
||||
"""Custom manager for the User model."""
|
||||
|
||||
def _create_user(self, email: str, password: str | None, **extra_fields):
|
||||
"""
|
||||
Create and save a user with the given email and password.
|
||||
"""
|
||||
if not email:
|
||||
raise ValueError("The given email must be set")
|
||||
email = self.normalize_email(email)
|
||||
user = self.model(email=email, **extra_fields)
|
||||
user.password = make_password(password)
|
||||
user.save(using=self._db)
|
||||
return user
|
||||
|
||||
def create_user(self, email: str, password: str | None = None, **extra_fields):
|
||||
extra_fields.setdefault("is_staff", False)
|
||||
extra_fields.setdefault("is_superuser", False)
|
||||
return self._create_user(email, password, **extra_fields)
|
||||
|
||||
def create_superuser(self, email: str, password: str | None = None, **extra_fields):
|
||||
extra_fields.setdefault("is_staff", True)
|
||||
extra_fields.setdefault("is_superuser", True)
|
||||
|
||||
if extra_fields.get("is_staff") is not True:
|
||||
raise ValueError("Superuser must have is_staff=True.")
|
||||
if extra_fields.get("is_superuser") is not True:
|
||||
raise ValueError("Superuser must have is_superuser=True.")
|
||||
|
||||
return self._create_user(email, password, **extra_fields)
|
|
@ -3,6 +3,8 @@ import django.contrib.auth.validators
|
|||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
import {{cookiecutter.project_slug}}.users.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
|
@ -40,6 +42,7 @@ class Migration(migrations.Migration):
|
|||
verbose_name="superuser status",
|
||||
),
|
||||
),
|
||||
{%- if cookiecutter.username_type == "username" -%}
|
||||
(
|
||||
"username",
|
||||
models.CharField(
|
||||
|
@ -61,6 +64,14 @@ class Migration(migrations.Migration):
|
|||
blank=True, max_length=254, verbose_name="email address"
|
||||
),
|
||||
),
|
||||
{%- else %}
|
||||
(
|
||||
"email",
|
||||
models.EmailField(
|
||||
unique=True, max_length=254, verbose_name="email address"
|
||||
),
|
||||
),
|
||||
{%- endif %}
|
||||
(
|
||||
"is_staff",
|
||||
models.BooleanField(
|
||||
|
@ -118,7 +129,11 @@ class Migration(migrations.Migration):
|
|||
"abstract": False,
|
||||
},
|
||||
managers=[
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
("objects", {{cookiecutter.project_slug}}.users.models.UserManager()),
|
||||
{%- else %}
|
||||
("objects", django.contrib.auth.models.UserManager()),
|
||||
{%- endif %}
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
from django.contrib.auth.models import AbstractUser
|
||||
from django.db.models import CharField
|
||||
from django.db.models import CharField{% if cookiecutter.username_type == "email" %}, EmailField{% endif %}
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
|
||||
from {{ cookiecutter.project_slug }}.users.managers import UserManager
|
||||
{%- endif %}
|
||||
|
||||
|
||||
class User(AbstractUser):
|
||||
|
@ -11,16 +15,29 @@ class User(AbstractUser):
|
|||
check forms.SignupForm and forms.SocialSignupForms accordingly.
|
||||
"""
|
||||
|
||||
#: First and last name do not cover name patterns around the globe
|
||||
# First and last name do not cover name patterns around the globe
|
||||
name = CharField(_("Name of User"), blank=True, max_length=255)
|
||||
first_name = None # type: ignore
|
||||
last_name = None # type: ignore
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
email = EmailField(_("email address"), unique=True)
|
||||
username = None # type: ignore
|
||||
|
||||
def get_absolute_url(self):
|
||||
"""Get url for user's detail view.
|
||||
USERNAME_FIELD = "email"
|
||||
REQUIRED_FIELDS = []
|
||||
|
||||
objects = UserManager()
|
||||
{%- endif %}
|
||||
|
||||
def get_absolute_url(self) -> str:
|
||||
"""Get URL for user's detail view.
|
||||
|
||||
Returns:
|
||||
str: URL for user detail.
|
||||
|
||||
"""
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
return reverse("users:detail", kwargs={"pk": self.id})
|
||||
{%- else %}
|
||||
return reverse("users:detail", kwargs={"username": self.username})
|
||||
{%- endif %}
|
||||
|
|
|
@ -7,7 +7,9 @@ from factory.django import DjangoModelFactory
|
|||
|
||||
|
||||
class UserFactory(DjangoModelFactory):
|
||||
{%- if cookiecutter.username_type == "username" %}
|
||||
username = Faker("user_name")
|
||||
{%- endif %}
|
||||
email = Faker("email")
|
||||
name = Faker("name")
|
||||
|
||||
|
@ -29,4 +31,4 @@ class UserFactory(DjangoModelFactory):
|
|||
|
||||
class Meta:
|
||||
model = get_user_model()
|
||||
django_get_or_create = ["username"]
|
||||
django_get_or_create = ["{{cookiecutter.username_type}}"]
|
||||
|
|
|
@ -22,16 +22,28 @@ class TestUserAdmin:
|
|||
response = admin_client.post(
|
||||
url,
|
||||
data={
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
"email": "new-admin@example.com",
|
||||
{%- else %}
|
||||
"username": "test",
|
||||
{%- endif %}
|
||||
"password1": "My_R@ndom-P@ssw0rd",
|
||||
"password2": "My_R@ndom-P@ssw0rd",
|
||||
},
|
||||
)
|
||||
assert response.status_code == 302
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
assert User.objects.filter(email="new-admin@example.com").exists()
|
||||
{%- else %}
|
||||
assert User.objects.filter(username="test").exists()
|
||||
{%- endif %}
|
||||
|
||||
def test_view_user(self, admin_client):
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
user = User.objects.get(email="admin@example.com")
|
||||
{%- else %}
|
||||
user = User.objects.get(username="admin")
|
||||
{%- endif %}
|
||||
url = reverse("admin:users_user_change", kwargs={"object_id": user.pk})
|
||||
response = admin_client.get(url)
|
||||
assert response.status_code == 200
|
||||
|
|
|
@ -4,11 +4,19 @@ from {{ cookiecutter.project_slug }}.users.models import User
|
|||
|
||||
|
||||
def test_user_detail(user: User):
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
assert (
|
||||
reverse("api:user-detail", kwargs={"pk": user.pk})
|
||||
== f"/api/users/{user.pk}/"
|
||||
)
|
||||
assert resolve(f"/api/users/{user.pk}/").view_name == "api:user-detail"
|
||||
{%- else %}
|
||||
assert (
|
||||
reverse("api:user-detail", kwargs={"username": user.username})
|
||||
== f"/api/users/{user.username}/"
|
||||
)
|
||||
assert resolve(f"/api/users/{user.username}/").view_name == "api:user-detail"
|
||||
{%- endif %}
|
||||
|
||||
|
||||
def test_user_list():
|
||||
|
|
|
@ -29,7 +29,11 @@ class TestUserViewSet:
|
|||
response = view.me(request) # type: ignore
|
||||
|
||||
assert response.data == {
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
"url": f"http://testserver/api/users/{user.pk}/",
|
||||
{%- else %}
|
||||
"username": user.username,
|
||||
"name": user.name,
|
||||
"url": f"http://testserver/api/users/{user.username}/",
|
||||
{%- endif %}
|
||||
"name": user.name,
|
||||
}
|
||||
|
|
|
@ -24,7 +24,11 @@ class TestUserAdminCreationForm:
|
|||
# hence cannot be created.
|
||||
form = UserAdminCreationForm(
|
||||
{
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
"email": user.email,
|
||||
{%- else %}
|
||||
"username": user.username,
|
||||
{%- endif %}
|
||||
"password1": user.password,
|
||||
"password2": user.password,
|
||||
}
|
||||
|
@ -32,5 +36,10 @@ class TestUserAdminCreationForm:
|
|||
|
||||
assert not form.is_valid()
|
||||
assert len(form.errors) == 1
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
assert "email" in form.errors
|
||||
assert form.errors["email"][0] == _("This email has already been taken.")
|
||||
{%- else %}
|
||||
assert "username" in form.errors
|
||||
assert form.errors["username"][0] == _("This username has already been taken.")
|
||||
{%- endif %}
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
from io import StringIO
|
||||
|
||||
import pytest
|
||||
from django.core.management import call_command
|
||||
|
||||
from {{ cookiecutter.project_slug }}.users.models import User
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestUserManager:
|
||||
def test_create_user(self):
|
||||
user = User.objects.create_user(
|
||||
email="john@example.com",
|
||||
password="something-r@nd0m!",
|
||||
)
|
||||
assert user.email == "john@example.com"
|
||||
assert not user.is_staff
|
||||
assert not user.is_superuser
|
||||
assert user.check_password("something-r@nd0m!")
|
||||
assert user.username is None
|
||||
|
||||
def test_create_superuser(self):
|
||||
user = User.objects.create_superuser(
|
||||
email="admin@example.com",
|
||||
password="something-r@nd0m!",
|
||||
)
|
||||
assert user.email == "admin@example.com"
|
||||
assert user.is_staff
|
||||
assert user.is_superuser
|
||||
assert user.username is None
|
||||
|
||||
def test_create_superuser_username_ignored(self):
|
||||
user = User.objects.create_superuser(
|
||||
email="test@example.com",
|
||||
password="something-r@nd0m!",
|
||||
)
|
||||
assert user.username is None
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_createsuperuser_command():
|
||||
"""Ensure createsuperuser command works with our custom manager."""
|
||||
out = StringIO()
|
||||
command_result = call_command(
|
||||
"createsuperuser",
|
||||
"--email",
|
||||
"henry@example.com",
|
||||
interactive=False,
|
||||
stdout=out,
|
||||
)
|
||||
|
||||
assert command_result is None
|
||||
assert out.getvalue() == "Superuser created successfully.\n"
|
||||
user = User.objects.get(email="henry@example.com")
|
||||
assert not user.has_usable_password()
|
|
@ -2,4 +2,8 @@ from {{ cookiecutter.project_slug }}.users.models import User
|
|||
|
||||
|
||||
def test_user_get_absolute_url(user: User):
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
assert user.get_absolute_url() == f"/users/{user.pk}/"
|
||||
{%- else %}
|
||||
assert user.get_absolute_url() == f"/users/{user.username}/"
|
||||
{%- endif %}
|
||||
|
|
|
@ -4,11 +4,16 @@ from {{ cookiecutter.project_slug }}.users.models import User
|
|||
|
||||
|
||||
def test_detail(user: User):
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
assert reverse("users:detail", kwargs={"pk": user.pk}) == f"/users/{user.pk}/"
|
||||
assert resolve(f"/users/{user.pk}/").view_name == "users:detail"
|
||||
{%- else %}
|
||||
assert (
|
||||
reverse("users:detail", kwargs={"username": user.username})
|
||||
== f"/users/{user.username}/"
|
||||
)
|
||||
assert resolve(f"/users/{user.username}/").view_name == "users:detail"
|
||||
{%- endif %}
|
||||
|
||||
|
||||
def test_update():
|
||||
|
|
|
@ -39,7 +39,11 @@ class TestUserUpdateView:
|
|||
|
||||
view.request = request
|
||||
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
assert view.get_success_url() == f"/users/{user.pk}/"
|
||||
{%- else %}
|
||||
assert view.get_success_url() == f"/users/{user.username}/"
|
||||
{%- endif %}
|
||||
|
||||
def test_get_object(self, user: User, rf: RequestFactory):
|
||||
view = UserUpdateView()
|
||||
|
@ -79,7 +83,11 @@ class TestUserRedirectView:
|
|||
|
||||
view.request = request
|
||||
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
assert view.get_redirect_url() == f"/users/{user.pk}/"
|
||||
{%- else %}
|
||||
assert view.get_redirect_url() == f"/users/{user.username}/"
|
||||
{%- endif %}
|
||||
|
||||
|
||||
class TestUserDetailView:
|
||||
|
@ -87,7 +95,11 @@ class TestUserDetailView:
|
|||
request = rf.get("/fake-url/")
|
||||
request.user = UserFactory()
|
||||
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
response = user_detail_view(request, pk=user.pk)
|
||||
{%- else %}
|
||||
response = user_detail_view(request, username=user.username)
|
||||
{%- endif %}
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
|
@ -95,7 +107,11 @@ class TestUserDetailView:
|
|||
request = rf.get("/fake-url/")
|
||||
request.user = AnonymousUser()
|
||||
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
response = user_detail_view(request, pk=user.pk)
|
||||
{%- else %}
|
||||
response = user_detail_view(request, username=user.username)
|
||||
{%- endif %}
|
||||
login_url = reverse(settings.LOGIN_URL)
|
||||
|
||||
assert isinstance(response, HttpResponseRedirect)
|
||||
|
|
|
@ -10,5 +10,9 @@ app_name = "users"
|
|||
urlpatterns = [
|
||||
path("~redirect/", view=user_redirect_view, name="redirect"),
|
||||
path("~update/", view=user_update_view, name="update"),
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
path("<int:pk>/", view=user_detail_view, name="detail"),
|
||||
{%- else %}
|
||||
path("<str:username>/", view=user_detail_view, name="detail"),
|
||||
{%- endif %}
|
||||
]
|
||||
|
|
|
@ -10,8 +10,13 @@ User = get_user_model()
|
|||
|
||||
class UserDetailView(LoginRequiredMixin, DetailView):
|
||||
model = User
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
slug_field = "id"
|
||||
slug_url_kwarg = "id"
|
||||
{%- else %}
|
||||
slug_field = "username"
|
||||
slug_url_kwarg = "username"
|
||||
{%- endif %}
|
||||
|
||||
|
||||
user_detail_view = UserDetailView.as_view()
|
||||
|
@ -39,7 +44,11 @@ class UserRedirectView(LoginRequiredMixin, RedirectView):
|
|||
permanent = False
|
||||
|
||||
def get_redirect_url(self):
|
||||
{%- if cookiecutter.username_type == "email" %}
|
||||
return reverse("users:detail", kwargs={"pk": self.request.user.pk})
|
||||
{%- else %}
|
||||
return reverse("users:detail", kwargs={"username": self.request.user.username})
|
||||
{%- endif %}
|
||||
|
||||
|
||||
user_redirect_view = UserRedirectView.as_view()
|
||||
|
|
Loading…
Reference in New Issue
Block a user