Merge branch 'master' into fix_upgrade_py39

# Conflicts:
#	README.rst
#	{{cookiecutter.project_slug}}/.gitlab-ci.yml
#	{{cookiecutter.project_slug}}/compose/local/django/Dockerfile
#	{{cookiecutter.project_slug}}/compose/production/django/Dockerfile
This commit is contained in:
Bruno Alla 2021-04-08 18:43:54 +01:00
commit 9e7320ea1b
74 changed files with 622 additions and 266 deletions

View File

@ -1067,5 +1067,20 @@
"name": "vascop",
"github_login": "vascop",
"twitter_username": ""
},
{
"name": "PJ Hoberman",
"github_login": "pjhoberman",
"twitter_username": ""
},
{
"name": "lcd1232",
"github_login": "lcd1232",
"twitter_username": ""
},
{
"name": "Tames McTigue",
"github_login": "Tamerz",
"twitter_username": ""
}
]

View File

@ -8,3 +8,5 @@ updates:
directory: "/"
schedule:
interval: "daily"
labels:
- "update"

View File

@ -26,7 +26,7 @@ jobs:
run: pre-commit autoupdate
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3.7.0
uses: peter-evans/create-pull-request@v3.8.2
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: update/pre-commit-autoupdate

View File

@ -28,7 +28,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v4.8.0
uses: stefanzweifel/git-auto-commit-action@v4.9.2
with:
commit_message: Update Changelog
file_pattern: CHANGELOG.md

View File

@ -24,7 +24,7 @@ jobs:
run: python scripts/update_contributors.py
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v4.8.0
uses: stefanzweifel/git-auto-commit-action@v4.9.2
with:
commit_message: Update Contributors
file_pattern: CONTRIBUTORS.md .github/contributors.json

View File

@ -3,6 +3,144 @@ All enhancements and patches to Cookiecutter Django will be documented in this f
<!-- GENERATOR_PLACEHOLDER -->
## [2021-04-07]
### Changed
- Update django to 3.1.8 ([#3117](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3117))
### Fixed
- Fix linting via pre-commit on Github CI ([#3077](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3077))
- Fix gitlab-ci using duplicate key name for image ([#3112](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3112))
### Updated
- Update sentry-sdk to 1.0.0 ([#3080](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3080))
- Update gunicorn to 20.1.0 ([#3108](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3108))
- Update pre-commit to 2.12.0 ([#3118](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3118))
- Update django-extensions to 3.1.2 ([#3116](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3116))
- Update pillow to 8.2.0 ([#3113](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3113))
- Update pytest to 6.2.3 ([#3115](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3115))
## [2021-03-26]
### Updated
- Update djangorestframework to 3.12.4 ([#3107](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3107))
## [2021-03-25]
### Updated
- Update djangorestframework to 3.12.3 ([#3105](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3105))
## [2021-03-22]
### Updated
- Update django-crispy-forms to 1.11.2 ([#3104](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3104))
- Update sphinx to 3.5.3 ([#3103](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3103))
- Update ipdb to 0.13.7 ([#3102](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3102))
- Update sphinx-autobuild to 2021.3.14 ([#3101](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3101))
- Update isort to 5.8.0 ([#3100](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3100))
- Update pre-commit to 2.11.1 ([#3089](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3089))
- Update flake8 to 3.9.0 ([#3096](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3096))
- Update pillow to 8.1.2 ([#3084](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3084))
- Auto-update pre-commit hooks ([#3095](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3095))
## [2021-03-05]
### Changed
- Updated test_urls.py and views.py to re-use User.get_absolute_url() ([#3070](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3070))
### Updated
- Bump stefanzweifel/git-auto-commit-action from v4.9.1 to v4.9.2 ([#3082](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3082))
## [2021-03-03]
### Updated
- Update tox to 3.23.0 ([#3079](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3079))
- Update ipdb to 0.13.5 ([#3078](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3078))
## [2021-03-02]
### Fixed
- Fixes for pytest job in Github CI workflow ([#3076](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3076))
### Updated
- Update pillow to 8.1.1 ([#3075](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3075))
- Update coverage to 5.5 ([#3074](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3074))
## [2021-02-24]
### Updated
- Bump stefanzweifel/git-auto-commit-action from v4.9.0 to v4.9.1 ([#3069](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3069))
## [2021-02-23]
### Changed
- Update to Django 3.1 ([#3043](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3043))
- Lint with pre-commit on CI with Github actions ([#3066](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3066))
- Use exception var in status code pages if available ([#2992](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/2992))
## [2021-02-22]
### Changed
- refactor: remove default cache settings in test.py ([#3064](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3064))
- Update django to 3.0.13 ([#3060](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3060))
### Fixed
- Fix missing Django Debug toolbar with node container ([#2865](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/2865))
- Remove Email from User API ([#3055](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3055))
### Updated
- Bump stefanzweifel/git-auto-commit-action from v4.8.0 to v4.9.0 ([#3065](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3065))
- Update django-crispy-forms to 1.11.1 ([#3063](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3063))
- Update uvicorn to 0.13.4 ([#3062](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3062))
- Update mypy to 0.812 ([#3061](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3061))
- Update sentry-sdk to 0.20.3 ([#3059](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3059))
- Update tox to 3.22.0 ([#3057](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3057))
- Update sphinx to 3.5.1 ([#3056](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3056))
## [2021-02-16]
### Updated
- Update sentry-sdk to 0.20.2 ([#3054](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3054))
- Update sphinx to 3.5.0 ([#3053](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3053))
## [2021-02-13]
### Updated
- Update sentry-sdk to 0.20.1 ([#3052](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3052))
## [2021-02-12]
### Updated
- Update pre-commit to 2.10.1 ([#3045](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3045))
- Update sentry-sdk to 0.20.0 ([#3051](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3051))
## [2021-02-10]
### Updated
- Bump peter-evans/create-pull-request from v3.8.1 to v3.8.2 ([#3049](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3049))
## [2021-02-08]
### Updated
- Update django-extensions to 3.1.1 ([#3047](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3047))
- Bump peter-evans/create-pull-request from v3.8.0 to v3.8.1 ([#3046](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3046))
## [2021-02-06]
### Changed
- Removed Redundant test_case_sensitivity() and made test_not_authenticated() get the LOGIN_URL dynamically. ([#3041](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3041))
- Refactored users.forms to make the code more readeable ([#3029](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3029))
- Update django to 3.0.12 ([#3037](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3037))
### Updated
- Update tox to 3.21.4 ([#3044](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3044))
## [2021-02-01]
### Updated
- Update pytz to 2021.1 ([#3035](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3035))
- Update jinja2 to 2.11.3 ([#3033](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3033))
- Bump peter-evans/create-pull-request from v3.7.0 to v3.8.0 ([#3034](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3034))
## [2021-01-31]
### Changed
- Adding local celery instructions to developing-locally ([#3031](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3031))
### Updated
- Update django-crispy-forms to 1.11.0 ([#3032](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3032))
## [2021-01-28]
### Updated
- Update pre-commit to 2.10.0 ([#3028](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3028))
- Update django-anymail to 8.2 ([#3027](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3027))
- Update tox to 3.21.3 ([#3026](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3026))
## [2021-01-26]
### Changed
- Bump peter-evans/create-pull-request from v3.6.0 to v3.7.0 ([#3022](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3022))
- Using SuccessMessageMixin to send success message to django template ([#3021](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3021))
### Fixed
- Update admin to ignore *_name User attributes ([#3018](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3018))
### Updated
- Update coverage to 5.4 ([#3024](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3024))
- Update pytest to 6.2.2 ([#3020](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3020))
- Update django-cors-headers to 3.7.0 ([#3019](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/3019))
## [2021-01-24]
### Changed
- Use defer for script tags (Fix #2922) ([#2927](https://api.github.com/repos/pydanny/cookiecutter-django/pulls/2927))

View File

@ -978,6 +978,13 @@ Listed in alphabetical order.
</td>
<td></td>
</tr>
<tr>
<td>lcd1232</td>
<td>
<a href="https://github.com/lcd1232">lcd1232</a>
</td>
<td></td>
</tr>
<tr>
<td>Leo won</td>
<td>
@ -1223,6 +1230,13 @@ Listed in alphabetical order.
</td>
<td></td>
</tr>
<tr>
<td>PJ Hoberman</td>
<td>
<a href="https://github.com/pjhoberman">pjhoberman</a>
</td>
<td></td>
</tr>
<tr>
<td>Raony Guimarães Corrêa</td>
<td>
@ -1349,6 +1363,13 @@ Listed in alphabetical order.
</td>
<td></td>
</tr>
<tr>
<td>Tames McTigue</td>
<td>
<a href="https://github.com/Tamerz">Tamerz</a>
</td>
<td></td>
</tr>
<tr>
<td>Tano Abeleyra</td>
<td>

View File

@ -40,7 +40,7 @@ production-ready Django projects quickly.
Features
---------
* For Django 3.0
* For Django 3.1
* Works with Python 3.9
* Renders Django projects with 100% starting test coverage
* Twitter Bootstrap_ v4 (`maintained Foundation fork`_ also available)

View File

@ -124,8 +124,8 @@ To check the logs out, run::
If you want to scale your application, run::
docker-compose -f production.yml scale django=4
docker-compose -f production.yml scale celeryworker=2
docker-compose -f production.yml up --scale django=4
docker-compose -f production.yml up --scale celeryworker=2
.. warning:: don't try to scale ``postgres``, ``celerybeat``, or ``traefik``.

View File

@ -143,6 +143,10 @@ when developing locally. If you have the appropriate setup on your local machine
in ``config/settings/local.py``::
CELERY_TASK_ALWAYS_EAGER = False
To run Celery locally, make sure redis-server is installed (instructions are available at https://redis.io/topics/quickstart), run the server in one terminal with `redis-server`, and then start celery in another terminal with the following command::
celery -A config.celery_app worker --loglevel=info
Sass Compilation & Live Reloading

View File

@ -5,13 +5,14 @@ binaryornot==0.4.4
# Code quality
# ------------------------------------------------------------------------------
black==20.8b1
isort==5.7.0
flake8==3.8.4
isort==5.8.0
flake8==3.9.0
flake8-isort==4.0.0
pre-commit==2.12.0
# Testing
# ------------------------------------------------------------------------------
tox==3.21.2
tox==3.23.0
pytest==5.4.3 # pyup: <6 # https://github.com/hackebrot/pytest-cookies/issues/51
pytest-cookies==0.5.1
pytest-instafail==0.4.2
@ -20,4 +21,4 @@ pyyaml==5.4.1
# Scripting
# ------------------------------------------------------------------------------
PyGithub==1.54.1
jinja2==2.11.2
jinja2==2.11.3

View File

@ -10,7 +10,7 @@ except ImportError:
# Our version ALWAYS matches the version of Django we support
# If Django has a new release, we branch, tag, then update this setting after the tag.
version = "3.0.11"
version = "3.1.8"
if sys.argv[-1] == "tag":
os.system(f'git tag -a {version} -m "version {version}"')

View File

@ -27,5 +27,11 @@ 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

View File

@ -231,10 +231,10 @@ def test_gitlab_invokes_flake8_and_pytest(
["use_docker", "expected_test_script"],
[
("n", "pytest"),
("y", "docker-compose -f local.yml exec -T django pytest"),
("y", "docker-compose -f local.yml run django pytest"),
],
)
def test_github_invokes_flake8_and_pytest(
def test_github_invokes_linter_and_pytest(
cookies, context, use_docker, expected_test_script
):
context.update({"ci_tool": "Github", "use_docker": use_docker})
@ -248,11 +248,11 @@ def test_github_invokes_flake8_and_pytest(
with open(f"{result.project}/.github/workflows/ci.yml", "r") as github_yml:
try:
github_config = yaml.safe_load(github_yml)
flake8_present = False
for action_step in github_config["jobs"]["flake8"]["steps"]:
if action_step.get("run") == "flake8":
flake8_present = True
assert flake8_present
linter_present = False
for action_step in github_config["jobs"]["linter"]["steps"]:
if action_step.get("uses", "NA").startswith("pre-commit"):
linter_present = True
assert linter_present
expected_test_script_present = False
for action_step in github_config["jobs"]["pytest"]["steps"]:

View File

@ -17,12 +17,16 @@ 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
# run the project's type checks
docker-compose -f local.yml run django mypy my_awesome_project
# Run black with --check option
docker-compose -f local.yml run django black --check --diff --exclude 'migrations' ./
# run the project's tests
docker-compose -f local.yml run django pytest
@ -30,4 +34,4 @@ docker-compose -f local.yml run django pytest
docker-compose -f local.yml run django python manage.py makemigrations --dry-run --check || { echo "ERROR: there were changes in the models, but migration listed above have not been created and are not saved in version control"; exit 1; }
# Test support for translations
docker-compose -f local.yml run django python manage.py makemessages
docker-compose -f local.yml run django python manage.py makemessages --all

View File

@ -14,4 +14,4 @@ REDIS_URL=redis://redis:6379/0
# Flower
CELERY_FLOWER_USER=!!!SET CELERY_FLOWER_USER!!!
CELERY_FLOWER_PASSWORD=!!!SET CELERY_FLOWER_PASSWORD!!!
{% endif %}
{%- endif %}

View File

@ -7,16 +7,16 @@ env:
on:
pull_request:
branches: [ "master" ]
branches: [ "master", "main" ]
paths-ignore: [ "docs/**" ]
push:
branches: [ "master" ]
branches: [ "master", "main" ]
paths-ignore: [ "docs/**" ]
jobs:
flake8:
linter:
runs-on: ubuntu-latest
steps:
@ -28,38 +28,56 @@ jobs:
with:
python-version: 3.9
- name: Install flake8
run: |
python -m pip install --upgrade pip
pip install flake8
# Run all pre-commit hooks on all the files.
# Getting only staged files can be tricky in case a new PR is opened
# since the action is run on a branch in detached head state
- name: Install and Run Pre-commit
uses: pre-commit/action@v2.0.0
- name: Lint with flake8
run: flake8
# With no caching at all the entire ci process takes 4m 30s to complete!
# With no caching at all the entire ci process takes 4m 30s to complete!
pytest:
runs-on: ubuntu-latest
{%- if cookiecutter.use_docker == 'n' %}
services:
{%- if cookiecutter.use_celery == 'y' %}
redis:
image: redis:5.0
ports:
- 6379:6379
{%- endif %}
postgres:
image: postgres:12
ports:
- 5432:5432
env:
POSTGRES_PASSWORD: postgres
env:
{%- if cookiecutter.use_celery == 'y' %}
CELERY_BROKER_URL: "redis://localhost:6379/0"
{%- endif %}
# postgres://user:password@host:port/database
DATABASE_URL: "postgres://postgres:postgres@localhost:5432/postgres"
{%- endif %}
steps:
- name: Checkout Code Repository
uses: actions/checkout@v2
{% if cookiecutter.use_docker == 'y' -%}
{%- if cookiecutter.use_docker == 'y' %}
- name: Build the Stack
run: docker-compose -f local.yml build
- name: Make DB Migrations
- name: Run DB Migrations
run: docker-compose -f local.yml run --rm django python manage.py migrate
- name: Run the Stack
run: docker-compose -f local.yml up -d
- name: Run Django Tests
run: docker-compose -f local.yml exec -T django pytest
run: docker-compose -f local.yml run django pytest
- name: Tear down the Stack
run: docker-compose -f local.yml down
{%- else %}
- name: Set up Python 3.9
@ -71,8 +89,8 @@ jobs:
id: pip-cache-location
run: |
echo "::set-output name=dir::$(pip cache dir)"
{%- raw %}
{% raw %}
- name: Cache pip Project Dependencies
uses: actions/cache@v2
with:
@ -82,7 +100,7 @@ jobs:
key: ${{ runner.os }}-pip-${{ hashFiles('**/local.txt') }}
restore-keys: |
${{ runner.os }}-pip-
{% endraw %}
{%- endraw %}
- name: Install Dependencies
run: |
@ -91,5 +109,4 @@ jobs:
- name: Test with pytest
run: pytest
{%- endif %}

View File

@ -21,7 +21,6 @@ flake8:
pytest:
stage: test
image: python:3.9
{% if cookiecutter.use_docker == 'y' -%}
image: docker/compose:latest
tags:
@ -35,7 +34,8 @@ pytest:
- docker-compose -f local.yml up -d
script:
- docker-compose -f local.yml run django pytest
{%- else %}
{%- else -%}
image: python:3.9
tags:
- python
services:

View File

@ -16,14 +16,13 @@ repos:
- id: black
- repo: https://github.com/timothycrosley/isort
rev: 5.7.0
rev: 5.8.0
hooks:
- id: isort
- repo: https://gitlab.com/pycqa/flake8
rev: 3.8.4
rev: 3.9.0
hooks:
- id: flake8
args: ['--config=setup.cfg']
additional_dependencies: [flake8-isort]

View File

@ -1,5 +1,5 @@
[MASTER]
load-plugins=pylint_django{% if cookiecutter.use_celery == "y" %}, pylint_celery {% endif %}
load-plugins=pylint_django{% if cookiecutter.use_celery == "y" %}, pylint_celery{% endif %}
[FORMAT]
max-line-length=120

View File

@ -7,7 +7,7 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
{% elif cookiecutter.open_source_license == 'BSD' %}
{%- elif cookiecutter.open_source_license == 'BSD' %}
Copyright (c) {% now 'utc', '%Y' %}, {{ cookiecutter.author_name }}
All rights reserved.
@ -35,7 +35,7 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
{% elif cookiecutter.open_source_license == 'GPLv3' %}
{%- elif cookiecutter.open_source_license == 'GPLv3' %}
Copyright (c) {% now 'utc', '%Y' %}, {{ cookiecutter.author_name }}
This program is free software: you can redistribute it and/or modify
@ -50,7 +50,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
{% elif cookiecutter.open_source_license == 'Apache Software License 2.0' %}
{%- elif cookiecutter.open_source_license == 'Apache Software License 2.0' %}
Apache License
Version 2.0, January 2004
@ -242,4 +242,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
{% endif %}
{%- endif %}

View File

@ -1,10 +1,10 @@
release: python manage.py migrate
{% if cookiecutter.use_async == "y" -%}
{%- if cookiecutter.use_async == "y" -%}
web: gunicorn config.asgi:application -k uvicorn.workers.UvicornWorker
{%- else %}
web: gunicorn config.wsgi:application
{%- endif %}
{% if cookiecutter.use_celery == "y" -%}
{%- if cookiecutter.use_celery == "y" -%}
worker: celery worker --app=config.celery_app --loglevel=info
beat: celery beat --app=config.celery_app --loglevel=info
{%- endif %}

View File

@ -9,10 +9,10 @@
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/ambv/black
:alt: Black code style
{% if cookiecutter.open_source_license != "Not open source" %}
{%- if cookiecutter.open_source_license != "Not open source" %}
:License: {{cookiecutter.open_source_license}}
{% endif %}
{%- endif %}
Settings
--------
@ -67,7 +67,7 @@ Moved to `Live reloading and SASS compilation`_.
.. _`Live reloading and SASS compilation`: http://cookiecutter-django.readthedocs.io/en/latest/live-reloading-and-sass-compilation.html
{% if cookiecutter.use_celery == "y" %}
{%- if cookiecutter.use_celery == "y" %}
Celery
^^^^^^
@ -83,19 +83,21 @@ To run a celery worker:
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.
{% endif %}
{% if cookiecutter.use_mailhog == "y" %}
{%- endif %}
{%- if cookiecutter.use_mailhog == "y" %}
Email Server
^^^^^^^^^^^^
{% if cookiecutter.use_docker == 'y' %}
{%- if cookiecutter.use_docker == 'y' %}
In development, it is often nice to be able to see emails that are being sent from your application. For that reason local SMTP server `MailHog`_ with a web interface is available as docker container.
Container mailhog will start automatically when you will run all docker containers.
Please check `cookiecutter-django Docker documentation`_ for more details how to start all containers.
With MailHog running, to view messages that are sent by your application, open your browser and go to ``http://127.0.0.1:8025``
{% else %}
{%- else %}
In development, it is often nice to be able to see emails that are being sent from your application. If you choose to use `MailHog`_ when generating the project a local SMTP server with a web interface will be available.
#. `Download the latest MailHog release`_ for your OS.
@ -117,10 +119,10 @@ In development, it is often nice to be able to see emails that are being sent fr
Now you have your own mail server running locally, ready to receive whatever you send it.
.. _`Download the latest MailHog release`: https://github.com/mailhog/MailHog/releases
{% endif %}
{%- endif %}
.. _mailhog: https://github.com/mailhog/MailHog
{% endif %}
{% if cookiecutter.use_sentry == "y" %}
{%- endif %}
{%- if cookiecutter.use_sentry == "y" %}
Sentry
^^^^^^
@ -129,13 +131,13 @@ Sentry is an error logging aggregator service. You can sign up for a free accoun
The system is setup with reasonable defaults, including 404 logging and integration with the WSGI application.
You must set the DSN url in production.
{% endif %}
{%- endif %}
Deployment
----------
The following details how to deploy this application.
{% if cookiecutter.use_heroku.lower() == "y" %}
{%- if cookiecutter.use_heroku.lower() == "y" %}
Heroku
^^^^^^
@ -143,8 +145,8 @@ Heroku
See detailed `cookiecutter-django Heroku documentation`_.
.. _`cookiecutter-django Heroku documentation`: http://cookiecutter-django.readthedocs.io/en/latest/deployment-on-heroku.html
{% endif %}
{% if cookiecutter.use_docker.lower() == "y" %}
{%- endif %}
{%- if cookiecutter.use_docker.lower() == "y" %}
Docker
^^^^^^
@ -152,9 +154,8 @@ Docker
See detailed `cookiecutter-django Docker documentation`_.
.. _`cookiecutter-django Docker documentation`: http://cookiecutter-django.readthedocs.io/en/latest/deployment-with-docker.html
{% endif %}
{% if cookiecutter.custom_bootstrap_compilation == "y" %}
{%- endif %}
{%- if cookiecutter.custom_bootstrap_compilation == "y" %}
Custom Bootstrap Compilation
^^^^^^
@ -163,11 +164,11 @@ Bootstrap v4 is installed using npm and customised by tweaking your variables in
You can find a list of available variables `in the bootstrap source`_, or get explanations on them in the `Bootstrap docs`_.
{% if cookiecutter.js_task_runner == 'Gulp' %}
{%- if cookiecutter.js_task_runner == 'Gulp' %}
Bootstrap's javascript as well as its dependencies is concatenated into a single file: ``static/js/vendors.js``.
{% endif %}
{%- endif %}
.. _in the bootstrap source: https://github.com/twbs/bootstrap/blob/v4-dev/scss/_variables.scss
.. _Bootstrap docs: https://getbootstrap.com/docs/4.1/getting-started/theming/
{% endif %}
{%- endif %}

View File

@ -1,22 +1,57 @@
FROM python:3.9-slim-buster
ARG PYTHON_VERSION=3.9-slim-buster
# define an alias for the specfic python version used in this file.
FROM python:${PYTHON_VERSION} as python
# Python build stage
FROM python as python-build-stage
ARG BUILD_ENVIRONMENT=local
# Install apt packages
RUN apt-get update && apt-get install --no-install-recommends -y \
# dependencies for building Python packages
build-essential \
# psycopg2 dependencies
libpq-dev
# Requirements are installed here to ensure they will be cached.
COPY ./requirements .
# Create Python Dependency and Sub-Dependency Wheels.
RUN pip wheel --wheel-dir /usr/src/app/wheels \
-r ${BUILD_ENVIRONMENT}.txt
# Python 'run' stage
FROM python as python-run-stage
ARG BUILD_ENVIRONMENT=local
ARG APP_HOME=/app
ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1
ENV BUILD_ENV ${BUILD_ENVIRONMENT}
RUN apt-get update \
# dependencies for building Python packages
&& apt-get install -y build-essential \
WORKDIR ${APP_HOME}
# Install required system dependencies
RUN apt-get update && apt-get install --no-install-recommends -y \
# psycopg2 dependencies
&& apt-get install -y libpq-dev \
libpq-dev \
# Translations dependencies
&& apt-get install -y gettext \
gettext \
# cleaning up unused files
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
&& rm -rf /var/lib/apt/lists/*
# Requirements are installed here to ensure they will be cached.
COPY ./requirements /requirements
RUN pip install -r /requirements/local.txt
# All absolute dir copies ignore workdir instruction. All relative dir copies are wrt to the workdir instruction
# copy python dependency wheels from python-build-stage
COPY --from=python-build-stage /usr/src/app/wheels /wheels/
# use wheels to install python dependencies
RUN pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* \
&& rm -rf /wheels/
COPY ./compose/production/django/entrypoint /entrypoint
RUN sed -i 's/\r$//g' /entrypoint
@ -25,6 +60,7 @@ RUN chmod +x /entrypoint
COPY ./compose/local/django/start /start
RUN sed -i 's/\r$//g' /start
RUN chmod +x /start
{% if cookiecutter.use_celery == "y" %}
COPY ./compose/local/django/celery/worker/start /start-celeryworker
RUN sed -i 's/\r$//g' /start-celeryworker
@ -38,6 +74,8 @@ COPY ./compose/local/django/celery/flower/start /start-flower
RUN sed -i 's/\r$//g' /start-flower
RUN chmod +x /start-flower
{% endif %}
WORKDIR /app
# copy application code to WORKDIR
COPY . ${APP_HOME}
ENTRYPOINT ["/entrypoint"]

View File

@ -10,4 +10,4 @@ python manage.py migrate
uvicorn config.asgi:application --host 0.0.0.0 --reload
{%- else %}
python manage.py runserver_plus 0.0.0.0:8000
{% endif %}
{%- endif %}

View File

@ -21,4 +21,3 @@ export AWS_STORAGE_BUCKET_NAME="${DJANGO_AWS_STORAGE_BUCKET_NAME}"
aws s3 cp s3://${AWS_STORAGE_BUCKET_NAME}${BACKUP_DIR_PATH}/${1} ${BACKUP_DIR_PATH}/${1}
message_success "Finished downloading ${1}."

View File

@ -27,4 +27,3 @@ message_info "Cleaning the directory ${BACKUP_DIR_PATH}"
rm -rf ${BACKUP_DIR_PATH}/*
message_success "Finished uploading and cleaning."

View File

@ -1,36 +1,75 @@
{% if cookiecutter.js_task_runner == 'Gulp' -%}
FROM node:10-stretch-slim as client-builder
WORKDIR /app
COPY ./package.json /app
ARG APP_HOME=/app
WORKDIR ${APP_HOME}
COPY ./package.json ${APP_HOME}
RUN npm install && npm cache clean --force
COPY . /app
COPY . ${APP_HOME}
RUN npm run build
# Python build stage
{%- endif %}
FROM python:3.9-slim-buster
ARG PYTHON_VERSION=3.9-slim-buster
# define an alias for the specfic python version used in this file.
FROM python:${PYTHON_VERSION} as python
# Python build stage
FROM python as python-build-stage
ARG BUILD_ENVIRONMENT=production
# Install apt packages
RUN apt-get update && apt-get install --no-install-recommends -y \
# dependencies for building Python packages
build-essential \
# psycopg2 dependencies
libpq-dev
# Requirements are installed here to ensure they will be cached.
COPY ./requirements .
# Create Python Dependency and Sub-Dependency Wheels.
RUN pip wheel --wheel-dir /usr/src/app/wheels \
-r ${BUILD_ENVIRONMENT}.txt
# Python 'run' stage
FROM python as python-run-stage
ARG BUILD_ENVIRONMENT=production
ARG APP_HOME=/app
ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1
ENV BUILD_ENV ${BUILD_ENVIRONMENT}
RUN apt-get update \
# dependencies for building Python packages
&& apt-get install -y build-essential \
# psycopg2 dependencies
&& apt-get install -y libpq-dev \
# Translations dependencies
&& apt-get install -y gettext \
# cleaning up unused files
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
&& rm -rf /var/lib/apt/lists/*
WORKDIR ${APP_HOME}
RUN addgroup --system django \
&& adduser --system --ingroup django django
# Requirements are installed here to ensure they will be cached.
COPY ./requirements /requirements
RUN pip install --no-cache-dir -r /requirements/production.txt \
&& rm -rf /requirements
# Install required system dependencies
RUN apt-get update && apt-get install --no-install-recommends -y \
# psycopg2 dependencies
libpq-dev \
# Translations dependencies
gettext \
# cleaning up unused files
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
&& rm -rf /var/lib/apt/lists/*
# All absolute dir copies ignore workdir instruction. All relative dir copies are wrt to the workdir instruction
# copy python dependency wheels from python-build-stage
COPY --from=python-build-stage /usr/src/app/wheels /wheels/
# use wheels to install python dependencies
RUN pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* \
&& rm -rf /wheels/
COPY --chown=django:django ./compose/production/django/entrypoint /entrypoint
RUN sed -i 's/\r$//g' /entrypoint
@ -58,14 +97,17 @@ RUN sed -i 's/\r$//g' /start-flower
RUN chmod +x /start-flower
{%- endif %}
# copy application code to WORKDIR
{%- if cookiecutter.js_task_runner == 'Gulp' %}
COPY --from=client-builder --chown=django:django /app /app
COPY --from=client-builder --chown=django:django ${APP_HOME} ${APP_HOME}
{% else %}
COPY --chown=django:django . /app
COPY --chown=django:django . ${APP_HOME}
{%- endif %}
# make django owner of the WORKDIR directory as well.
RUN chown django:django ${APP_HOME}
USER django
WORKDIR /app
ENTRYPOINT ["/entrypoint"]

View File

@ -35,7 +35,7 @@ http:
web-secure-router:
{%- if cookiecutter.domain_name.count('.') == 1 %}
rule: "Host(`{{ cookiecutter.domain_name }}`) || Host(`www.{{ cookiecutter.domain_name }}`)"
{% else %}
{%- else %}
rule: "Host(`{{ cookiecutter.domain_name }}`)"
{%- endif %}
entryPoints:

View File

@ -44,7 +44,7 @@ LOCALE_PATHS = [str(ROOT_DIR / "locale")]
DATABASES = {"default": env.db("DATABASE_URL")}
{%- else %}
DATABASES = {
"default": env.db("DATABASE_URL", default="postgres://{% if cookiecutter.windows == 'y' %}localhost{% endif %}/{{cookiecutter.project_slug}}")
"default": env.db("DATABASE_URL", default="postgres://{% if cookiecutter.windows == 'y' %}localhost{% endif %}/{{cookiecutter.project_slug}}"),
}
{%- endif %}
DATABASES["default"]["ATOMIC_REQUESTS"] = True
@ -230,7 +230,8 @@ X_FRAME_OPTIONS = "DENY"
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
EMAIL_BACKEND = env(
"DJANGO_EMAIL_BACKEND", default="django.core.mail.backends.smtp.EmailBackend"
"DJANGO_EMAIL_BACKEND",
default="django.core.mail.backends.smtp.EmailBackend",
)
# https://docs.djangoproject.com/en/dev/ref/settings/#email-timeout
EMAIL_TIMEOUT = 5

View File

@ -69,6 +69,14 @@ if env("USE_DOCKER") == "yes":
hostname, _, ips = socket.gethostbyname_ex(socket.gethostname())
INTERNAL_IPS += [".".join(ip.split(".")[:-1] + ["1"]) for ip in ips]
{%- if cookiecutter.js_task_runner == 'Gulp' %}
try:
_, _, ips = socket.gethostbyname_ex("node")
INTERNAL_IPS.extend(ips)
except socket.gaierror:
# The node container isn't started (yet?)
pass
{%- endif %}
{%- endif %}
# django-extensions

View File

@ -146,7 +146,8 @@ DEFAULT_FROM_EMAIL = env(
SERVER_EMAIL = env("DJANGO_SERVER_EMAIL", default=DEFAULT_FROM_EMAIL)
# https://docs.djangoproject.com/en/dev/ref/settings/#email-subject-prefix
EMAIL_SUBJECT_PREFIX = env(
"DJANGO_EMAIL_SUBJECT_PREFIX", default="[{{cookiecutter.project_name}}]"
"DJANGO_EMAIL_SUBJECT_PREFIX",
default="[{{cookiecutter.project_name}}]",
)
# ADMIN

View File

@ -15,16 +15,6 @@ SECRET_KEY = env(
# https://docs.djangoproject.com/en/dev/ref/settings/#test-runner
TEST_RUNNER = "django.test.runner.DiscoverRunner"
# CACHES
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#caches
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
"LOCATION": "",
}
}
# PASSWORDS
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers

View File

@ -29,14 +29,14 @@ function pathsConfig(appName) {
const vendorsRoot = 'node_modules'
return {
{% if cookiecutter.custom_bootstrap_compilation == 'y' %}
{%- if cookiecutter.custom_bootstrap_compilation == 'y' %}
bootstrapSass: `${vendorsRoot}/bootstrap/scss`,
vendorsJs: [
`${vendorsRoot}/jquery/dist/jquery.slim.js`,
`${vendorsRoot}/popper.js/dist/umd/popper.js`,
`${vendorsRoot}/bootstrap/dist/js/bootstrap.js`,
],
{% endif %}
{%- endif %}
app: this.app,
templates: `${this.app}/templates`,
css: `${this.app}/static/css`,
@ -67,9 +67,9 @@ function styles() {
return src(`${paths.sass}/project.scss`)
.pipe(sass({
includePaths: [
{% if cookiecutter.custom_bootstrap_compilation == 'y' %}
{%- if cookiecutter.custom_bootstrap_compilation == 'y' %}
paths.bootstrapSass,
{% endif %}
{%- endif %}
paths.sass
]
}).on('error', sass.logError))
@ -90,7 +90,7 @@ function scripts() {
.pipe(dest(paths.js))
}
{% if cookiecutter.custom_bootstrap_compilation == 'y' %}
{%- if cookiecutter.custom_bootstrap_compilation == 'y' %}
// Vendor Javascript minification
function vendorScripts() {
return src(paths.vendorsJs)
@ -101,7 +101,7 @@ function vendorScripts() {
.pipe(rename({ suffix: '.min' }))
.pipe(dest(paths.js))
}
{% endif %}
{%- endif %}
// Image compression
function imgCompression() {
@ -110,7 +110,7 @@ function imgCompression() {
.pipe(dest(paths.images))
}
{% if cookiecutter.use_async == 'y' -%}
{%- if cookiecutter.use_async == 'y' -%}
// Run django server
function asyncRunServer() {
var cmd = spawn('gunicorn', [
@ -143,7 +143,7 @@ function initBrowserSync() {
// https://www.browsersync.io/docs/options/#option-proxy
{%- if cookiecutter.use_docker == 'n' %}
proxy: 'localhost:8000'
{% else %}
{%- else %}
proxy: {
target: 'django:8000',
proxyReq: [
@ -172,7 +172,7 @@ function watchPaths() {
const generateAssets = parallel(
styles,
scripts,
{% if cookiecutter.custom_bootstrap_compilation == 'y' %}vendorScripts,{% endif %}
{%- if cookiecutter.custom_bootstrap_compilation == 'y' %}vendorScripts,{% endif %}
imgCompression
)

View File

@ -52,7 +52,6 @@ services:
ports:
- "7000:7000"
command: /start-docs
{%- if cookiecutter.use_mailhog == 'y' %}
mailhog:
@ -75,7 +74,7 @@ services:
depends_on:
- redis
- postgres
{% if cookiecutter.use_mailhog == 'y' -%}
{%- if cookiecutter.use_mailhog == 'y' %}
- mailhog
{%- endif %}
ports: []
@ -88,7 +87,7 @@ services:
depends_on:
- redis
- postgres
{% if cookiecutter.use_mailhog == 'y' -%}
{%- if cookiecutter.use_mailhog == 'y' %}
- mailhog
{%- endif %}
ports: []

View File

@ -64,10 +64,9 @@ services:
<<: *django
image: {{ cookiecutter.project_slug }}_production_flower
command: /start-flower
{%- endif %}
{%- if cookiecutter.cloud_provider == 'AWS' %}
{% if cookiecutter.cloud_provider == 'AWS' %}
awscli:
build:
context: .

View File

@ -1,6 +1,6 @@
pytz==2020.5 # https://github.com/stub42/pytz
pytz==2021.1 # https://github.com/stub42/pytz
python-slugify==4.0.1 # https://github.com/un33k/python-slugify
Pillow==8.1.0 # https://github.com/python-pillow/Pillow
Pillow==8.2.0 # https://github.com/python-pillow/Pillow
{%- if cookiecutter.use_compressor == "y" %}
{%- if cookiecutter.windows == 'y' and cookiecutter.use_docker == 'n' %}
rcssmin==1.0.6 --install-option="--without-c-extensions" # https://github.com/ndparker/rcssmin
@ -24,22 +24,22 @@ flower==0.9.7 # https://github.com/mher/flower
{%- endif %}
{%- endif %}
{%- if cookiecutter.use_async == 'y' %}
uvicorn[standard]==0.13.3 # https://github.com/encode/uvicorn
uvicorn[standard]==0.13.4 # https://github.com/encode/uvicorn
{%- endif %}
# Django
# ------------------------------------------------------------------------------
django==3.0.11 # pyup: < 3.1 # https://www.djangoproject.com/
django==3.1.8 # pyup: < 3.2 # https://www.djangoproject.com/
django-environ==0.4.5 # https://github.com/joke2k/django-environ
django-model-utils==4.1.1 # https://github.com/jazzband/django-model-utils
django-allauth==0.44.0 # https://github.com/pennersr/django-allauth
django-crispy-forms==1.10.0 # https://github.com/django-crispy-forms/django-crispy-forms
django-crispy-forms==1.11.2 # https://github.com/django-crispy-forms/django-crispy-forms
{%- if cookiecutter.use_compressor == "y" %}
django-compressor==2.4 # https://github.com/django-compressor/django-compressor
{%- endif %}
django-redis==4.12.1 # https://github.com/jazzband/django-redis
{%- if cookiecutter.use_drf == "y" %}
# Django REST Framework
djangorestframework==3.12.2 # https://github.com/encode/django-rest-framework
djangorestframework==3.12.4 # https://github.com/encode/django-rest-framework
django-cors-headers==3.7.0 # https://github.com/adamchainz/django-cors-headers
{%- endif %}

View File

@ -1,7 +1,7 @@
-r base.txt
Werkzeug==1.0.1 # https://github.com/pallets/werkzeug
ipdb==0.13.4 # https://github.com/gotcha/ipdb
ipdb==0.13.7 # https://github.com/gotcha/ipdb
{%- if cookiecutter.use_docker == 'y' %}
psycopg2==2.8.6 # https://github.com/psycopg/psycopg2
{%- else %}
@ -13,33 +13,33 @@ watchgod==0.6 # https://github.com/samuelcolvin/watchgod
# Testing
# ------------------------------------------------------------------------------
mypy==0.800 # https://github.com/python/mypy
mypy==0.812 # https://github.com/python/mypy
django-stubs==1.7.0 # https://github.com/typeddjango/django-stubs
pytest==6.2.2 # https://github.com/pytest-dev/pytest
pytest==6.2.3 # https://github.com/pytest-dev/pytest
pytest-sugar==0.9.4 # https://github.com/Frozenball/pytest-sugar
# Documentation
# ------------------------------------------------------------------------------
sphinx==3.4.3 # https://github.com/sphinx-doc/sphinx
sphinx-autobuild==2020.9.1 # https://github.com/GaretJax/sphinx-autobuild
sphinx==3.5.3 # https://github.com/sphinx-doc/sphinx
sphinx-autobuild==2021.3.14 # https://github.com/GaretJax/sphinx-autobuild
# Code quality
# ------------------------------------------------------------------------------
flake8==3.8.4 # https://github.com/PyCQA/flake8
flake8==3.9.0 # https://github.com/PyCQA/flake8
flake8-isort==4.0.0 # https://github.com/gforcada/flake8-isort
coverage==5.3.1 # https://github.com/nedbat/coveragepy
coverage==5.5 # https://github.com/nedbat/coveragepy
black==20.8b1 # https://github.com/ambv/black
pylint-django==2.4.2 # https://github.com/PyCQA/pylint-django
{%- if cookiecutter.use_celery == 'y' %}
pylint-celery==0.3 # https://github.com/PyCQA/pylint-celery
{%- endif %}
pre-commit==2.9.3 # https://github.com/pre-commit/pre-commit
pre-commit==2.12.0 # https://github.com/pre-commit/pre-commit
# Django
# ------------------------------------------------------------------------------
factory-boy==3.2.0 # https://github.com/FactoryBoy/factory_boy
django-debug-toolbar==3.2 # https://github.com/jazzband/django-debug-toolbar
django-extensions==3.1.0 # https://github.com/django-extensions/django-extensions
django-extensions==3.1.2 # https://github.com/django-extensions/django-extensions
django-coverage-plugin==1.8.0 # https://github.com/nedbat/django_coverage_plugin
pytest-django==4.1.0 # https://github.com/pytest-dev/pytest-django

View File

@ -2,13 +2,13 @@
-r base.txt
gunicorn==20.0.4 # https://github.com/benoitc/gunicorn
gunicorn==20.1.0 # https://github.com/benoitc/gunicorn
psycopg2==2.8.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==0.19.5 # https://github.com/getsentry/sentry-python
sentry-sdk==1.0.0 # https://github.com/getsentry/sentry-python
{%- endif %}
{%- if cookiecutter.use_docker == "n" and cookiecutter.windows == "y" %}
hiredis==1.1.0 # https://github.com/redis/hiredis-py
@ -22,21 +22,21 @@ django-storages[boto3]==1.11.1 # https://github.com/jschneier/django-storages
django-storages[google]==1.11.1 # https://github.com/jschneier/django-storages
{%- endif %}
{%- if cookiecutter.mail_service == 'Mailgun' %}
django-anymail[mailgun]==8.1 # https://github.com/anymail/django-anymail
django-anymail[mailgun]==8.2 # https://github.com/anymail/django-anymail
{%- elif cookiecutter.mail_service == 'Amazon SES' %}
django-anymail[amazon_ses]==8.1 # https://github.com/anymail/django-anymail
django-anymail[amazon_ses]==8.2 # https://github.com/anymail/django-anymail
{%- elif cookiecutter.mail_service == 'Mailjet' %}
django-anymail[mailjet]==8.1 # https://github.com/anymail/django-anymail
django-anymail[mailjet]==8.2 # https://github.com/anymail/django-anymail
{%- elif cookiecutter.mail_service == 'Mandrill' %}
django-anymail[mandrill]==8.1 # https://github.com/anymail/django-anymail
django-anymail[mandrill]==8.2 # https://github.com/anymail/django-anymail
{%- elif cookiecutter.mail_service == 'Postmark' %}
django-anymail[postmark]==8.1 # https://github.com/anymail/django-anymail
django-anymail[postmark]==8.2 # https://github.com/anymail/django-anymail
{%- elif cookiecutter.mail_service == 'Sendgrid' %}
django-anymail[sendgrid]==8.1 # https://github.com/anymail/django-anymail
django-anymail[sendgrid]==8.2 # https://github.com/anymail/django-anymail
{%- elif cookiecutter.mail_service == 'SendinBlue' %}
django-anymail[sendinblue]==8.1 # https://github.com/anymail/django-anymail
django-anymail[sendinblue]==8.2 # https://github.com/anymail/django-anymail
{%- elif cookiecutter.mail_service == 'SparkPost' %}
django-anymail[sparkpost]==8.1 # https://github.com/anymail/django-anymail
django-anymail[sparkpost]==8.2 # https://github.com/anymail/django-anymail
{%- elif cookiecutter.mail_service == 'Other SMTP' %}
django-anymail==8.1 # https://github.com/anymail/django-anymail
django-anymail==8.2 # https://github.com/anymail/django-anymail
{%- endif %}

View File

@ -33,9 +33,8 @@ if [ -z "$VIRTUAL_ENV" ]; then
echo >&2 -e "\n"
exit 1;
else
pip install -r $PROJECT_DIR/requirements/local.txt
{% if cookiecutter.use_heroku == "y" -%}
{%- if cookiecutter.use_heroku == "y" -%}
pip install -r $PROJECT_DIR/requirements.txt
{%- endif %}
fi

View File

@ -0,0 +1,23 @@
##basic build dependencies of various Django apps for Ubuntu Focal 20.04
#build-essential metapackage install: make, gcc, g++,
build-essential
#required to translate
gettext
python3-dev
##shared dependencies of:
##Pillow, pylibmc
zlib1g-dev
##Postgresql and psycopg2 dependencies
libpq-dev
##Pillow dependencies
libtiff5-dev
libjpeg8-dev
libfreetype6-dev
liblcms2-dev
libwebp-dev
##django-extensions
graphviz-dev

View File

@ -0,0 +1,17 @@
# Generated by Django 3.1.7 on 2021-02-04 14:49
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('sites', '0003_set_site_domain_and_name'),
]
operations = [
migrations.AlterModelOptions(
name='site',
options={'ordering': ['domain'], 'verbose_name': 'site', 'verbose_name_plural': 'sites'},
),
]

View File

@ -5,5 +5,6 @@
{% block content %}
<h1>Forbidden (403)</h1>
<p>CSRF verification failed. Request aborted.</p>
{% endblock content %}{% endraw %}
<p>{% if exception %}{{ exception }}{% else %}You're not allowed to access this page.{% endif %}</p>
{% endblock content %}
{%- endraw %}

View File

@ -5,5 +5,6 @@
{% block content %}
<h1>Page not found</h1>
<p>This is not the page you were looking for.</p>
{% endblock content %}{% endraw %}
<p>{% if exception %}{{ exception }}{% else %}This is not the page you were looking for.{% endif %}</p>
{% endblock content %}
{%- endraw %}

View File

@ -9,5 +9,4 @@
<p>We track these errors automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.</p>
{% endblock content %}
{% endraw %}
{%- endraw %}

View File

@ -9,4 +9,4 @@
<p>{% trans "This account is inactive." %}</p>
{% endblock %}
{% endraw %}
{%- endraw %}

View File

@ -8,4 +8,4 @@
</div>
</div>
{% endblock %}
{% endraw %}
{%- endraw %}

View File

@ -77,4 +77,4 @@ window.addEventListener('DOMContentLoaded',function() {
$('.form-group').removeClass('row');
</script>
{% endblock %}
{% endraw %}
{%- endraw %}

View File

@ -29,4 +29,4 @@
{% endif %}
{% endblock %}
{% endraw %}
{%- endraw %}

View File

@ -45,4 +45,4 @@ for a {{ site_name }} account and sign in below:{% endblocktrans %}</p>
</form>
{% endblock %}
{% endraw %}
{%- endraw %}

View File

@ -16,7 +16,5 @@
{% endif %}
<button class="btn btn-danger" type="submit">{% trans 'Sign Out' %}</button>
</form>
{% endblock %}
{% endraw %}
{%- endraw %}

View File

@ -14,4 +14,4 @@
<button class="btn btn-primary" type="submit" name="action">{% trans "Change Password" %}</button>
</form>
{% endblock %}
{% endraw %}
{%- endraw %}

View File

@ -23,4 +23,4 @@
<p>{% blocktrans %}Please contact us if you have any trouble resetting your password.{% endblocktrans %}</p>
{% endblock %}
{% endraw %}
{%- endraw %}

View File

@ -7,11 +7,11 @@
{% block inner %}
<h1>{% trans "Password Reset" %}</h1>
{% if user.is_authenticated %}
{% include "account/snippets/already_logged_in.html" %}
{% endif %}
<p>{% blocktrans %}We have sent you an e-mail. Please contact us if you do not receive it within a few minutes.{% endblocktrans %}</p>
{% endblock %}
{% endraw %}
{%- endraw %}

View File

@ -22,4 +22,4 @@
{% endif %}
{% endif %}
{% endblock %}
{% endraw %}
{%- endraw %}

View File

@ -7,4 +7,4 @@
<h1>{% trans "Change Password" %}</h1>
<p>{% trans 'Your password is now changed.' %}</p>
{% endblock %}
{% endraw %}
{%- endraw %}

View File

@ -14,4 +14,4 @@
<input class="btn btn-primary" type="submit" name="action" value="{% trans 'Set Password' %}"/>
</form>
{% endblock %}
{% endraw %}
{%- endraw %}

View File

@ -20,4 +20,4 @@
</form>
{% endblock %}
{% endraw %}
{%- endraw %}

View File

@ -9,4 +9,4 @@
<p>{% trans "We are sorry, but the sign up is currently closed." %}</p>
{% endblock %}
{% endraw %}
{%- endraw %}

View File

@ -10,4 +10,4 @@
<p>{% blocktrans %}We have sent an e-mail to you for verification. Follow the link provided to finalize the signup process. Please contact us if you do not receive it within a few minutes.{% endblocktrans %}</p>
{% endblock %}
{% endraw %}
{%- endraw %}

View File

@ -18,7 +18,5 @@ verification. Please click on the link inside this e-mail. Please
contact us if you do not receive it within a few minutes.{% endblocktrans %}</p>
<p>{% blocktrans %}<strong>Note:</strong> you can still <a href="{{ email_url }}">change your e-mail address</a>.{% endblocktrans %}</p>
{% endblock %}
{% endraw %}
{%- endraw %}

View File

@ -16,43 +16,43 @@
<link rel="icon" href="{% static 'images/favicons/favicon.ico' %}">
{% block css %}
{% endraw %}{% if cookiecutter.custom_bootstrap_compilation == "n" %}{% raw %}
{%- endraw %}{% if cookiecutter.custom_bootstrap_compilation == "n" %}{% raw %}
<!-- Latest compiled and minified Bootstrap CSS -->
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
{% endraw %}{% endif %}{% raw %}
{%- endraw %}{% endif %}{% raw %}
<!-- Your stuff: Third-party CSS libraries go here -->
{% endraw %}{% if cookiecutter.use_compressor == "y" %}{% raw %}{% compress css %}{% endraw %}{% endif %}{% raw %}
{%- endraw %}{% if cookiecutter.use_compressor == "y" %}{% raw %}{% compress css %}{% endraw %}{% endif %}{% raw %}
<!-- This file stores project-specific CSS -->
{% endraw %}{% if cookiecutter.js_task_runner == "Gulp" and cookiecutter.use_compressor == "n" %}{% raw %}
{%- endraw %}{% if cookiecutter.js_task_runner == "Gulp" and cookiecutter.use_compressor == "n" %}{% raw %}
<link href="{% static 'css/project.min.css' %}" rel="stylesheet">
{% endraw %}{% else %}{% raw %}
{%- endraw %}{% else %}{% raw %}
<link href="{% static 'css/project.css' %}" rel="stylesheet">
{% endraw %}{% endif %}{% raw %}
{% endraw %}{% if cookiecutter.use_compressor == "y" %}{% raw %}{% endcompress %}{% endraw %}{% endif %}{% raw %}
{%- endraw %}{% endif %}{% raw %}
{%- endraw %}{% if cookiecutter.use_compressor == "y" %}{% raw %}{% endcompress %}{% endraw %}{% endif %}{% raw %}
{% endblock %}
<!-- Le javascript
================================================== -->
{# Placed at the top of the document so pages load faster with defer #}
{% block javascript %}
{% endraw %}{% if cookiecutter.custom_bootstrap_compilation == "y" and cookiecutter.js_task_runner == "Gulp" %}{% raw %}
{%- endraw %}{% if cookiecutter.custom_bootstrap_compilation == "y" and cookiecutter.js_task_runner == "Gulp" %}{% raw %}
<!-- Vendor dependencies bundled as one file-->
{% endraw %}{% if cookiecutter.use_compressor == "y" %}{% raw %}{% compress js %}{% endraw %}{% endif %}{% raw %}
{%- endraw %}{% if cookiecutter.use_compressor == "y" %}{% raw %}{% compress js %}{% endraw %}{% endif %}{% raw %}
<script defer src="{% static 'js/vendors.js' %}"></script>
{% endraw %}{% if cookiecutter.use_compressor == "y" %}{% raw %}{% endcompress %}{% endraw %}{% endif %}{% raw %}
{% endraw %}{% else %}{% raw %}
{%- endraw %}{% if cookiecutter.use_compressor == "y" %}{% raw %}{% endcompress %}{% endraw %}{% endif %}{% raw %}
{%- endraw %}{% else %}{% raw %}
<!-- Bootstrap JS and its dependencies-->
<script defer src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script defer src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<!-- Your stuff: Third-party javascript libraries go here -->
{% endraw %}{% endif %}{% raw %}
{%- endraw %}{% endif %}{% raw %}
<!-- place project specific Javascript in this file -->
{% endraw %}{% if cookiecutter.use_compressor == "y" %}{% raw %}{% compress js %}{% endraw %}{% endif %}{% raw %}
{%- endraw %}{% if cookiecutter.use_compressor == "y" %}{% raw %}{% compress js %}{% endraw %}{% endif %}{% raw %}
<script defer src="{% static 'js/project.js' %}"></script>
{% endraw %}{% if cookiecutter.use_compressor == "y" %}{% raw %}{% endcompress %}{% endraw %}{% endif %}{% raw %}
{%- endraw %}{% if cookiecutter.use_compressor == "y" %}{% raw %}{% endcompress %}{% endraw %}{% endif %}{% raw %}
{% endblock javascript %}
@ -121,4 +121,4 @@
{% endblock inline_javascript %}
</body>
</html>
{% endraw %}
{%- endraw %}

View File

@ -1 +1 @@
{% raw %}{% extends "base.html" %}{% endraw %}
{% raw %}{% extends "base.html" %}{% endraw %}

View File

@ -1 +1 @@
{% raw %}{% extends "base.html" %}{% endraw %}
{% raw %}{% extends "base.html" %}{% endraw %}

View File

@ -30,7 +30,6 @@
<!-- End Action buttons -->
{% endif %}
</div>
{% endblock content %}
{% endraw %}
{%- endraw %}

View File

@ -14,4 +14,5 @@
</div>
</div>
</form>
{% endblock %}{% endraw %}
{% endblock %}
{%- endraw %}

View File

@ -1,6 +1,7 @@
from django.contrib import admin
from django.contrib.auth import admin as auth_admin
from django.contrib.auth import get_user_model
from django.utils.translation import gettext_lazy as _
from {{ cookiecutter.project_slug }}.users.forms import UserChangeForm, UserCreationForm
@ -12,8 +13,22 @@ class UserAdmin(auth_admin.UserAdmin):
form = UserChangeForm
add_form = UserCreationForm
fieldsets = (("User", {"fields": ("name",)}),) + tuple(
auth_admin.UserAdmin.fieldsets
fieldsets = (
(None, {"fields": ("username", "password")}),
(_("Personal info"), {"fields": ("name", "email")}),
(
_("Permissions"),
{
"fields": (
"is_active",
"is_staff",
"is_superuser",
"groups",
"user_permissions",
),
},
),
(_("Important dates"), {"fields": ("last_login", "date_joined")}),
)
list_display = ["username", "name", "is_superuser"]
search_fields = ["name"]

View File

@ -7,7 +7,7 @@ User = get_user_model()
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["username", "email", "name", "url"]
fields = ["username", "name", "url"]
extra_kwargs = {
"url": {"view_name": "api:user-detail", "lookup_field": "username"}

View File

@ -1,6 +1,5 @@
from django.contrib.auth import forms as admin_forms
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
User = get_user_model()
@ -12,20 +11,9 @@ class UserChangeForm(admin_forms.UserChangeForm):
class UserCreationForm(admin_forms.UserCreationForm):
error_message = admin_forms.UserCreationForm.error_messages.update(
{"duplicate_username": _("This username has already been taken.")}
)
class Meta(admin_forms.UserCreationForm.Meta):
model = User
def clean_username(self):
username = self.cleaned_data["username"]
try:
User.objects.get(username=username)
except User.DoesNotExist:
return username
raise ValidationError(self.error_messages["duplicate_username"])
error_messages = {
"username": {"unique": _("This username has already been taken.")}
}

View File

@ -0,0 +1,40 @@
import pytest
from django.urls import reverse
from {{ cookiecutter.project_slug }}.users.models import User
pytestmark = pytest.mark.django_db
class TestUserAdmin:
def test_changelist(self, admin_client):
url = reverse("admin:users_user_changelist")
response = admin_client.get(url)
assert response.status_code == 200
def test_search(self, admin_client):
url = reverse("admin:users_user_changelist")
response = admin_client.get(url, data={"q": "test"})
assert response.status_code == 200
def test_add(self, admin_client):
url = reverse("admin:users_user_add")
response = admin_client.get(url)
assert response.status_code == 200
response = admin_client.post(
url,
data={
"username": "test",
"password1": "My_R@ndom-P@ssw0rd",
"password2": "My_R@ndom-P@ssw0rd",
},
)
assert response.status_code == 302
assert User.objects.filter(username="test").exists()
def test_view_user(self, admin_client):
user = User.objects.get(username="admin")
url = reverse("admin:users_user_change", kwargs={"object_id": user.pk})
response = admin_client.get(url)
assert response.status_code == 200

View File

@ -28,7 +28,6 @@ class TestUserViewSet:
assert response.data == {
"username": user.username,
"email": user.email,
"name": user.name,
"url": f"http://testserver/api/users/{user.username}/",
}

View File

@ -1,40 +1,39 @@
"""
Module for all Form Tests.
"""
import pytest
from django.utils.translation import ugettext_lazy as _
from {{ cookiecutter.project_slug }}.users.forms import UserCreationForm
from {{ cookiecutter.project_slug }}.users.tests.factories import UserFactory
from {{ cookiecutter.project_slug }}.users.models import User
pytestmark = pytest.mark.django_db
class TestUserCreationForm:
def test_clean_username(self):
# A user with proto_user params does not exist yet.
proto_user = UserFactory.build()
"""
Test class for all tests related to the UserCreationForm
"""
form = UserCreationForm(
{
"username": proto_user.username,
"password1": proto_user._password,
"password2": proto_user._password,
}
)
def test_username_validation_error_msg(self, user: User):
"""
Tests UserCreation Form's unique validator functions correctly by testing:
1) A new user with an existing username cannot be added.
2) Only 1 error is raised by the UserCreation Form
3) The desired error message is raised
"""
assert form.is_valid()
assert form.clean_username() == proto_user.username
# Creating a user.
form.save()
# The user with proto_user params already exists,
# The user already exists,
# hence cannot be created.
form = UserCreationForm(
{
"username": proto_user.username,
"password1": proto_user._password,
"password2": proto_user._password,
"username": user.username,
"password1": user.password,
"password2": user.password,
}
)
assert not form.is_valid()
assert len(form.errors) == 1
assert "username" in form.errors
assert form.errors["username"][0] == _("This username has already been taken.")

View File

@ -1,10 +1,11 @@
import pytest
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.models import AnonymousUser
from django.contrib.messages.middleware import MessageMiddleware
from django.contrib.sessions.middleware import SessionMiddleware
from django.http.response import Http404
from django.test import RequestFactory
from django.urls import reverse
from {{ cookiecutter.project_slug }}.users.forms import UserChangeForm
from {{ cookiecutter.project_slug }}.users.models import User
@ -90,13 +91,7 @@ class TestUserDetailView:
request.user = AnonymousUser()
response = user_detail_view(request, username=user.username)
login_url = reverse(settings.LOGIN_URL)
assert response.status_code == 302
assert response.url == "/accounts/login/?next=/fake-url/"
def test_case_sensitivity(self, rf: RequestFactory):
request = rf.get("/fake-url/")
request.user = UserFactory(username="UserName")
with pytest.raises(Http404):
user_detail_view(request, username="username")
assert response.url == f"{login_url}?next=/fake-url/"

View File

@ -25,7 +25,7 @@ class UserUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
success_message = _("Information successfully updated")
def get_success_url(self):
return reverse("users:detail", kwargs={"username": self.request.user.username})
return self.request.user.get_absolute_url() # type: ignore [union-attr]
def get_object(self):
return self.request.user