Merge remote-tracking branch 'origin/master' into update-doc

This commit is contained in:
Bruno Alla 2020-01-23 18:38:26 +00:00
commit 00772d7141
38 changed files with 275 additions and 133 deletions

View File

@ -2,6 +2,19 @@
All enhancements and patches to Cookiecutter Django will be documented in this file. All enhancements and patches to Cookiecutter Django will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). This project adheres to [Semantic Versioning](http://semver.org/).
## [2020-01-12]
### Changed
- Fix mypy setup and added django-stubs
- Add Gitlab CI as option
## [2020-01-11]
### Changed
- Speed up & reduce size for production Django image
- Bumped runtime version for Heroku
- Added Debian 10 (Buster) OS dependencies
- Update Traefik to v2
- Switched Docker images from Alpine based to Debian based
## [2019-10-06] ## [2019-10-06]
### Changed ### Changed
- Default Python version is now 3.7 (@nicolas471) - Default Python version is now 3.7 (@nicolas471)

View File

@ -71,6 +71,7 @@ Listed in alphabetical order.
Benjamin Abel Benjamin Abel
Bert de Miranda `@bertdemiranda`_ Bert de Miranda `@bertdemiranda`_
Bo Lopker `@blopker`_ Bo Lopker `@blopker`_
Bo Peng `@BoPeng`_
Bouke Haarsma Bouke Haarsma
Brent Payne `@brentpayne`_ @brentpayne Brent Payne `@brentpayne`_ @brentpayne
Bruce Olivier `@bolivierjr`_ Bruce Olivier `@bolivierjr`_
@ -94,6 +95,7 @@ Listed in alphabetical order.
Dan Shultz `@shultz`_ Dan Shultz `@shultz`_
Dani Hodovic `@danihodovic`_ Dani Hodovic `@danihodovic`_
Daniel Hepper `@dhepper`_ @danielhepper Daniel Hepper `@dhepper`_ @danielhepper
Daniel Hillier `@danifus`_
Daniele Tricoli `@eriol`_ Daniele Tricoli `@eriol`_
David Díaz `@ddiazpinto`_ @DavidDiazPinto David Díaz `@ddiazpinto`_ @DavidDiazPinto
Davit Tovmasyan `@davitovmasyan`_ Davit Tovmasyan `@davitovmasyan`_
@ -102,6 +104,7 @@ Listed in alphabetical order.
Demetris Stavrou `@demestav`_ Demetris Stavrou `@demestav`_
Denis Bobrov `@delneg`_ Denis Bobrov `@delneg`_
Denis Orehovsky `@apirobot`_ Denis Orehovsky `@apirobot`_
Denis Savran `@blaxpy`_
Diane Chen `@purplediane`_ @purplediane88 Diane Chen `@purplediane`_ @purplediane88
Dónal Adams `@epileptic-fish`_ Dónal Adams `@epileptic-fish`_
Dong Huynh `@trungdong`_ Dong Huynh `@trungdong`_
@ -122,6 +125,8 @@ Listed in alphabetical order.
Henrique G. G. Pereira `@ikkebr`_ Henrique G. G. Pereira `@ikkebr`_
Ian Lee `@IanLee1521`_ Ian Lee `@IanLee1521`_
Irfan Ahmad `@erfaan`_ @erfaan Irfan Ahmad `@erfaan`_ @erfaan
Isaac12x `@Isaac12x`_
Ivan Khomutov `@ikhomutov`_
Jan Van Bruggen `@jvanbrug`_ Jan Van Bruggen `@jvanbrug`_
Jelmer Draaijer `@foarsitter`_ Jelmer Draaijer `@foarsitter`_
Jens Nilsson `@phiberjenz`_ Jens Nilsson `@phiberjenz`_
@ -226,10 +231,12 @@ Listed in alphabetical order.
.. _@arruda: https://github.com/arruda .. _@arruda: https://github.com/arruda
.. _@bertdemiranda: https://github.com/bertdemiranda .. _@bertdemiranda: https://github.com/bertdemiranda
.. _@bittner: https://github.com/bittner .. _@bittner: https://github.com/bittner
.. _@blaxpy: https://github.com/blaxpy
.. _@bloodpet: https://github.com/bloodpet .. _@bloodpet: https://github.com/bloodpet
.. _@blopker: https://github.com/blopker .. _@blopker: https://github.com/blopker
.. _@bogdal: https://github.com/bogdal .. _@bogdal: https://github.com/bogdal
.. _@bolivierjr: https://github.com/bolivierjr .. _@bolivierjr: https://github.com/bolivierjr
.. _@BoPeng: https://github.com/BoPeng
.. _@brentpayne: https://github.com/brentpayne .. _@brentpayne: https://github.com/brentpayne
.. _@btknu: https://github.com/btknu .. _@btknu: https://github.com/btknu
.. _@burhan: https://github.com/burhan .. _@burhan: https://github.com/burhan
@ -252,6 +259,7 @@ Listed in alphabetical order.
.. _@curtisstpierre: https://github.com/curtisstpierre .. _@curtisstpierre: https://github.com/curtisstpierre
.. _@dadokkio: https://github.com/dadokkio .. _@dadokkio: https://github.com/dadokkio
.. _@danihodovic: https://github.com/danihodovic .. _@danihodovic: https://github.com/danihodovic
.. _@danifus: https://github.com/danifus
.. _@davitovmasyan: https://github.com/davitovmasyan .. _@davitovmasyan: https://github.com/davitovmasyan
.. _@ddiazpinto: https://github.com/ddiazpinto .. _@ddiazpinto: https://github.com/ddiazpinto
.. _@delneg: https://github.com/delneg .. _@delneg: https://github.com/delneg
@ -281,7 +289,9 @@ Listed in alphabetical order.
.. _@hendrikschneider: https://github.com/hendrikschneider .. _@hendrikschneider: https://github.com/hendrikschneider
.. _@hjwp: https://github.com/hjwp .. _@hjwp: https://github.com/hjwp
.. _@IanLee1521: https://github.com/IanLee1521 .. _@IanLee1521: https://github.com/IanLee1521
.. _@ikhomutov: https://github.com/ikhomutov
.. _@ikkebr: https://github.com/ikkebr .. _@ikkebr: https://github.com/ikkebr
.. _@Isaac12x: https://github.com/Isaac12x
.. _@iynaix: https://github.com/iynaix .. _@iynaix: https://github.com/iynaix
.. _@jangeador: https://github.com/jangeador .. _@jangeador: https://github.com/jangeador
.. _@jazztpt: https://github.com/jazztpt .. _@jazztpt: https://github.com/jazztpt

View File

@ -9,8 +9,8 @@ Cookiecutter Django
:target: https://pyup.io/repos/github/pydanny/cookiecutter-django/ :target: https://pyup.io/repos/github/pydanny/cookiecutter-django/
:alt: Updates :alt: Updates
.. image:: https://badges.gitter.im/Join Chat.svg .. image:: https://img.shields.io/badge/cookiecutter-Join%20on%20Slack-green?style=flat&logo=slack
:target: https://gitter.im/pydanny/cookiecutter-django?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge :target: https://join.slack.com/t/cookie-cutter/shared_invite/enQtNzI0Mzg5NjE5Nzk5LTRlYWI2YTZhYmQ4YmU1Y2Q2NmE1ZjkwOGM0NDQyNTIwY2M4ZTgyNDVkNjMxMDdhZGI5ZGE5YmJjM2M3ODJlY2U
.. image:: https://www.codetriage.com/pydanny/cookiecutter-django/badges/users.svg .. image:: https://www.codetriage.com/pydanny/cookiecutter-django/badges/users.svg
:target: https://www.codetriage.com/pydanny/cookiecutter-django :target: https://www.codetriage.com/pydanny/cookiecutter-django
@ -226,11 +226,11 @@ Community
* Have questions? **Before you ask questions anywhere else**, please post your question on `Stack Overflow`_ under the *cookiecutter-django* tag. We check there periodically for questions. * Have questions? **Before you ask questions anywhere else**, please post your question on `Stack Overflow`_ 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_. * If you think you found a bug or want to request a feature, please open an issue_.
* For anything else, you can chat with us on `Gitter`_. * For anything else, you can chat with us on `Slack`_.
.. _`Stack Overflow`: http://stackoverflow.com/questions/tagged/cookiecutter-django .. _`Stack Overflow`: http://stackoverflow.com/questions/tagged/cookiecutter-django
.. _`issue`: https://github.com/pydanny/cookiecutter-django/issues .. _`issue`: https://github.com/pydanny/cookiecutter-django/issues
.. _`Gitter`: https://gitter.im/pydanny/cookiecutter-django?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge .. _`Slack`: https://join.slack.com/t/cookie-cutter/shared_invite/enQtNzI0Mzg5NjE5Nzk5LTRlYWI2YTZhYmQ4YmU1Y2Q2NmE1ZjkwOGM0NDQyNTIwY2M4ZTgyNDVkNjMxMDdhZGI5ZGE5YmJjM2M3ODJlY2U
For Readers of Two Scoops of Django For Readers of Two Scoops of Django
-------------------------------------------- --------------------------------------------

View File

@ -40,7 +40,11 @@
"use_sentry": "n", "use_sentry": "n",
"use_whitenoise": "n", "use_whitenoise": "n",
"use_heroku": "n", "use_heroku": "n",
"use_travisci": "n", "ci_tool": [
"None",
"Travis",
"Gitlab"
],
"keep_local_envs_in_vcs": "y", "keep_local_envs_in_vcs": "y",
"debug": "n" "debug": "n"

View File

@ -94,8 +94,12 @@ use_heroku:
Indicates whether the project should be configured so as to be deployable Indicates whether the project should be configured so as to be deployable
to Heroku_. to Heroku_.
use_travisci: ci_tool:
Indicates whether the project should be configured to use `Travis CI`_. Select a CI tool for running tests. The choices are:
1. None
2. Travis_
3. Gitlab_
keep_local_envs_in_vcs: keep_local_envs_in_vcs:
Indicates whether the project's ``.envs/.local/`` should be kept in VCS Indicates whether the project's ``.envs/.local/`` should be kept in VCS
@ -138,3 +142,6 @@ debug:
.. _Heroku: https://github.com/heroku/heroku-buildpack-python .. _Heroku: https://github.com/heroku/heroku-buildpack-python
.. _Travis CI: https://travis-ci.org/ .. _Travis CI: https://travis-ci.org/
.. _GitLab CI: https://docs.gitlab.com/ee/ci/

View File

@ -49,8 +49,8 @@ Once the tests are complete, in order to see the code coverage, run the followin
Since this is a fresh install, and there are no tests built using the Python `unittest`_ library yet, you should get feedback that says there were no tests carried out. Since this is a fresh install, and there are no tests built using the Python `unittest`_ library yet, you should get feedback that says there were no tests carried out.
.. _Pytest: https://docs.pytest.org/en/latest/example/simple.html .. _Pytest: https://docs.pytest.org/en/latest/example/simple.html
.. _develop locally: ../developing-locally.rst .. _develop locally: ./developing-locally.html
.. _develop locally with docker: ..../developing-locally-docker.rst .. _develop locally with docker: ./developing-locally-docker.html
.. _customize: https://docs.pytest.org/en/latest/customize.html .. _customize: https://docs.pytest.org/en/latest/customize.html
.. _unittest: https://docs.python.org/3/library/unittest.html#module-unittest .. _unittest: https://docs.python.org/3/library/unittest.html#module-unittest
.. _configuring: https://coverage.readthedocs.io/en/v4.5.x/config.html .. _configuring: https://coverage.readthedocs.io/en/v4.5.x/config.html

View File

@ -70,7 +70,7 @@ def remove_heroku_files():
for file_name in file_names: for file_name in file_names:
if ( if (
file_name == "requirements.txt" file_name == "requirements.txt"
and "{{ cookiecutter.use_travisci }}".lower() == "y" and "{{ cookiecutter.ci_tool }}".lower() == "travis"
): ):
# don't remove the file if we are using travisci but not using heroku # don't remove the file if we are using travisci but not using heroku
continue continue
@ -105,6 +105,10 @@ def remove_dottravisyml_file():
os.remove(".travis.yml") os.remove(".travis.yml")
def remove_dotgitlabciyml_file():
os.remove(".gitlab-ci.yml")
def append_to_project_gitignore(path): def append_to_project_gitignore(path):
gitignore_file_path = ".gitignore" gitignore_file_path = ".gitignore"
with open(gitignore_file_path, "a") as gitignore_file: with open(gitignore_file_path, "a") as gitignore_file:
@ -349,9 +353,12 @@ def main():
if "{{ cookiecutter.use_docker }}".lower() == "y": if "{{ cookiecutter.use_docker }}".lower() == "y":
remove_celery_compose_dirs() remove_celery_compose_dirs()
if "{{ cookiecutter.use_travisci }}".lower() == "n": if "{{ cookiecutter.ci_tool }}".lower() != "travis":
remove_dottravisyml_file() remove_dottravisyml_file()
if "{{ cookiecutter.ci_tool }}".lower() != "gitlab":
remove_dotgitlabciyml_file()
print(SUCCESS + "Project initialized, keep up the good work!" + TERMINATOR) print(SUCCESS + "Project initialized, keep up the good work!" + TERMINATOR)

View File

@ -1,4 +1,4 @@
cookiecutter==1.6.0 cookiecutter==1.7.0
sh==1.12.14 sh==1.12.14
binaryornot==0.4.4 binaryornot==0.4.4
@ -9,9 +9,9 @@ flake8==3.7.9
# Testing # Testing
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
tox==3.14.2 tox==3.14.3
pytest==5.3.2 pytest==5.3.4
pytest_cases==1.12.0 pytest_cases==1.12.0
pytest-cookies==0.4.0 pytest-cookies==0.4.0
pytest-xdist==1.31.0 pytest-xdist==1.31.0
pyyaml==5.2 pyyaml==5.3

View File

@ -140,7 +140,7 @@ def test_black_passes(cookies, context_combination):
def test_travis_invokes_pytest(cookies, context): def test_travis_invokes_pytest(cookies, context):
context.update({"use_travisci": "y"}) context.update({"ci_tool": "Travis"})
result = cookies.bake(extra_context=context) result = cookies.bake(extra_context=context)
assert result.exit_code == 0 assert result.exit_code == 0
@ -155,6 +155,24 @@ def test_travis_invokes_pytest(cookies, context):
pytest.fail(e) pytest.fail(e)
def test_gitlab_invokes_flake8_and_pytest(cookies, context):
context.update({"ci_tool": "Gitlab"})
result = cookies.bake(extra_context=context)
assert result.exit_code == 0
assert result.exception is None
assert result.project.basename == context["project_slug"]
assert result.project.isdir()
with open(f"{result.project}/.gitlab-ci.yml", "r") as gitlab_yml:
try:
gitlab_config = yaml.load(gitlab_yml)
assert gitlab_config["flake8"]["script"] == ["flake8"]
assert gitlab_config["pytest"]["script"] == ["pytest"]
except yaml.YAMLError as e:
pytest.fail(e)
@pytest.mark.parametrize("slug", ["project slug", "Project_Slug"]) @pytest.mark.parametrize("slug", ["project slug", "Project_Slug"])
def test_invalid_slug(cookies, context, slug): def test_invalid_slug(cookies, context, slug):
"""Invalid slug should failed pre-generation hook.""" """Invalid slug should failed pre-generation hook."""

View File

@ -0,0 +1,33 @@
stages:
- lint
- test
variables:
POSTGRES_USER: '{{ cookiecutter.project_slug }}'
POSTGRES_PASSWORD: ''
POSTGRES_DB: 'test_{{ cookiecutter.project_slug }}'
flake8:
stage: lint
image: python:3.7-alpine
before_script:
- pip install -q flake8
script:
- flake8
pytest:
stage: test
image: python:3.7
tags:
- docker
services:
- postgres:11
variables:
DATABASE_URL: pgsql://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres/$POSTGRES_DB
before_script:
- pip install -r requirements/local.txt
script:
- pytest

View File

@ -1,19 +1,17 @@
FROM python:3.7-alpine FROM python:3.7-slim-buster
ENV PYTHONUNBUFFERED 1 ENV PYTHONUNBUFFERED 1
RUN apk update \ RUN apt-get update \
# dependencies for building Python packages
&& apt-get install -y build-essential \
# psycopg2 dependencies # psycopg2 dependencies
&& apk add --virtual build-deps gcc python3-dev musl-dev \ && apt-get install -y libpq-dev \
&& apk add postgresql-dev \
# Pillow dependencies
&& apk add jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev \
# CFFI dependencies
&& apk add libffi-dev py-cffi \
# Translations dependencies # Translations dependencies
&& apk add gettext \ && apt-get install -y gettext \
# https://docs.djangoproject.com/en/dev/ref/django-admin/#dbshell # cleaning up unused files
&& apk add postgresql-client && 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. # Requirements are installed here to ensure they will be cached.
COPY ./requirements /requirements COPY ./requirements /requirements

View File

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
set -o errexit set -o errexit
set -o nounset set -o nounset

View File

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
set -o errexit set -o errexit
set -o nounset set -o nounset

View File

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
set -o errexit set -o errexit
set -o nounset set -o nounset

View File

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
set -o errexit set -o errexit
set -o pipefail set -o pipefail

View File

@ -9,21 +9,23 @@ RUN npm run build
# Python build stage # Python build stage
{%- endif %} {%- endif %}
FROM python:3.7-alpine FROM python:3.7-slim-buster
ENV PYTHONUNBUFFERED 1 ENV PYTHONUNBUFFERED 1
RUN apk update \ RUN apt-get update \
# dependencies for building Python packages
&& apt-get install -y build-essential \
# psycopg2 dependencies # psycopg2 dependencies
&& apk add --virtual build-deps gcc python3-dev musl-dev \ && apt-get install -y libpq-dev \
&& apk add postgresql-dev \ # Translations dependencies
# Pillow dependencies && apt-get install -y gettext \
&& apk add jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev \ # cleaning up unused files
# CFFI dependencies && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
&& apk add libffi-dev py-cffi && rm -rf /var/lib/apt/lists/*
RUN addgroup -S django \ RUN addgroup --system django \
&& adduser -S -G django django && adduser --system --ingroup django django
# Requirements are installed here to ensure they will be cached. # Requirements are installed here to ensure they will be cached.
COPY ./requirements /requirements COPY ./requirements /requirements
@ -57,13 +59,11 @@ RUN chmod +x /start-flower
{%- endif %} {%- endif %}
{%- if cookiecutter.js_task_runner == 'Gulp' %} {%- if cookiecutter.js_task_runner == 'Gulp' %}
COPY --from=client-builder /app /app COPY --from=client-builder --chown=django:django /app /app
{% else %} {% else %}
COPY . /app COPY --chown=django:django . /app
{%- endif %} {%- endif %}
RUN chown -R django /app
USER django USER django
WORKDIR /app WORKDIR /app

View File

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
set -o errexit set -o errexit
set -o pipefail set -o pipefail

View File

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
set -o errexit set -o errexit
set -o nounset set -o nounset

View File

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
set -o errexit set -o errexit
set -o pipefail set -o pipefail

View File

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
set -o errexit set -o errexit
set -o pipefail set -o pipefail

View File

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
set -o errexit set -o errexit
set -o pipefail set -o pipefail

View File

@ -1,5 +1,5 @@
FROM traefik:1.7-alpine FROM traefik:v2.0
RUN mkdir -p /etc/traefik/acme RUN mkdir -p /etc/traefik/acme
RUN touch /etc/traefik/acme/acme.json RUN touch /etc/traefik/acme/acme.json
RUN chmod 600 /etc/traefik/acme/acme.json RUN chmod 600 /etc/traefik/acme/acme.json
COPY ./compose/production/traefik/traefik.toml /etc/traefik COPY ./compose/production/traefik/traefik.yml /etc/traefik

View File

@ -1,41 +0,0 @@
logLevel = "INFO"
defaultEntryPoints = ["http", "https"]
# Entrypoints, http and https
[entryPoints]
# http should be redirected to https
[entryPoints.http]
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https"
# https is the default
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
# Enable ACME (Let's Encrypt): automatic SSL
[acme]
# Email address used for registration
email = "{{ cookiecutter.email }}"
storage = "/etc/traefik/acme/acme.json"
entryPoint = "https"
onDemand = false
OnHostRule = true
# Use a HTTP-01 acme challenge rather than TLS-SNI-01 challenge
[acme.httpChallenge]
entryPoint = "http"
[file]
[backends]
[backends.django]
[backends.django.servers.server1]
url = "http://django:5000"
[frontends]
[frontends.django]
backend = "django"
passHostHeader = true
[frontends.django.headers]
HostsProxyHeaders = ['X-CSRFToken']
[frontends.django.routes.dr1]
rule = "Host:{{ cookiecutter.domain_name }}"

View File

@ -0,0 +1,67 @@
log:
level: INFO
entryPoints:
web:
# http
address: ":80"
web-secure:
# https
address: ":443"
certificatesResolvers:
letsencrypt:
# https://docs.traefik.io/master/https/acme/#lets-encrypt
acme:
email: "{{ cookiecutter.email }}"
storage: /etc/traefik/acme/acme.json
# https://docs.traefik.io/master/https/acme/#httpchallenge
httpChallenge:
entryPoint: web
http:
routers:
web-router:
rule: "Host(`{{ cookiecutter.domain_name }}`)"
entryPoints:
- web
middlewares:
- redirect
- csrf
service: django
web-secure-router:
rule: "Host(`{{ cookiecutter.domain_name }}`)"
entryPoints:
- web-secure
middlewares:
- csrf
service: django
tls:
# https://docs.traefik.io/master/routing/routers/#certresolver
certResolver: letsencrypt
middlewares:
redirect:
# https://docs.traefik.io/master/middlewares/redirectscheme/
redirectScheme:
scheme: https
permanent: true
csrf:
# https://docs.traefik.io/master/middlewares/headers/#hostsproxyheaders
# https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
headers:
hostsProxyHeaders: ['X-CSRFToken']
services:
django:
loadBalancer:
servers:
- url: http://django:5000
providers:
# https://docs.traefik.io/master/providers/file/
file:
filename: /etc/traefik/traefik.yml
watch: true

View File

@ -140,6 +140,7 @@ MIDDLEWARE = [
"django.middleware.csrf.CsrfViewMiddleware", "django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware", "django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.common.BrokenLinkEmailsMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware",
] ]

View File

@ -154,7 +154,7 @@ MEDIA_URL = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/media/"
# TEMPLATES # TEMPLATES
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#templates # https://docs.djangoproject.com/en/dev/ref/settings/#templates
TEMPLATES[0]["OPTIONS"]["loaders"] = [ # noqa F405 TEMPLATES[-1]["OPTIONS"]["loaders"] = [ # type: ignore[index] # noqa F405
( (
"django.template.loaders.cached.Loader", "django.template.loaders.cached.Loader",
[ [

View File

@ -32,7 +32,7 @@ PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]
# TEMPLATES # TEMPLATES
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
TEMPLATES[0]["OPTIONS"]["loaders"] = [ # noqa F405 TEMPLATES[-1]["OPTIONS"]["loaders"] = [ # type: ignore[index] # noqa F405
( (
"django.template.loaders.cached.Loader", "django.template.loaders.cached.Loader",
[ [

View File

@ -1,6 +1,6 @@
pytz==2019.3 # https://github.com/stub42/pytz pytz==2019.3 # https://github.com/stub42/pytz
python-slugify==4.0.0 # https://github.com/un33k/python-slugify python-slugify==4.0.0 # https://github.com/un33k/python-slugify
Pillow==6.2.1 # https://github.com/python-pillow/Pillow Pillow==7.0.0 # https://github.com/python-pillow/Pillow
{%- if cookiecutter.use_compressor == "y" %} {%- if cookiecutter.use_compressor == "y" %}
rcssmin==1.0.6{% if cookiecutter.windows == 'y' and cookiecutter.use_docker == 'n' %} --install-option="--without-c-extensions"{% endif %} # https://github.com/ndparker/rcssmin rcssmin==1.0.6{% if cookiecutter.windows == 'y' and cookiecutter.use_docker == 'n' %} --install-option="--without-c-extensions"{% endif %} # https://github.com/ndparker/rcssmin
{%- endif %} {%- endif %}
@ -25,7 +25,7 @@ django-model-utils==4.0.0 # https://github.com/jazzband/django-model-utils
django-allauth==0.41.0 # https://github.com/pennersr/django-allauth django-allauth==0.41.0 # https://github.com/pennersr/django-allauth
django-crispy-forms==1.8.1 # https://github.com/django-crispy-forms/django-crispy-forms django-crispy-forms==1.8.1 # https://github.com/django-crispy-forms/django-crispy-forms
{%- if cookiecutter.use_compressor == "y" %} {%- if cookiecutter.use_compressor == "y" %}
django-compressor==2.3 # https://github.com/django-compressor/django-compressor django-compressor==2.4 # https://github.com/django-compressor/django-compressor
{%- endif %} {%- endif %}
django-redis==4.11.0 # https://github.com/niwinz/django-redis django-redis==4.11.0 # https://github.com/niwinz/django-redis

View File

@ -2,9 +2,9 @@
Werkzeug==0.16.0 # https://github.com/pallets/werkzeug Werkzeug==0.16.0 # https://github.com/pallets/werkzeug
ipdb==0.12.3 # https://github.com/gotcha/ipdb ipdb==0.12.3 # https://github.com/gotcha/ipdb
Sphinx==2.3.0 # https://github.com/sphinx-doc/sphinx Sphinx==2.3.1 # https://github.com/sphinx-doc/sphinx
{%- if cookiecutter.use_docker == 'y' %} {%- if cookiecutter.use_docker == 'y' %}
psycopg2==2.8.3 --no-binary psycopg2 # https://github.com/psycopg/psycopg2 psycopg2==2.8.4 --no-binary psycopg2 # https://github.com/psycopg/psycopg2
{%- else %} {%- else %}
psycopg2-binary==2.8.4 # https://github.com/psycopg/psycopg2 psycopg2-binary==2.8.4 # https://github.com/psycopg/psycopg2
{%- endif %} {%- endif %}
@ -12,25 +12,26 @@ psycopg2-binary==2.8.4 # https://github.com/psycopg/psycopg2
# Testing # Testing
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
mypy==0.761 # https://github.com/python/mypy mypy==0.761 # https://github.com/python/mypy
pytest==5.3.1 # https://github.com/pytest-dev/pytest django-stubs==1.4.0 # https://github.com/typeddjango/django-stubs
pytest==5.3.4 # https://github.com/pytest-dev/pytest
pytest-sugar==0.9.2 # https://github.com/Frozenball/pytest-sugar pytest-sugar==0.9.2 # https://github.com/Frozenball/pytest-sugar
# Code quality # Code quality
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
flake8==3.7.9 # https://github.com/PyCQA/flake8 flake8==3.7.9 # https://github.com/PyCQA/flake8
coverage==5.0 # https://github.com/nedbat/coveragepy coverage==5.0.3 # https://github.com/nedbat/coveragepy
black==19.10b0 # https://github.com/ambv/black black==19.10b0 # https://github.com/ambv/black
pylint-django==2.0.13 # https://github.com/PyCQA/pylint-django pylint-django==2.0.13 # https://github.com/PyCQA/pylint-django
{%- if cookiecutter.use_celery == 'y' %} {%- if cookiecutter.use_celery == 'y' %}
pylint-celery==0.3 # https://github.com/PyCQA/pylint-celery pylint-celery==0.3 # https://github.com/PyCQA/pylint-celery
{%- endif %} {%- endif %}
pre-commit==1.20.0 # https://github.com/pre-commit/pre-commit pre-commit==1.21.0 # https://github.com/pre-commit/pre-commit
# Django # Django
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
factory-boy==2.12.0 # https://github.com/FactoryBoy/factory_boy factory-boy==2.12.0 # https://github.com/FactoryBoy/factory_boy
django-debug-toolbar==2.1 # https://github.com/jazzband/django-debug-toolbar django-debug-toolbar==2.1 # https://github.com/jazzband/django-debug-toolbar
django-extensions==2.2.5 # https://github.com/django-extensions/django-extensions django-extensions==2.2.6 # https://github.com/django-extensions/django-extensions
django-coverage-plugin==1.6.0 # https://github.com/nedbat/django_coverage_plugin django-coverage-plugin==1.7.0 # https://github.com/nedbat/django_coverage_plugin
pytest-django==3.7.0 # https://github.com/pytest-dev/pytest-django pytest-django==3.8.0 # https://github.com/pytest-dev/pytest-django

View File

@ -3,12 +3,12 @@
-r ./base.txt -r ./base.txt
gunicorn==20.0.4 # https://github.com/benoitc/gunicorn gunicorn==20.0.4 # https://github.com/benoitc/gunicorn
psycopg2==2.8.3 --no-binary psycopg2 # https://github.com/psycopg/psycopg2 psycopg2==2.8.4 --no-binary psycopg2 # https://github.com/psycopg/psycopg2
{%- if cookiecutter.use_whitenoise == 'n' %} {%- if cookiecutter.use_whitenoise == 'n' %}
Collectfast==1.3.1 # https://github.com/antonagestam/collectfast Collectfast==1.3.1 # https://github.com/antonagestam/collectfast
{%- endif %} {%- endif %}
{%- if cookiecutter.use_sentry == "y" %} {%- if cookiecutter.use_sentry == "y" %}
sentry-sdk==0.13.5 # https://github.com/getsentry/sentry-python sentry-sdk==0.14.1 # https://github.com/getsentry/sentry-python
{%- endif %} {%- endif %}
# Django # Django

View File

@ -1 +1 @@
python-3.7.4 python-3.7.6

View File

@ -13,6 +13,7 @@ ignore_missing_imports = True
warn_unused_ignores = True warn_unused_ignores = True
warn_redundant_casts = True warn_redundant_casts = True
warn_unused_configs = True warn_unused_configs = True
plugins = mypy_django_plugin.main
[mypy.plugins.django-stubs] [mypy.plugins.django-stubs]
django_settings_module = config.settings.test django_settings_module = config.settings.test

View File

@ -0,0 +1,23 @@
##basic build dependencies of various Django apps for Debian Jessie 10.x
#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
libjpeg62-turbo-dev
libfreetype6-dev
liblcms2-dev
libwebp-dev
##django-extensions
libgraphviz-dev

View File

@ -1,7 +1,7 @@
import pytest import pytest
from django.conf import settings
from django.test import RequestFactory from django.test import RequestFactory
from {{ cookiecutter.project_slug }}.users.models import User
from {{ cookiecutter.project_slug }}.users.tests.factories import UserFactory from {{ cookiecutter.project_slug }}.users.tests.factories import UserFactory
@ -11,7 +11,7 @@ def media_storage(settings, tmpdir):
@pytest.fixture @pytest.fixture
def user() -> settings.AUTH_USER_MODEL: def user() -> User:
return UserFactory() return UserFactory()

View File

@ -12,7 +12,10 @@ class UserFactory(DjangoModelFactory):
@post_generation @post_generation
def password(self, create: bool, extracted: Sequence[Any], **kwargs): def password(self, create: bool, extracted: Sequence[Any], **kwargs):
password = Faker( password = (
extracted
if extracted
else Faker(
"password", "password",
length=42, length=42,
special_chars=True, special_chars=True,
@ -20,6 +23,7 @@ class UserFactory(DjangoModelFactory):
upper_case=True, upper_case=True,
lower_case=True, lower_case=True,
).generate(extra_kwargs={}) ).generate(extra_kwargs={})
)
self.set_password(password) self.set_password(password)
class Meta: class Meta:

View File

@ -1,8 +1,9 @@
import pytest import pytest
from django.conf import settings
from {{ cookiecutter.project_slug }}.users.models import User
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db
def test_user_get_absolute_url(user: settings.AUTH_USER_MODEL): def test_user_get_absolute_url(user: User):
assert user.get_absolute_url() == f"/users/{user.username}/" assert user.get_absolute_url() == f"/users/{user.username}/"

View File

@ -1,11 +1,12 @@
import pytest import pytest
from django.conf import settings
from django.urls import reverse, resolve from django.urls import reverse, resolve
from {{ cookiecutter.project_slug }}.users.models import User
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db
def test_detail(user: settings.AUTH_USER_MODEL): def test_detail(user: User):
assert ( assert (
reverse("users:detail", kwargs={"username": user.username}) reverse("users:detail", kwargs={"username": user.username})
== f"/users/{user.username}/" == f"/users/{user.username}/"

View File

@ -1,7 +1,7 @@
import pytest import pytest
from django.conf import settings
from django.test import RequestFactory from django.test import RequestFactory
from {{ cookiecutter.project_slug }}.users.models import User
from {{ cookiecutter.project_slug }}.users.views import UserRedirectView, UserUpdateView from {{ cookiecutter.project_slug }}.users.views import UserRedirectView, UserUpdateView
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db
@ -16,9 +16,7 @@ class TestUserUpdateView:
https://github.com/pytest-dev/pytest-django/pull/258 https://github.com/pytest-dev/pytest-django/pull/258
""" """
def test_get_success_url( def test_get_success_url(self, user: User, request_factory: RequestFactory):
self, user: settings.AUTH_USER_MODEL, request_factory: RequestFactory
):
view = UserUpdateView() view = UserUpdateView()
request = request_factory.get("/fake-url/") request = request_factory.get("/fake-url/")
request.user = user request.user = user
@ -27,9 +25,7 @@ class TestUserUpdateView:
assert view.get_success_url() == f"/users/{user.username}/" assert view.get_success_url() == f"/users/{user.username}/"
def test_get_object( def test_get_object(self, user: User, request_factory: RequestFactory):
self, user: settings.AUTH_USER_MODEL, request_factory: RequestFactory
):
view = UserUpdateView() view = UserUpdateView()
request = request_factory.get("/fake-url/") request = request_factory.get("/fake-url/")
request.user = user request.user = user
@ -40,9 +36,7 @@ class TestUserUpdateView:
class TestUserRedirectView: class TestUserRedirectView:
def test_get_redirect_url( def test_get_redirect_url(self, user: User, request_factory: RequestFactory):
self, user: settings.AUTH_USER_MODEL, request_factory: RequestFactory
):
view = UserRedirectView() view = UserRedirectView()
request = request_factory.get("/fake-url") request = request_factory.get("/fake-url")
request.user = user request.user = user