Merge branch 'master' into upgrade/django-2.2

This commit is contained in:
Bruno Alla 2019-05-27 17:17:43 +01:00 committed by GitHub
commit 15f283bb74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 92 additions and 83 deletions

View File

@ -2,6 +2,13 @@
All enhancements and patches to Cookiecutter Django will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [2019-05-18]
### Removed
- Remove the user list view (@browniebroke)
### Fixed
- Static storage default ACL (@browniebroke)
## [2019-05-17]
### Fixed
- Added `LocaleMiddleware` to the list of middlewares (@tanoabeleyra)

View File

@ -7,19 +7,19 @@ Core Developers
These contributors have commit flags for the repository,
and are able to accept and merge pull requests.
=========================== ================ ===========
Name Github Twitter
=========================== ================ ===========
Daniel Roy Greenfeld `@pydanny`_ @pydanny
Audrey Roy Greenfeld* `@audreyr`_ @audreyr
Fábio C. Barrionuevo da Luz `@luzfcb`_ @luzfcb
Saurabh Kumar `@theskumar`_ @_theskumar
=========================== ================= ===========
Name Github Twitter
=========================== ================= ===========
Daniel Roy Greenfeld `@pydanny`_ @pydanny
Audrey Roy Greenfeld* `@audreyr`_ @audreyr
Fábio C. Barrionuevo da Luz `@luzfcb`_ @luzfcb
Saurabh Kumar `@theskumar`_ @_theskumar
Jannis Gebauer `@jayfk`_
Burhan Khalid `@burhan`_ @burhan
Nikita Shupeyko `@webyneter`_ @webyneter
Bruno Alla               `@browniebroke`_ @_BrunoAlla
Wan Liuyang `@sfdye`_ @sfdye
=========================== ================ ===========
Burhan Khalid `@burhan`_ @burhan
Nikita Shupeyko `@webyneter`_ @webyneter
Bruno Alla               `@browniebroke`_ @_BrunoAlla
Wan Liuyang `@sfdye`_ @sfdye
=========================== ================= ===========
*Audrey is also the creator of Cookiecutter. Audrey and
Daniel are on the Cookiecutter core team.*
@ -87,6 +87,7 @@ Listed in alphabetical order.
Craig Margieson `@cmargieson`_
Cristian Vargas `@cdvv7788`_
Cullen Rhodes `@c-rhodes`_
Curtis St Pierre `@curtisstpierre`_ @cstpierre1388
Dan Shultz `@shultz`_
Daniel Hepper `@dhepper`_ @danielhepper
Daniele Tricoli `@eriol`_
@ -143,6 +144,7 @@ Listed in alphabetical order.
Mateusz Ostaszewski `@mostaszewski`_
Mathijs Hoogland `@MathijsHoogland`_
Matt Braymer-Hayes `@mattayes`_ @mattayes
Matt Knapper `@mknapper1`_
Matt Linares
Matt Menzenski `@menzenski`_
Matt Warren `@mfwarren`_
@ -221,6 +223,7 @@ Listed in alphabetical order.
.. _@chuckus: https://github.com/chuckus
.. _@cmackenzie1: https://github.com/cmackenzie1
.. _@Collederas: https://github.com/Collederas
.. _@curtisstpierre: https://github.com/curtisstpierre
.. _@davitovmasyan: https://github.com/davitovmasyan
.. _@ddiazpinto: https://github.com/ddiazpinto
.. _@demestav: https://github.com/demestav
@ -260,6 +263,7 @@ Listed in alphabetical order.
.. _@msaizar: https://github.com/msaizar
.. _@MathijsHoogland: https://github.com/MathijsHoogland
.. _@mattayes: https://github.com/mattayes
.. _@mknapper1: https://github.com/mknapper1
.. _@menzenski: https://github.com/menzenski
.. _@mostaszewski: https://github.com/mostaszewski
.. _@mfwarren: https://github.com/mfwarren

View File

@ -89,7 +89,7 @@ Constraints
-----------
* Only maintained 3rd party libraries are used.
* Uses PostgreSQL everywhere (9.4 - 10.5)
* Uses PostgreSQL everywhere (9.4 - 11.3)
* Environment variables for configuration (This won't work with Apache/mod_wsgi).
Support this Project!
@ -169,15 +169,12 @@ Answer the prompts with your own desired options_. For example::
use_heroku [n]: y
use_compressor [n]: y
Select postgresql_version:
1 - 10.5
2 - 10.4
3 - 10.3
4 - 10.2
5 - 10.1
6 - 9.6
7 - 9.5
8 - 9.4
Choose from 1, 2, 3, 4, 5, 6, 7, 8 [1]: 1
1 - 11.3
2 - 10.8
3 - 9.6
4 - 9.5
5 - 9.4
Choose from 1, 2, 3, 4, 5 [1]: 1
Select js_task_runner:
1 - None
2 - Gulp
@ -185,7 +182,8 @@ Answer the prompts with your own desired options_. For example::
Select cloud_provider:
1 - AWS
2 - GCS
Choose from 1, 2 [1]: 1
3 - None
Choose from 1, 2, 3 [1]: 1
custom_bootstrap_compilation [n]: n
Select open_source_license:
1 - MIT

View File

@ -18,11 +18,8 @@
"use_pycharm": "n",
"use_docker": "n",
"postgresql_version": [
"10.5",
"10.4",
"10.3",
"10.2",
"10.1",
"11.3",
"10.8",
"9.6",
"9.5",
"9.4"
@ -33,7 +30,8 @@
],
"cloud_provider": [
"AWS",
"GCE"
"GCE",
"None"
],
"custom_bootstrap_compilation": "n",
"use_compressor": "n",

View File

@ -35,7 +35,15 @@ Configuring the Stack
The majority of services above are configured through the use of environment variables. Just check out :ref:`envs` and you will know the drill.
To obtain logs and information about crashes in a production setup, make sure that you have access to an external Sentry instance (e.g. by creating an account with `sentry.io`_), and set the ``SENTRY_DSN`` variable.
To obtain logs and information about crashes in a production setup, make sure that you have access to an external Sentry instance (e.g. by creating an account with `sentry.io`_), and set the ``SENTRY_DSN`` variable. Logs of level `logging.ERROR` are sent as Sentry events. Therefore, in order to send a Sentry event use:
.. code-block:: python
import logging
logging.error("This event is sent to Sentry", extra={"<example_key>": "<example_value>"})
The `extra` parameter allows you to send additional information about the context of this error.
You will probably also need to setup the Mail backend, for example by adding a `Mailgun`_ API key and a `Mailgun`_ sender domain, otherwise, the account creation view will crash and result in a 500 error when the backend attempts to send an email to the account owner.

View File

@ -49,14 +49,11 @@ use_docker:
postgresql_version:
Select a PostgreSQL_ version to use. The choices are:
1. 10.5
2. 10.4
3. 10.3
4. 10.2
5. 10.1
6. 9.6
7. 9.5
8. 9.4
1. 11.3
2. 10.8
3. 9.6
4. 9.5
5. 9.4
js_task_runner:
Select a JavaScript task runner. The choices are:
@ -69,6 +66,7 @@ cloud_provider:
1. AWS_
2. GCS_
3. None
custom_bootstrap_compilation:
Indicates whether the project should support Bootstrap recompilation
@ -100,7 +98,7 @@ use_travisci:
keep_local_envs_in_vcs:
Indicates whether the project's ``.envs/.local/`` should be kept in VCS
(comes in handy when working in teams where local environment reproducibility
is strongly encouraged).
is strongly encouraged).
Note: .env(s) are only utilized when Docker Compose and/or Heroku support is enabled.
debug:

View File

@ -328,6 +328,12 @@ def main():
if "{{ cookiecutter.use_docker }}".lower() == "y":
remove_node_dockerfile()
if "{{ cookiecutter.cloud_provider}}".lower() == "none":
print(
WARNING + "You chose not to use a cloud provider, "
"media files won't be served in production." + TERMINATOR
)
if "{{ cookiecutter.use_celery }}".lower() == "n":
remove_celery_files()
if "{{ cookiecutter.use_docker }}".lower() == "y":

View File

@ -1,3 +1,6 @@
[pytest]
python_paths = .
norecursedirs = .tox .git */migrations/* */static/* docs venv */{{cookiecutter.project_slug}}/*
markers =
flake8: Run flake8 on all possible template combinations
black: Run black on all possible template combinations

View File

@ -5,11 +5,11 @@ binaryornot==0.4.4
# Code quality
# ------------------------------------------------------------------------------
black==19.3b0
flake8==3.7.6
flake8==3.7.7
# Testing
# ------------------------------------------------------------------------------
tox==3.11.1
tox==3.12.1
pytest==4.5.0
pytest_cases==1.6.2
pytest-cookies==0.3.0

View File

@ -7,11 +7,11 @@ import sh
import yaml
from binaryornot.check import is_binary
PATTERN = "{{(\s?cookiecutter)[.](.*?)}}"
PATTERN = r"{{(\s?cookiecutter)[.](.*?)}}"
RE_OBJ = re.compile(PATTERN)
YN_CHOICES = ["y", "n"]
CLOUD_CHOICES = ["AWS", "GCE"]
CLOUD_CHOICES = ["AWS", "GCE", "None"]
@pytest.fixture

View File

@ -17,7 +17,7 @@ defaultEntryPoints = ["http", "https"]
[acme]
# Email address used for registration
email = "{{ cookiecutter.email }}"
storageFile = "/etc/traefik/acme/acme.json"
storage = "/etc/traefik/acme/acme.json"
entryPoint = "https"
onDemand = false
OnHostRule = true

View File

@ -266,10 +266,10 @@ CELERY_TASK_SERIALIZER = "json"
CELERY_RESULT_SERIALIZER = "json"
# http://docs.celeryproject.org/en/latest/userguide/configuration.html#task-time-limit
# TODO: set to whatever value is adequate in your circumstances
CELERYD_TASK_TIME_LIMIT = 5 * 60
CELERY_TASK_TIME_LIMIT = 5 * 60
# http://docs.celeryproject.org/en/latest/userguide/configuration.html#task-soft-time-limit
# TODO: set to whatever value is adequate in your circumstances
CELERYD_TASK_SOFT_TIME_LIMIT = 60
CELERY_TASK_SOFT_TIME_LIMIT = 60
{%- endif %}
# django-allauth

View File

@ -66,10 +66,12 @@ SECURE_CONTENT_TYPE_NOSNIFF = env.bool(
"DJANGO_SECURE_CONTENT_TYPE_NOSNIFF", default=True
)
{% if cookiecutter.cloud_provider != 'None' -%}
# STORAGES
# ------------------------------------------------------------------------------
# https://django-storages.readthedocs.io/en/latest/#installation
INSTALLED_APPS += ["storages"] # noqa F405
{%- endif -%}
{% if cookiecutter.cloud_provider == 'AWS' %}
# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings
AWS_ACCESS_KEY_ID = env("DJANGO_AWS_ACCESS_KEY_ID")
@ -93,19 +95,20 @@ AWS_S3_REGION_NAME = env("DJANGO_AWS_S3_REGION_NAME", default=None)
DEFAULT_FILE_STORAGE = "storages.backends.gcloud.GoogleCloudStorage"
GS_BUCKET_NAME = env("DJANGO_GCE_STORAGE_BUCKET_NAME")
GS_DEFAULT_ACL = "publicRead"
{% endif %}
{% endif -%}
{% if cookiecutter.cloud_provider != 'None' or cookiecutter.use_whitenoise == 'y' -%}
# STATIC
# ------------------------
{% endif -%}
{% if cookiecutter.use_whitenoise == 'y' -%}
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
{%- endif -%}
{%- if cookiecutter.cloud_provider == 'AWS' %}
{% elif cookiecutter.cloud_provider == 'AWS' -%}
STATICFILES_STORAGE = "config.settings.production.StaticRootS3Boto3Storage"
STATIC_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/static/"
{%- elif cookiecutter.cloud_provider == 'GCE' %}
STATIC_URL = "https://storage.googleapis.com/{}/static/".format(GS_BUCKET_NAME)
{%- endif %}
{% elif cookiecutter.cloud_provider == 'GCE' -%}
STATIC_URL = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/static/"
{% endif -%}
# MEDIA
# ------------------------------------------------------------------------------
@ -117,6 +120,7 @@ from storages.backends.s3boto3 import S3Boto3Storage # noqa E402
class StaticRootS3Boto3Storage(S3Boto3Storage):
location = "static"
default_acl = "public-read"
class MediaRootS3Boto3Storage(S3Boto3Storage):
@ -128,8 +132,8 @@ class MediaRootS3Boto3Storage(S3Boto3Storage):
DEFAULT_FILE_STORAGE = "config.settings.production.MediaRootS3Boto3Storage"
MEDIA_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/media/"
{%- elif cookiecutter.cloud_provider == 'GCE' %}
MEDIA_URL = "https://storage.googleapis.com/{}/media/".format(GS_BUCKET_NAME)
MEDIA_ROOT = "https://storage.googleapis.com/{}/media/".format(GS_BUCKET_NAME)
MEDIA_URL = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/media/"
MEDIA_ROOT = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/media/"
{%- endif %}
# TEMPLATES
@ -193,7 +197,7 @@ COMPRESS_ENABLED = env.bool("COMPRESS_ENABLED", default=True)
# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_STORAGE
COMPRESS_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_URL
COMPRESS_URL = STATIC_URL{% if cookiecutter.use_whitenoise == 'y' %} # noqa F405{% endif %}
COMPRESS_URL = STATIC_URL{% if cookiecutter.use_whitenoise == 'y' or cookiecutter.cloud_provider == 'None' %} # noqa F405{% endif %}
{% endif %}
{%- if cookiecutter.use_whitenoise == 'n' -%}
# Collectfast
@ -288,7 +292,7 @@ SENTRY_LOG_LEVEL = env.int("DJANGO_SENTRY_LOG_LEVEL", logging.INFO)
sentry_logging = LoggingIntegration(
level=SENTRY_LOG_LEVEL, # Capture info and above as breadcrumbs
event_level=None, # Send no events from log messages
event_level=logging.ERROR, # Send errors as events
)
{%- if cookiecutter.use_celery == 'y' %}

View File

@ -45,7 +45,7 @@ services:
{%- if cookiecutter.use_celery == 'y' %}
redis:
image: redis:3.2
image: redis:5.0
celeryworker:
<<: *django

View File

@ -44,7 +44,7 @@ services:
- "0.0.0.0:443:443"
redis:
image: redis:3.2
image: redis:5.0
{%- if cookiecutter.use_celery == 'y' %}
celeryworker:

View File

@ -1,10 +1,10 @@
-r ./base.txt
Werkzeug==0.15.4 # https://github.com/pallets/werkzeug
Werkzeug==0.14.1 # pyup: < 0.15 # https://github.com/pallets/werkzeug
ipdb==0.12 # https://github.com/gotcha/ipdb
Sphinx==2.0.1 # https://github.com/sphinx-doc/sphinx
{%- if cookiecutter.use_docker == 'y' %}
psycopg2==2.8 --no-binary psycopg2 # https://github.com/psycopg/psycopg2
psycopg2==2.8.2 --no-binary psycopg2 # https://github.com/psycopg/psycopg2
{%- else %}
psycopg2-binary==2.8.2 # https://github.com/psycopg/psycopg2
{%- endif %}
@ -17,7 +17,7 @@ pytest-sugar==0.9.2 # https://github.com/Frozenball/pytest-sugar
# Code quality
# ------------------------------------------------------------------------------
flake8==3.7.5 # https://github.com/PyCQA/flake8
flake8==3.7.7 # https://github.com/PyCQA/flake8
coverage==4.5.3 # https://github.com/nedbat/coveragepy
black==19.3b0 # https://github.com/ambv/black
pylint-django==2.0.9 # https://github.com/PyCQA/pylint-django
@ -30,6 +30,6 @@ pylint-celery==0.3 # https://github.com/PyCQA/pylint-celery
factory-boy==2.12.0 # https://github.com/FactoryBoy/factory_boy
django-debug-toolbar==1.11 # https://github.com/jazzband/django-debug-toolbar
django-extensions==2.1.6 # https://github.com/django-extensions/django-extensions
django-extensions==2.1.7 # https://github.com/django-extensions/django-extensions
django-coverage-plugin==1.6.0 # https://github.com/nedbat/django_coverage_plugin
pytest-django==3.4.8 # https://github.com/pytest-dev/pytest-django

View File

@ -3,12 +3,12 @@
-r ./base.txt
gunicorn==19.9.0 # https://github.com/benoitc/gunicorn
psycopg2==2.8 --no-binary psycopg2 # https://github.com/psycopg/psycopg2
psycopg2==2.8.2 --no-binary psycopg2 # https://github.com/psycopg/psycopg2
{%- if cookiecutter.use_whitenoise == 'n' %}
Collectfast==0.6.2 # https://github.com/antonagestam/collectfast
{%- endif %}
{%- if cookiecutter.use_sentry == "y" %}
sentry-sdk==0.7.14 # https://github.com/getsentry/sentry-python
sentry-sdk==0.8.0 # https://github.com/getsentry/sentry-python
{%- endif %}
# Django
@ -18,4 +18,4 @@ django-storages[boto3]==1.7.1 # https://github.com/jschneier/django-storages
{%- elif cookiecutter.cloud_provider == 'GCE' %}
django-storages[google]==1.7.1 # https://github.com/jschneier/django-storages
{%- endif %}
django-anymail[mailgun]==6.0 # https://github.com/anymail/django-anymail
django-anymail[mailgun]==6.0.1 # https://github.com/anymail/django-anymail

View File

@ -13,11 +13,6 @@ def test_detail(user: settings.AUTH_USER_MODEL):
assert resolve(f"/users/{user.username}/").view_name == "users:detail"
def test_list():
assert reverse("users:list") == "/users/"
assert resolve("/users/").view_name == "users:list"
def test_update():
assert reverse("users:update") == "/users/~update/"
assert resolve("/users/~update/").view_name == "users:update"

View File

@ -1,7 +1,6 @@
from django.urls import path
from {{ cookiecutter.project_slug }}.users.views import (
user_list_view,
user_redirect_view,
user_update_view,
user_detail_view,
@ -9,7 +8,6 @@ from {{ cookiecutter.project_slug }}.users.views import (
app_name = "users"
urlpatterns = [
path("", view=user_list_view, name="list"),
path("~redirect/", view=user_redirect_view, name="redirect"),
path("~update/", view=user_update_view, name="update"),
path("<str:username>/", view=user_detail_view, name="detail"),

View File

@ -1,7 +1,7 @@
from django.contrib.auth import get_user_model
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse
from django.views.generic import DetailView, ListView, RedirectView, UpdateView
from django.views.generic import DetailView, RedirectView, UpdateView
User = get_user_model()
@ -16,16 +16,6 @@ class UserDetailView(LoginRequiredMixin, DetailView):
user_detail_view = UserDetailView.as_view()
class UserListView(LoginRequiredMixin, ListView):
model = User
slug_field = "username"
slug_url_kwarg = "username"
user_list_view = UserListView.as_view()
class UserUpdateView(LoginRequiredMixin, UpdateView):
model = User