mirror of
https://github.com/cookiecutter/cookiecutter-django.git
synced 2024-11-23 18:14:01 +03:00
Merge commit '416edce1f9d5d3b73ae40efeb440c70a8d053d0a'
This commit is contained in:
commit
ec48a217dc
10
.travis.yml
10
.travis.yml
|
@ -1,4 +1,4 @@
|
||||||
sudo: required
|
dist: xenial
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- docker
|
- docker
|
||||||
|
@ -13,10 +13,14 @@ before_install:
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- name: Tox Test
|
- name: Test results
|
||||||
script: tox -e py36
|
script: tox -e py36
|
||||||
- name: Black template
|
- name: Run flake8 on result
|
||||||
|
script: tox -e flake8
|
||||||
|
- name: Run black on result
|
||||||
script: tox -e black
|
script: tox -e black
|
||||||
|
- name: Black template
|
||||||
|
script: tox -e black-template
|
||||||
- name: Basic Docker
|
- name: Basic Docker
|
||||||
script: sh tests/test_docker.sh
|
script: sh tests/test_docker.sh
|
||||||
- name: Docker with Celery
|
- name: Docker with Celery
|
||||||
|
|
190
CHANGELOG.md
190
CHANGELOG.md
|
@ -2,6 +2,196 @@
|
||||||
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/).
|
||||||
|
|
||||||
|
## [2019-05-27]
|
||||||
|
### Changed
|
||||||
|
- Made cloud provider optional (@tanoabeleyra)
|
||||||
|
- Updated to Django 2.2.1 (@browniebroke)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Celery worker-related setting names (@browniebroke)
|
||||||
|
|
||||||
|
## [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)
|
||||||
|
- Added `LOCALE_PATH` to settings (@tanoabeleyra)
|
||||||
|
|
||||||
|
## [2019-05-16]
|
||||||
|
### Changed
|
||||||
|
- Users app to have a translated verbose name (@tanoabeleyra)
|
||||||
|
- Logging configuration for local (@browniebroke)
|
||||||
|
|
||||||
|
## [2019-05-08]
|
||||||
|
### Changed
|
||||||
|
- Upgraded to Django 2.1 (@browniebroke)
|
||||||
|
|
||||||
|
## [2019-04-07]
|
||||||
|
### Added
|
||||||
|
- Support for Google Cloud Storage (@ahhda)
|
||||||
|
|
||||||
|
## [2019-04-03]
|
||||||
|
### Added
|
||||||
|
- Command to backup Db to AWS S3 (@foarsitter)
|
||||||
|
|
||||||
|
## [2019-03-25]
|
||||||
|
### Added
|
||||||
|
- Node image to run Gulp with Docker (@browniebroke)
|
||||||
|
|
||||||
|
## [2019-03-19]
|
||||||
|
### Changed
|
||||||
|
- Replaced Caddy with Traefik (@demestav)
|
||||||
|
|
||||||
|
## [2019-03-11]
|
||||||
|
### Changed
|
||||||
|
- Sentry integration from Raven to Sentry-SDK (@gfabricio)
|
||||||
|
- Made Redis config conditional on Celery locally (@demestav)
|
||||||
|
|
||||||
|
## [2019-03-11]
|
||||||
|
### Added
|
||||||
|
- Automatic migrations on Heroku (@yunti)
|
||||||
|
|
||||||
|
## [2019-03-06]
|
||||||
|
### Fixed
|
||||||
|
- Missing script tag in Travis config (@btknu)
|
||||||
|
|
||||||
|
## [2019-03-02]
|
||||||
|
### Changed
|
||||||
|
- Celery eager setting in local setting with Docker (@keithjeb)
|
||||||
|
|
||||||
|
## [2019-03-01]
|
||||||
|
### Updated
|
||||||
|
- All NPM dependencies (@takkaria)
|
||||||
|
|
||||||
|
## [2018-11-13]
|
||||||
|
### Changed
|
||||||
|
- Security settings in Dev (@carlmjohnson)
|
||||||
|
|
||||||
|
## [2018-11-20]
|
||||||
|
### Fixed
|
||||||
|
- Passing the CSRF header from the reverse proxy to Django server for DRF (@hpbruna)
|
||||||
|
|
||||||
|
## [2018-11-12]
|
||||||
|
### Fixed
|
||||||
|
- Initialisation of Celery app (@glasslion)
|
||||||
|
|
||||||
|
## [2018-10-24]
|
||||||
|
### Fixed
|
||||||
|
- Persisting of iPython history between sessions (@davitovmasyan)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Postgres 10.5 option (@jleclanche)
|
||||||
|
|
||||||
|
## [2018-09-18]
|
||||||
|
### Added
|
||||||
|
- Included `mypy` in dependencies and run it in tests (@apirobot)
|
||||||
|
|
||||||
|
## [2018-09-18]
|
||||||
|
### Fixed
|
||||||
|
- Avoid `$` in environment variables to workaround a bug from django-environ (@browniebroke)
|
||||||
|
|
||||||
|
## [2018-09-16]
|
||||||
|
### Fixed
|
||||||
|
- Bug in ordering of Middleware for production config (@ChrisPappalardo)
|
||||||
|
|
||||||
|
## [2018-09-12]
|
||||||
|
### Fixed
|
||||||
|
- URLs for Static and Media for S3 buckets in regions other than N. Virginia (@umrashrf)
|
||||||
|
|
||||||
|
## [2018-09-09]
|
||||||
|
### Changed
|
||||||
|
- Name of static and media storage classes (@sfdye)
|
||||||
|
|
||||||
|
## [2018-09-01]
|
||||||
|
### Changed
|
||||||
|
- Make static and media storage fully-fledged classes (@erfaan)
|
||||||
|
|
||||||
|
## [2018-08-28]
|
||||||
|
### Fixed
|
||||||
|
- Running tests in docker test script (@apirobot)
|
||||||
|
|
||||||
|
## [2018-07-23]
|
||||||
|
### Changed
|
||||||
|
- Test commands to use pytest (@jcass77)
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Some hacks leftovers from Bootstrap v4 beta in `project.js` (@hendrikschneider)
|
||||||
|
|
||||||
|
## [2018-07-12]
|
||||||
|
### Changed
|
||||||
|
- Upgraded to Bootstrap 4.1.1 (@mostaszewski)
|
||||||
|
|
||||||
|
## [2018-06-25]
|
||||||
|
### Added
|
||||||
|
- Flower integration with Docker (@webyneter)
|
||||||
|
|
||||||
|
## [2018-06-25]
|
||||||
|
### Changed
|
||||||
|
- Rewrite user app test to use a pytest style (@webyneter)
|
||||||
|
|
||||||
|
## [2018-06-21]
|
||||||
|
### Added
|
||||||
|
- Extend & update Celery config (@webyneter & @apirobot)
|
||||||
|
|
||||||
|
## [2018-05-25]
|
||||||
|
### Fixed
|
||||||
|
- Build issues due to incompatibility between libressl & openssl (@SassanoM)
|
||||||
|
|
||||||
|
## [2018-05-21]
|
||||||
|
### Changed
|
||||||
|
- Updated Caddy to 0.11 and pin its version (@webyneter)
|
||||||
|
|
||||||
|
## [2018-05-14]
|
||||||
|
### Changed
|
||||||
|
- Replace `awesome-slugify` by `python-slugify` (@hongquan)
|
||||||
|
- Migrate to Django 2.0+ URL style (@saschalalala)
|
||||||
|
|
||||||
|
## [2018-05-05]
|
||||||
|
### Fixed
|
||||||
|
- Postgres backup & restore commands (@webyneter)
|
||||||
|
|
||||||
|
## [2018-04-10]
|
||||||
|
### Changed
|
||||||
|
- Simplify configuration (@danidee10)
|
||||||
|
|
||||||
|
## [2018-04-08]
|
||||||
|
### Added
|
||||||
|
- Adopt Black code style (@pydanny)
|
||||||
|
|
||||||
|
## [2018-03-27]
|
||||||
|
### Fixed
|
||||||
|
- Simplified extra Celery config generated when opted out (@webyneter)
|
||||||
|
|
||||||
|
## [2018-03-21]
|
||||||
|
### Removed
|
||||||
|
- Remove Opbeat support (@sfdye)
|
||||||
|
|
||||||
|
## [2018-03-16]
|
||||||
|
### Fixed
|
||||||
|
- Install `psycopg2-binary` when using Docker locally (@browniebroke)
|
||||||
|
|
||||||
|
## [2018-03-14]
|
||||||
|
### Fixed
|
||||||
|
- Fixed and improved Postgres backup & restore scripts (@webyneter)
|
||||||
|
|
||||||
|
## [2018-03-10]
|
||||||
|
### Changed
|
||||||
|
- Simplify Mailgun setting (@browniebroke)
|
||||||
|
|
||||||
|
## [2018-03-06]
|
||||||
|
### Changed
|
||||||
|
- Convert string formatting to f-strings (@sfdye)
|
||||||
|
|
||||||
|
## [2018-03-01]
|
||||||
|
### Changed
|
||||||
|
- Celery to use JSON serialization by default (@adammsteele)
|
||||||
|
- Use Docker version from Travis to run tests (@browniebroke)
|
||||||
|
|
||||||
## [2018-02-16]
|
## [2018-02-16]
|
||||||
### Changed
|
### Changed
|
||||||
- Upgraded to Django 2.0 (@epicwhale)
|
- Upgraded to Django 2.0 (@epicwhale)
|
||||||
|
|
|
@ -7,19 +7,19 @@ Core Developers
|
||||||
These contributors have commit flags for the repository,
|
These contributors have commit flags for the repository,
|
||||||
and are able to accept and merge pull requests.
|
and are able to accept and merge pull requests.
|
||||||
|
|
||||||
=========================== ================ ===========
|
=========================== ================= ===========
|
||||||
Name Github Twitter
|
Name Github Twitter
|
||||||
=========================== ================ ===========
|
=========================== ================= ===========
|
||||||
Daniel Roy Greenfeld `@pydanny`_ @pydanny
|
Daniel Roy Greenfeld `@pydanny`_ @pydanny
|
||||||
Audrey Roy Greenfeld* `@audreyr`_ @audreyr
|
Audrey Roy Greenfeld* `@audreyr`_ @audreyr
|
||||||
Fábio C. Barrionuevo da Luz `@luzfcb`_ @luzfcb
|
Fábio C. Barrionuevo da Luz `@luzfcb`_ @luzfcb
|
||||||
Saurabh Kumar `@theskumar`_ @_theskumar
|
Saurabh Kumar `@theskumar`_ @_theskumar
|
||||||
Jannis Gebauer `@jayfk`_
|
Jannis Gebauer `@jayfk`_
|
||||||
Burhan Khalid `@burhan`_ @burhan
|
Burhan Khalid `@burhan`_ @burhan
|
||||||
Nikita Shupeyko `@webyneter`_ @webyneter
|
Nikita Shupeyko `@webyneter`_ @webyneter
|
||||||
Bruno Alla `@browniebroke`_ @_BrunoAlla
|
Bruno Alla `@browniebroke`_ @_BrunoAlla
|
||||||
Wan Liuyang `@sfdye`_ @sfdye
|
Wan Liuyang `@sfdye`_ @sfdye
|
||||||
=========================== ================ ===========
|
=========================== ================= ===========
|
||||||
|
|
||||||
*Audrey is also the creator of Cookiecutter. Audrey and
|
*Audrey is also the creator of Cookiecutter. Audrey and
|
||||||
Daniel are on the Cookiecutter core team.*
|
Daniel are on the Cookiecutter core team.*
|
||||||
|
@ -64,6 +64,7 @@ Listed in alphabetical order.
|
||||||
Areski Belaid `@areski`_
|
Areski Belaid `@areski`_
|
||||||
Ashley Camba
|
Ashley Camba
|
||||||
Barclay Gauld `@yunti`_
|
Barclay Gauld `@yunti`_
|
||||||
|
Bartek `@btknu`_
|
||||||
Ben Warren `@bwarren2`_
|
Ben Warren `@bwarren2`_
|
||||||
Ben Lopatin
|
Ben Lopatin
|
||||||
Benjamin Abel
|
Benjamin Abel
|
||||||
|
@ -71,7 +72,6 @@ Listed in alphabetical order.
|
||||||
Bo Lopker `@blopker`_
|
Bo Lopker `@blopker`_
|
||||||
Bouke Haarsma
|
Bouke Haarsma
|
||||||
Brent Payne `@brentpayne`_ @brentpayne
|
Brent Payne `@brentpayne`_ @brentpayne
|
||||||
Bartek `@btknu`_
|
|
||||||
Burhan Khalid `@burhan`_ @burhan
|
Burhan Khalid `@burhan`_ @burhan
|
||||||
Carl Johnson `@carlmjohnson`_ @carlmjohnson
|
Carl Johnson `@carlmjohnson`_ @carlmjohnson
|
||||||
Catherine Devlin `@catherinedevlin`_
|
Catherine Devlin `@catherinedevlin`_
|
||||||
|
@ -87,6 +87,7 @@ Listed in alphabetical order.
|
||||||
Craig Margieson `@cmargieson`_
|
Craig Margieson `@cmargieson`_
|
||||||
Cristian Vargas `@cdvv7788`_
|
Cristian Vargas `@cdvv7788`_
|
||||||
Cullen Rhodes `@c-rhodes`_
|
Cullen Rhodes `@c-rhodes`_
|
||||||
|
Curtis St Pierre `@curtisstpierre`_ @cstpierre1388
|
||||||
Dan Shultz `@shultz`_
|
Dan Shultz `@shultz`_
|
||||||
Daniel Hepper `@dhepper`_ @danielhepper
|
Daniel Hepper `@dhepper`_ @danielhepper
|
||||||
Daniele Tricoli `@eriol`_
|
Daniele Tricoli `@eriol`_
|
||||||
|
@ -95,6 +96,7 @@ Listed in alphabetical order.
|
||||||
Davur Clementsen `@dsclementsen`_ @davur
|
Davur Clementsen `@dsclementsen`_ @davur
|
||||||
Delio Castillo `@jangeador`_ @jangeador
|
Delio Castillo `@jangeador`_ @jangeador
|
||||||
Demetris Stavrou `@demestav`_
|
Demetris Stavrou `@demestav`_
|
||||||
|
Denis Bobrov `@delneg`_
|
||||||
Denis Orehovsky `@apirobot`_
|
Denis Orehovsky `@apirobot`_
|
||||||
Dónal Adams `@epileptic-fish`_
|
Dónal Adams `@epileptic-fish`_
|
||||||
Diane Chen `@purplediane`_ @purplediane88
|
Diane Chen `@purplediane`_ @purplediane88
|
||||||
|
@ -114,8 +116,8 @@ Listed in alphabetical order.
|
||||||
Ian Lee `@IanLee1521`_
|
Ian Lee `@IanLee1521`_
|
||||||
Irfan Ahmad `@erfaan`_ @erfaan
|
Irfan Ahmad `@erfaan`_ @erfaan
|
||||||
Jan Van Bruggen `@jvanbrug`_
|
Jan Van Bruggen `@jvanbrug`_
|
||||||
Jens Nilsson `@phiberjenz`_
|
|
||||||
Jelmer Draaijer `@foarsitter`_
|
Jelmer Draaijer `@foarsitter`_
|
||||||
|
Jens Nilsson `@phiberjenz`_
|
||||||
Jerome Leclanche `@jleclanche`_ @Adys
|
Jerome Leclanche `@jleclanche`_ @Adys
|
||||||
Jimmy Gitonga `@afrowave`_ @afrowave
|
Jimmy Gitonga `@afrowave`_ @afrowave
|
||||||
John Cass `@jcass77`_ @cass_john
|
John Cass `@jcass77`_ @cass_john
|
||||||
|
@ -124,9 +126,10 @@ Listed in alphabetical order.
|
||||||
Kaido Kert `@kaidokert`_
|
Kaido Kert `@kaidokert`_
|
||||||
kappataumu `@kappataumu`_ @kappataumu
|
kappataumu `@kappataumu`_ @kappataumu
|
||||||
Kaveh `@ka7eh`_
|
Kaveh `@ka7eh`_
|
||||||
|
Keith Bailey `@keithjeb`_
|
||||||
|
Keith Webber `@townie`_
|
||||||
Kevin A. Stone
|
Kevin A. Stone
|
||||||
Kevin Ndung'u `@kevgathuku`_
|
Kevin Ndung'u `@kevgathuku`_
|
||||||
Keith Webber `@townie`_
|
|
||||||
Krzysztof Szumny `@noisy`_
|
Krzysztof Szumny `@noisy`_
|
||||||
Krzysztof Żuraw `@krzysztofzuraw`_
|
Krzysztof Żuraw `@krzysztofzuraw`_
|
||||||
Leonardo Jimenez `@xpostudio4`_
|
Leonardo Jimenez `@xpostudio4`_
|
||||||
|
@ -141,6 +144,7 @@ Listed in alphabetical order.
|
||||||
Mateusz Ostaszewski `@mostaszewski`_
|
Mateusz Ostaszewski `@mostaszewski`_
|
||||||
Mathijs Hoogland `@MathijsHoogland`_
|
Mathijs Hoogland `@MathijsHoogland`_
|
||||||
Matt Braymer-Hayes `@mattayes`_ @mattayes
|
Matt Braymer-Hayes `@mattayes`_ @mattayes
|
||||||
|
Matt Knapper `@mknapper1`_
|
||||||
Matt Linares
|
Matt Linares
|
||||||
Matt Menzenski `@menzenski`_
|
Matt Menzenski `@menzenski`_
|
||||||
Matt Warren `@mfwarren`_
|
Matt Warren `@mfwarren`_
|
||||||
|
@ -154,22 +158,24 @@ Listed in alphabetical order.
|
||||||
Parbhat Puri `@parbhat`_
|
Parbhat Puri `@parbhat`_
|
||||||
Peter Bittner `@bittner`_
|
Peter Bittner `@bittner`_
|
||||||
Peter Coles `@mrcoles`_
|
Peter Coles `@mrcoles`_
|
||||||
|
Philipp Matthies `@canonnervio`_
|
||||||
Pierre Chiquet `@pchiquet`_
|
Pierre Chiquet `@pchiquet`_
|
||||||
Raphael Pierzina `@hackebrot`_
|
|
||||||
Raony Guimarães Corrêa `@raonyguimaraes`_
|
Raony Guimarães Corrêa `@raonyguimaraes`_
|
||||||
|
Raphael Pierzina `@hackebrot`_
|
||||||
Reggie Riser `@reggieriser`_
|
Reggie Riser `@reggieriser`_
|
||||||
René Muhl `@rm--`_
|
René Muhl `@rm--`_
|
||||||
Roman Afanaskin `@siauPatrick`_
|
Roman Afanaskin `@siauPatrick`_
|
||||||
Roman Osipenko `@romanosipenko`_
|
Roman Osipenko `@romanosipenko`_
|
||||||
Russell Davies
|
Russell Davies
|
||||||
Sascha `@saschalalala`_ @saschalalala
|
|
||||||
Sam Collins `@MightySCollins`_
|
Sam Collins `@MightySCollins`_
|
||||||
|
Sascha `@saschalalala`_ @saschalalala
|
||||||
Shupeyko Nikita `@webyneter`_
|
Shupeyko Nikita `@webyneter`_
|
||||||
Sławek Ehlert `@slafs`_
|
Sławek Ehlert `@slafs`_
|
||||||
Srinivas Nyayapati `@shireenrao`_
|
Srinivas Nyayapati `@shireenrao`_
|
||||||
stepmr `@stepmr`_
|
stepmr `@stepmr`_
|
||||||
Steve Steiner `@ssteinerX`_
|
Steve Steiner `@ssteinerX`_
|
||||||
Sule Marshall `@suledev`_
|
Sule Marshall `@suledev`_
|
||||||
|
Tano Abeleyra `@tanoabeleyra`_
|
||||||
Taylor Baldwin
|
Taylor Baldwin
|
||||||
Théo Segonds `@show0k`_
|
Théo Segonds `@show0k`_
|
||||||
Tim Freund `@timfreund`_
|
Tim Freund `@timfreund`_
|
||||||
|
@ -178,16 +184,13 @@ Listed in alphabetical order.
|
||||||
Travis McNeill `@Travistock`_ @tavistock_esq
|
Travis McNeill `@Travistock`_ @tavistock_esq
|
||||||
Tubo Shi `@Tubo`_
|
Tubo Shi `@Tubo`_
|
||||||
Umair Ashraf `@umrashrf`_ @fabumair
|
Umair Ashraf `@umrashrf`_ @fabumair
|
||||||
Vlad Doster `@vladdoster`_
|
Vadim Iskuchekov `@Egregors`_ @egregors
|
||||||
Vitaly Babiy
|
Vitaly Babiy
|
||||||
Vivian Guillen `@viviangb`_
|
Vivian Guillen `@viviangb`_
|
||||||
|
Vlad Doster `@vladdoster`_
|
||||||
Will Farley `@goldhand`_ @g01dhand
|
Will Farley `@goldhand`_ @g01dhand
|
||||||
William Archinal `@archinal`_
|
William Archinal `@archinal`_
|
||||||
Yaroslav Halchenko
|
Yaroslav Halchenko
|
||||||
Denis Bobrov `@delneg`_
|
|
||||||
Philipp Matthies `@canonnervio`_
|
|
||||||
Vadim Iskuchekov `@Egregors`_ @egregors
|
|
||||||
Keith Bailey `@keithjeb`_
|
|
||||||
========================== ============================ ==============
|
========================== ============================ ==============
|
||||||
|
|
||||||
.. _@a7p: https://github.com/a7p
|
.. _@a7p: https://github.com/a7p
|
||||||
|
@ -220,6 +223,7 @@ Listed in alphabetical order.
|
||||||
.. _@chuckus: https://github.com/chuckus
|
.. _@chuckus: https://github.com/chuckus
|
||||||
.. _@cmackenzie1: https://github.com/cmackenzie1
|
.. _@cmackenzie1: https://github.com/cmackenzie1
|
||||||
.. _@Collederas: https://github.com/Collederas
|
.. _@Collederas: https://github.com/Collederas
|
||||||
|
.. _@curtisstpierre: https://github.com/curtisstpierre
|
||||||
.. _@davitovmasyan: https://github.com/davitovmasyan
|
.. _@davitovmasyan: https://github.com/davitovmasyan
|
||||||
.. _@ddiazpinto: https://github.com/ddiazpinto
|
.. _@ddiazpinto: https://github.com/ddiazpinto
|
||||||
.. _@demestav: https://github.com/demestav
|
.. _@demestav: https://github.com/demestav
|
||||||
|
@ -259,6 +263,7 @@ Listed in alphabetical order.
|
||||||
.. _@msaizar: https://github.com/msaizar
|
.. _@msaizar: https://github.com/msaizar
|
||||||
.. _@MathijsHoogland: https://github.com/MathijsHoogland
|
.. _@MathijsHoogland: https://github.com/MathijsHoogland
|
||||||
.. _@mattayes: https://github.com/mattayes
|
.. _@mattayes: https://github.com/mattayes
|
||||||
|
.. _@mknapper1: https://github.com/mknapper1
|
||||||
.. _@menzenski: https://github.com/menzenski
|
.. _@menzenski: https://github.com/menzenski
|
||||||
.. _@mostaszewski: https://github.com/mostaszewski
|
.. _@mostaszewski: https://github.com/mostaszewski
|
||||||
.. _@mfwarren: https://github.com/mfwarren
|
.. _@mfwarren: https://github.com/mfwarren
|
||||||
|
@ -319,6 +324,7 @@ Listed in alphabetical order.
|
||||||
.. _@hanaquadara: https://github.com/hanaquadara
|
.. _@hanaquadara: https://github.com/hanaquadara
|
||||||
.. _@vladdoster: https://github.com/vladdoster
|
.. _@vladdoster: https://github.com/vladdoster
|
||||||
.. _@cmargieson: https://github.com/cmargieson
|
.. _@cmargieson: https://github.com/cmargieson
|
||||||
|
.. _@tanoabeleyra: https://github.com/tanoabeleyra
|
||||||
|
|
||||||
Special Thanks
|
Special Thanks
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
24
README.rst
24
README.rst
|
@ -36,7 +36,7 @@ production-ready Django projects quickly.
|
||||||
Features
|
Features
|
||||||
---------
|
---------
|
||||||
|
|
||||||
* For Django 2.1
|
* For Django 2.2
|
||||||
* Works with Python 3.6
|
* Works with Python 3.6
|
||||||
* Renders Django projects with 100% starting test coverage
|
* Renders Django projects with 100% starting test coverage
|
||||||
* Twitter Bootstrap_ v4 (`maintained Foundation fork`_ also available)
|
* Twitter Bootstrap_ v4 (`maintained Foundation fork`_ also available)
|
||||||
|
@ -89,7 +89,7 @@ Constraints
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
* Only maintained 3rd party libraries are used.
|
* 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).
|
* Environment variables for configuration (This won't work with Apache/mod_wsgi).
|
||||||
|
|
||||||
Support this Project!
|
Support this Project!
|
||||||
|
@ -169,23 +169,21 @@ Answer the prompts with your own desired options_. For example::
|
||||||
use_heroku [n]: y
|
use_heroku [n]: y
|
||||||
use_compressor [n]: y
|
use_compressor [n]: y
|
||||||
Select postgresql_version:
|
Select postgresql_version:
|
||||||
1 - 10.5
|
1 - 11.3
|
||||||
2 - 10.4
|
2 - 10.8
|
||||||
3 - 10.3
|
3 - 9.6
|
||||||
4 - 10.2
|
4 - 9.5
|
||||||
5 - 10.1
|
5 - 9.4
|
||||||
6 - 9.6
|
Choose from 1, 2, 3, 4, 5 [1]: 1
|
||||||
7 - 9.5
|
|
||||||
8 - 9.4
|
|
||||||
Choose from 1, 2, 3, 4, 5, 6, 7, 8 [1]: 1
|
|
||||||
Select js_task_runner:
|
Select js_task_runner:
|
||||||
1 - None
|
1 - None
|
||||||
2 - Gulp
|
2 - Gulp
|
||||||
Choose from 1, 2 [1]: 1
|
Choose from 1, 2 [1]: 1
|
||||||
Select cloud_provider:
|
Select cloud_provider:
|
||||||
1 - AWS
|
1 - AWS
|
||||||
2 - GCS
|
2 - GCP
|
||||||
Choose from 1, 2 [1]: 1
|
3 - None
|
||||||
|
Choose from 1, 2, 3 [1]: 1
|
||||||
custom_bootstrap_compilation [n]: n
|
custom_bootstrap_compilation [n]: n
|
||||||
Select open_source_license:
|
Select open_source_license:
|
||||||
1 - MIT
|
1 - MIT
|
||||||
|
|
|
@ -18,11 +18,8 @@
|
||||||
"use_pycharm": "n",
|
"use_pycharm": "n",
|
||||||
"use_docker": "n",
|
"use_docker": "n",
|
||||||
"postgresql_version": [
|
"postgresql_version": [
|
||||||
"10.5",
|
"11.3",
|
||||||
"10.4",
|
"10.8",
|
||||||
"10.3",
|
|
||||||
"10.2",
|
|
||||||
"10.1",
|
|
||||||
"9.6",
|
"9.6",
|
||||||
"9.5",
|
"9.5",
|
||||||
"9.4"
|
"9.4"
|
||||||
|
@ -33,7 +30,8 @@
|
||||||
],
|
],
|
||||||
"cloud_provider": [
|
"cloud_provider": [
|
||||||
"AWS",
|
"AWS",
|
||||||
"GCE"
|
"GCP",
|
||||||
|
"None"
|
||||||
],
|
],
|
||||||
"custom_bootstrap_compilation": "n",
|
"custom_bootstrap_compilation": "n",
|
||||||
"use_compressor": "n",
|
"use_compressor": "n",
|
||||||
|
|
|
@ -42,7 +42,7 @@ master_doc = "index"
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = "Cookiecutter Django"
|
project = "Cookiecutter Django"
|
||||||
copyright = "2013-2018, Daniel Roy Greenfeld".format(now.year)
|
copyright = "2013-{}, Daniel Roy Greenfeld".format(now.year)
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
|
|
@ -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.
|
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.
|
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.
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,12 @@ Getting Up and Running Locally With Docker
|
||||||
The steps below will get you up and running with a local development environment.
|
The steps below will get you up and running with a local development environment.
|
||||||
All of these commands assume you are in the root of your generated project.
|
All of these commands assume you are in the root of your generated project.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
If you're new to Docker, please be aware that some resources are cached system-wide
|
||||||
|
and might reappear if you generate a project multiple times with the same name (e.g.
|
||||||
|
:ref:`this issue with Postgres <docker-postgres-auth-failed>`).
|
||||||
|
|
||||||
|
|
||||||
Prerequisites
|
Prerequisites
|
||||||
-------------
|
-------------
|
||||||
|
|
|
@ -49,14 +49,11 @@ use_docker:
|
||||||
postgresql_version:
|
postgresql_version:
|
||||||
Select a PostgreSQL_ version to use. The choices are:
|
Select a PostgreSQL_ version to use. The choices are:
|
||||||
|
|
||||||
1. 10.5
|
1. 11.3
|
||||||
2. 10.4
|
2. 10.8
|
||||||
3. 10.3
|
3. 9.6
|
||||||
4. 10.2
|
4. 9.5
|
||||||
5. 10.1
|
5. 9.4
|
||||||
6. 9.6
|
|
||||||
7. 9.5
|
|
||||||
8. 9.4
|
|
||||||
|
|
||||||
js_task_runner:
|
js_task_runner:
|
||||||
Select a JavaScript task runner. The choices are:
|
Select a JavaScript task runner. The choices are:
|
||||||
|
@ -68,7 +65,10 @@ cloud_provider:
|
||||||
Select a cloud provider for static & media files. The choices are:
|
Select a cloud provider for static & media files. The choices are:
|
||||||
|
|
||||||
1. AWS_
|
1. AWS_
|
||||||
2. GCS_
|
2. GCP_
|
||||||
|
3. None
|
||||||
|
|
||||||
|
Note that if you choose no cloud provider, media files won't work.
|
||||||
|
|
||||||
custom_bootstrap_compilation:
|
custom_bootstrap_compilation:
|
||||||
Indicates whether the project should support Bootstrap recompilation
|
Indicates whether the project should support Bootstrap recompilation
|
||||||
|
@ -100,7 +100,7 @@ use_travisci:
|
||||||
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
|
||||||
(comes in handy when working in teams where local environment reproducibility
|
(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.
|
Note: .env(s) are only utilized when Docker Compose and/or Heroku support is enabled.
|
||||||
|
|
||||||
debug:
|
debug:
|
||||||
|
@ -123,7 +123,7 @@ debug:
|
||||||
.. _Gulp: https://github.com/gulpjs/gulp
|
.. _Gulp: https://github.com/gulpjs/gulp
|
||||||
|
|
||||||
.. _AWS: https://aws.amazon.com/s3/
|
.. _AWS: https://aws.amazon.com/s3/
|
||||||
.. _GCS: https://cloud.google.com/storage/
|
.. _GCP: https://cloud.google.com/storage/
|
||||||
|
|
||||||
.. _Django Compressor: https://github.com/django-compressor/django-compressor
|
.. _Django Compressor: https://github.com/django-compressor/django-compressor
|
||||||
|
|
||||||
|
|
|
@ -45,12 +45,13 @@ DJANGO_AWS_ACCESS_KEY_ID AWS_ACCESS_KEY_ID n/a
|
||||||
DJANGO_AWS_SECRET_ACCESS_KEY AWS_SECRET_ACCESS_KEY n/a raises error
|
DJANGO_AWS_SECRET_ACCESS_KEY AWS_SECRET_ACCESS_KEY n/a raises error
|
||||||
DJANGO_AWS_STORAGE_BUCKET_NAME AWS_STORAGE_BUCKET_NAME n/a raises error
|
DJANGO_AWS_STORAGE_BUCKET_NAME AWS_STORAGE_BUCKET_NAME n/a raises error
|
||||||
DJANGO_AWS_S3_REGION_NAME AWS_S3_REGION_NAME n/a None
|
DJANGO_AWS_S3_REGION_NAME AWS_S3_REGION_NAME n/a None
|
||||||
DJANGO_GCE_STORAGE_BUCKET_NAME GS_BUCKET_NAME n/a raises error
|
DJANGO_GCP_STORAGE_BUCKET_NAME GS_BUCKET_NAME n/a raises error
|
||||||
GOOGLE_APPLICATION_CREDENTIALS n/a n/a raises error
|
GOOGLE_APPLICATION_CREDENTIALS n/a n/a raises error
|
||||||
SENTRY_DSN SENTRY_DSN n/a raises error
|
SENTRY_DSN SENTRY_DSN n/a raises error
|
||||||
DJANGO_SENTRY_LOG_LEVEL SENTRY_LOG_LEVEL n/a logging.INFO
|
DJANGO_SENTRY_LOG_LEVEL SENTRY_LOG_LEVEL n/a logging.INFO
|
||||||
MAILGUN_API_KEY MAILGUN_ACCESS_KEY n/a raises error
|
MAILGUN_API_KEY MAILGUN_API_KEY n/a raises error
|
||||||
MAILGUN_DOMAIN MAILGUN_SENDER_DOMAIN n/a raises error
|
MAILGUN_DOMAIN MAILGUN_SENDER_DOMAIN n/a raises error
|
||||||
|
MAILGUN_API_URL n/a n/a "https://api.mailgun.net/v3"
|
||||||
======================================= =========================== ============================================== ======================================================================
|
======================================= =========================== ============================================== ======================================================================
|
||||||
|
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
|
@ -3,15 +3,49 @@ Troubleshooting
|
||||||
|
|
||||||
This page contains some advice about errors and problems commonly encountered during the development of Cookiecutter Django applications.
|
This page contains some advice about errors and problems commonly encountered during the development of Cookiecutter Django applications.
|
||||||
|
|
||||||
|
Server Error on sign-up/log-in
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Make sure you have configured the mail backend (e.g. Mailgun) by adding the API key and sender domain
|
||||||
|
|
||||||
|
.. include:: mailgun.rst
|
||||||
|
|
||||||
|
.. _docker-postgres-auth-failed:
|
||||||
|
|
||||||
|
Docker: Postgres authentication failed
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
Examples of logs::
|
||||||
|
|
||||||
|
postgres_1 | 2018-06-07 19:11:23.963 UTC [81] FATAL: password authentication failed for user "pydanny"
|
||||||
|
postgres_1 | 2018-06-07 19:11:23.963 UTC [81] DETAIL: Password does not match for user "pydanny".
|
||||||
|
postgres_1 | Connection matched pg_hba.conf line 95: "host all all all md5"
|
||||||
|
|
||||||
|
If you recreate the project multiple times with the same name, Docker would preserve the volumes for the postgres container between projects. Here is what happens:
|
||||||
|
|
||||||
|
#. You generate the project the first time. The .env postgres file is populated with the random password
|
||||||
|
#. You run the docker-compose and the containers are created. The postgres container creates the database based on the .env file credentials
|
||||||
|
#. You "regenerate" the project with the same name, so the postgres .env file is populated with a new random password
|
||||||
|
#. You run docker-compose. Since the names of the containers are the same, docker will try to start them (not create them from scratch i.e. it won't execute the Dockerfile to recreate the database). When this happens, it tries to start the database based on the new credentials which do not match the ones that the database was created with, and you get the error message above.
|
||||||
|
|
||||||
|
To fix this, you can either:
|
||||||
|
|
||||||
|
- Clear your project-related Docker cache with ``docker-compose -f local.yml down --volumes --rmi all``.
|
||||||
|
- Use the Docker volume sub-commands to find volumes (`ls`_) and remove them (`rm`_).
|
||||||
|
- Use the `prune`_ command to clear system-wide (use with care!).
|
||||||
|
|
||||||
|
.. _ls: https://docs.docker.com/engine/reference/commandline/volume_ls/
|
||||||
|
.. _rm: https://docs.docker.com/engine/reference/commandline/volume_rm/
|
||||||
|
.. _prune: https://docs.docker.com/v17.09/engine/reference/commandline/system_prune/
|
||||||
|
|
||||||
|
Others
|
||||||
|
------
|
||||||
|
|
||||||
#. ``project_slug`` must be a valid Python module name or you will have issues on imports.
|
#. ``project_slug`` must be a valid Python module name or you will have issues on imports.
|
||||||
|
|
||||||
#. ``jinja2.exceptions.TemplateSyntaxError: Encountered unknown tag 'now'.``: please upgrade your cookiecutter version to >= 1.4 (see `#528`_)
|
#. ``jinja2.exceptions.TemplateSyntaxError: Encountered unknown tag 'now'.``: please upgrade your cookiecutter version to >= 1.4 (see `#528`_)
|
||||||
|
|
||||||
#. Internal server error on user registration: make sure you have configured the mail backend (e.g. Mailgun) by adding the API key and sender domain
|
|
||||||
|
|
||||||
#. New apps not getting created in project root: This is the expected behavior, because cookiecutter-django does not change the way that django startapp works, you'll have to fix this manually (see `#1725`_)
|
#. New apps not getting created in project root: This is the expected behavior, because cookiecutter-django does not change the way that django startapp works, you'll have to fix this manually (see `#1725`_)
|
||||||
|
|
||||||
#. .. include:: mailgun.rst
|
|
||||||
|
|
||||||
.. _#528: https://github.com/pydanny/cookiecutter-django/issues/528#issuecomment-212650373
|
.. _#528: https://github.com/pydanny/cookiecutter-django/issues/528#issuecomment-212650373
|
||||||
.. _#1725: https://github.com/pydanny/cookiecutter-django/issues/1725#issuecomment-407493176
|
.. _#1725: https://github.com/pydanny/cookiecutter-django/issues/1725#issuecomment-407493176
|
||||||
|
|
|
@ -89,8 +89,16 @@ def remove_packagejson_file():
|
||||||
os.remove(file_name)
|
os.remove(file_name)
|
||||||
|
|
||||||
|
|
||||||
def remove_celery_app():
|
def remove_celery_files():
|
||||||
shutil.rmtree(os.path.join("{{ cookiecutter.project_slug }}", "taskapp"))
|
file_names = [
|
||||||
|
os.path.join("config", "celery_app.py"),
|
||||||
|
os.path.join("{{ cookiecutter.project_slug }}", "users", "tasks.py"),
|
||||||
|
os.path.join(
|
||||||
|
"{{ cookiecutter.project_slug }}", "users", "tests", "test_tasks.py"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
for file_name in file_names:
|
||||||
|
os.remove(file_name)
|
||||||
|
|
||||||
|
|
||||||
def remove_dottravisyml_file():
|
def remove_dottravisyml_file():
|
||||||
|
@ -320,8 +328,14 @@ def main():
|
||||||
if "{{ cookiecutter.use_docker }}".lower() == "y":
|
if "{{ cookiecutter.use_docker }}".lower() == "y":
|
||||||
remove_node_dockerfile()
|
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":
|
if "{{ cookiecutter.use_celery }}".lower() == "n":
|
||||||
remove_celery_app()
|
remove_celery_files()
|
||||||
if "{{ cookiecutter.use_docker }}".lower() == "y":
|
if "{{ cookiecutter.use_docker }}".lower() == "y":
|
||||||
remove_celery_compose_dirs()
|
remove_celery_compose_dirs()
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
[pytest]
|
[pytest]
|
||||||
|
addopts = -x --tb=short
|
||||||
python_paths = .
|
python_paths = .
|
||||||
norecursedirs = .tox .git */migrations/* */static/* docs venv */{{cookiecutter.project_slug}}/*
|
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
|
||||||
|
|
|
@ -5,12 +5,13 @@ binaryornot==0.4.4
|
||||||
# Code quality
|
# Code quality
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
black==19.3b0
|
black==19.3b0
|
||||||
flake8==3.7.6
|
flake8==3.7.7
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
tox==3.9.0
|
tox==3.12.1
|
||||||
pytest==4.4.2
|
pytest==4.6.0
|
||||||
pytest_cases==1.6.2
|
pytest_cases==1.6.3
|
||||||
pytest-cookies==0.3.0
|
pytest-cookies==0.3.0
|
||||||
|
pytest-xdist==1.28.0
|
||||||
pyyaml==5.1
|
pyyaml==5.1
|
||||||
|
|
6
setup.py
6
setup.py
|
@ -10,10 +10,10 @@ except ImportError:
|
||||||
|
|
||||||
# Our version ALWAYS matches the version of Django we support
|
# 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.
|
# If Django has a new release, we branch, tag, then update this setting after the tag.
|
||||||
version = "2.0.2"
|
version = "2.2.1"
|
||||||
|
|
||||||
if sys.argv[-1] == "tag":
|
if sys.argv[-1] == "tag":
|
||||||
os.system('git tag -a %s -m "version %s"' % (version, version))
|
os.system(f'git tag -a {version} -m "version {version}"')
|
||||||
os.system("git push --tags")
|
os.system("git push --tags")
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ setup(
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"Development Status :: 4 - Beta",
|
"Development Status :: 4 - Beta",
|
||||||
"Environment :: Console",
|
"Environment :: Console",
|
||||||
"Framework :: Django :: 2.0",
|
"Framework :: Django :: 2.2",
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
"Natural Language :: English",
|
"Natural Language :: English",
|
||||||
"License :: OSI Approved :: BSD License",
|
"License :: OSI Approved :: BSD License",
|
||||||
|
|
|
@ -7,11 +7,11 @@ import sh
|
||||||
import yaml
|
import yaml
|
||||||
from binaryornot.check import is_binary
|
from binaryornot.check import is_binary
|
||||||
|
|
||||||
PATTERN = "{{(\s?cookiecutter)[.](.*?)}}"
|
PATTERN = r"{{(\s?cookiecutter)[.](.*?)}}"
|
||||||
RE_OBJ = re.compile(PATTERN)
|
RE_OBJ = re.compile(PATTERN)
|
||||||
|
|
||||||
YN_CHOICES = ["y", "n"]
|
YN_CHOICES = ["y", "n"]
|
||||||
CLOUD_CHOICES = ["AWS", "GCE"]
|
CLOUD_CHOICES = ["AWS", "GCE", "None"]
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -101,9 +101,10 @@ def test_project_generation(cookies, context, context_combination):
|
||||||
check_paths(paths)
|
check_paths(paths)
|
||||||
|
|
||||||
|
|
||||||
def test_linting_passes(cookies, context_combination):
|
@pytest.mark.flake8
|
||||||
|
def test_flake8_passes(cookies, context_combination):
|
||||||
"""
|
"""
|
||||||
Generated project should pass flake8 & black.
|
Generated project should pass flake8.
|
||||||
|
|
||||||
This is parametrized for each combination from ``context_combination`` fixture
|
This is parametrized for each combination from ``context_combination`` fixture
|
||||||
"""
|
"""
|
||||||
|
@ -114,6 +115,16 @@ def test_linting_passes(cookies, context_combination):
|
||||||
except sh.ErrorReturnCode as e:
|
except sh.ErrorReturnCode as e:
|
||||||
pytest.fail(e)
|
pytest.fail(e)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.black
|
||||||
|
def test_black_passes(cookies, context_combination):
|
||||||
|
"""
|
||||||
|
Generated project should pass black.
|
||||||
|
|
||||||
|
This is parametrized for each combination from ``context_combination`` fixture
|
||||||
|
"""
|
||||||
|
result = cookies.bake(extra_context=context_combination)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sh.black("--check", "--diff", "--exclude", "migrations", f"{result.project}/")
|
sh.black("--check", "--diff", "--exclude", "migrations", f"{result.project}/")
|
||||||
except sh.ErrorReturnCode as e:
|
except sh.ErrorReturnCode as e:
|
||||||
|
|
12
tox.ini
12
tox.ini
|
@ -1,11 +1,19 @@
|
||||||
[tox]
|
[tox]
|
||||||
skipsdist = true
|
skipsdist = true
|
||||||
envlist = py36,black
|
envlist = py36,flake8,black,black-template
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
deps = -rrequirements.txt
|
deps = -rrequirements.txt
|
||||||
commands = pytest {posargs:./tests}
|
commands = pytest -m "not flake8" -m "not black" {posargs:./tests}
|
||||||
|
|
||||||
|
[testenv:flake8]
|
||||||
|
deps = -rrequirements.txt
|
||||||
|
commands = pytest -m flake8 {posargs:./tests}
|
||||||
|
|
||||||
[testenv:black]
|
[testenv:black]
|
||||||
|
deps = -rrequirements.txt
|
||||||
|
commands = pytest -m black {posargs:./tests}
|
||||||
|
|
||||||
|
[testenv:black-template]
|
||||||
deps = black
|
deps = black
|
||||||
commands = black --check hooks tests setup.py docs
|
commands = black --check hooks tests setup.py docs
|
||||||
|
|
|
@ -22,11 +22,11 @@ MAILGUN_DOMAIN=
|
||||||
DJANGO_AWS_ACCESS_KEY_ID=
|
DJANGO_AWS_ACCESS_KEY_ID=
|
||||||
DJANGO_AWS_SECRET_ACCESS_KEY=
|
DJANGO_AWS_SECRET_ACCESS_KEY=
|
||||||
DJANGO_AWS_STORAGE_BUCKET_NAME=
|
DJANGO_AWS_STORAGE_BUCKET_NAME=
|
||||||
{% elif cookiecutter.cloud_provider == 'GCE' %}
|
{% elif cookiecutter.cloud_provider == 'GCP' %}
|
||||||
# GCE
|
# GCP
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
GOOGLE_APPLICATION_CREDENTIALS=
|
GOOGLE_APPLICATION_CREDENTIALS=
|
||||||
DJANGO_GCE_STORAGE_BUCKET_NAME=
|
DJANGO_GCP_STORAGE_BUCKET_NAME=
|
||||||
{% endif %}
|
{% endif %}
|
||||||
# django-allauth
|
# django-allauth
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
release: python manage.py migrate
|
release: python manage.py migrate
|
||||||
web: gunicorn config.wsgi:application
|
web: gunicorn config.wsgi:application
|
||||||
{% if cookiecutter.use_celery == "y" -%}
|
{% if cookiecutter.use_celery == "y" -%}
|
||||||
worker: celery worker --app={{cookiecutter.project_slug}}.taskapp --loglevel=info
|
worker: celery worker --app=config.celery_app --loglevel=info
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
|
@ -79,7 +79,7 @@ To run a celery worker:
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
cd {{cookiecutter.project_slug}}
|
cd {{cookiecutter.project_slug}}
|
||||||
celery -A {{cookiecutter.project_slug}}.taskapp worker -l info
|
celery -A config.celery_app worker -l info
|
||||||
|
|
||||||
Please note: For Celery's import magic to work, it is important *where* the celery commands are run. If you are in the same folder with *manage.py*, you should be right.
|
Please note: For Celery's import magic to work, it is important *where* the celery commands are run. If you are in the same folder with *manage.py*, you should be right.
|
||||||
|
|
||||||
|
|
|
@ -5,4 +5,4 @@ set -o nounset
|
||||||
|
|
||||||
|
|
||||||
rm -f './celerybeat.pid'
|
rm -f './celerybeat.pid'
|
||||||
celery -A {{cookiecutter.project_slug}}.taskapp beat -l INFO
|
celery -A config.celery_app beat -l INFO
|
||||||
|
|
|
@ -5,6 +5,6 @@ set -o nounset
|
||||||
|
|
||||||
|
|
||||||
celery flower \
|
celery flower \
|
||||||
--app={{cookiecutter.project_slug}}.taskapp \
|
--app=config.celery_app \
|
||||||
--broker="${CELERY_BROKER_URL}" \
|
--broker="${CELERY_BROKER_URL}" \
|
||||||
--basic_auth="${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}"
|
--basic_auth="${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}"
|
||||||
|
|
|
@ -4,4 +4,4 @@ set -o errexit
|
||||||
set -o nounset
|
set -o nounset
|
||||||
|
|
||||||
|
|
||||||
celery -A {{cookiecutter.project_slug}}.taskapp worker -l INFO
|
celery -A config.celery_app worker -l INFO
|
||||||
|
|
|
@ -5,4 +5,4 @@ set -o pipefail
|
||||||
set -o nounset
|
set -o nounset
|
||||||
|
|
||||||
|
|
||||||
celery -A {{cookiecutter.project_slug}}.taskapp beat -l INFO
|
celery -A config.celery_app beat -l INFO
|
||||||
|
|
|
@ -5,6 +5,6 @@ set -o nounset
|
||||||
|
|
||||||
|
|
||||||
celery flower \
|
celery flower \
|
||||||
--app={{cookiecutter.project_slug}}.taskapp \
|
--app=config.celery_app \
|
||||||
--broker="${CELERY_BROKER_URL}" \
|
--broker="${CELERY_BROKER_URL}" \
|
||||||
--basic_auth="${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}"
|
--basic_auth="${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}"
|
||||||
|
|
|
@ -5,4 +5,4 @@ set -o pipefail
|
||||||
set -o nounset
|
set -o nounset
|
||||||
|
|
||||||
|
|
||||||
celery -A {{cookiecutter.project_slug}}.taskapp worker -l INFO
|
celery -A config.celery_app worker -l INFO
|
||||||
|
|
|
@ -17,7 +17,7 @@ defaultEntryPoints = ["http", "https"]
|
||||||
[acme]
|
[acme]
|
||||||
# Email address used for registration
|
# Email address used for registration
|
||||||
email = "{{ cookiecutter.email }}"
|
email = "{{ cookiecutter.email }}"
|
||||||
storageFile = "/etc/traefik/acme/acme.json"
|
storage = "/etc/traefik/acme/acme.json"
|
||||||
entryPoint = "https"
|
entryPoint = "https"
|
||||||
onDemand = false
|
onDemand = false
|
||||||
OnHostRule = true
|
OnHostRule = true
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
{% if cookiecutter.use_celery == 'y' -%}
|
||||||
|
# This will make sure the app is always imported when
|
||||||
|
# Django starts so that shared_task will use this app.
|
||||||
|
from .celery_app import app as celery_app
|
||||||
|
|
||||||
|
__all__ = ("celery_app",)
|
||||||
|
{% endif -%}
|
16
{{cookiecutter.project_slug}}/config/celery_app.py
Normal file
16
{{cookiecutter.project_slug}}/config/celery_app.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import os
|
||||||
|
from celery import Celery
|
||||||
|
|
||||||
|
# set the default Django settings module for the 'celery' program.
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
|
||||||
|
|
||||||
|
app = Celery("{{cookiecutter.project_slug}}")
|
||||||
|
|
||||||
|
# Using a string here means the worker doesn't have to serialize
|
||||||
|
# the configuration object to child processes.
|
||||||
|
# - namespace='CELERY' means all celery-related configuration keys
|
||||||
|
# should have a `CELERY_` prefix.
|
||||||
|
app.config_from_object("django.conf:settings", namespace="CELERY")
|
||||||
|
|
||||||
|
# Load task modules from all registered Django app configs.
|
||||||
|
app.autodiscover_tasks()
|
|
@ -35,6 +35,8 @@ USE_I18N = True
|
||||||
USE_L10N = True
|
USE_L10N = True
|
||||||
# https://docs.djangoproject.com/en/dev/ref/settings/#use-tz
|
# https://docs.djangoproject.com/en/dev/ref/settings/#use-tz
|
||||||
USE_TZ = True
|
USE_TZ = True
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#locale-paths
|
||||||
|
LOCALE_PATHS = [ROOT_DIR.path("locale")]
|
||||||
|
|
||||||
# DATABASES
|
# DATABASES
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -75,7 +77,7 @@ THIRD_PARTY_APPS = [
|
||||||
"rest_framework",
|
"rest_framework",
|
||||||
]
|
]
|
||||||
LOCAL_APPS = [
|
LOCAL_APPS = [
|
||||||
"{{ cookiecutter.project_slug }}.users.apps.UsersAppConfig",
|
"{{ cookiecutter.project_slug }}.users.apps.UsersConfig",
|
||||||
# Your stuff: custom apps go here
|
# Your stuff: custom apps go here
|
||||||
]
|
]
|
||||||
# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
|
# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
|
||||||
|
@ -126,6 +128,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
"django.middleware.security.SecurityMiddleware",
|
"django.middleware.security.SecurityMiddleware",
|
||||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||||
|
"django.middleware.locale.LocaleMiddleware",
|
||||||
"django.middleware.common.CommonMiddleware",
|
"django.middleware.common.CommonMiddleware",
|
||||||
"django.middleware.csrf.CsrfViewMiddleware",
|
"django.middleware.csrf.CsrfViewMiddleware",
|
||||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
|
@ -221,10 +224,33 @@ ADMINS = [("""{{cookiecutter.author_name}}""", "{{cookiecutter.email}}")]
|
||||||
# https://docs.djangoproject.com/en/dev/ref/settings/#managers
|
# https://docs.djangoproject.com/en/dev/ref/settings/#managers
|
||||||
MANAGERS = ADMINS
|
MANAGERS = ADMINS
|
||||||
|
|
||||||
|
# LOGGING
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#logging
|
||||||
|
# See https://docs.djangoproject.com/en/dev/topics/logging for
|
||||||
|
# more details on how to customize your logging configuration.
|
||||||
|
LOGGING = {
|
||||||
|
"version": 1,
|
||||||
|
"disable_existing_loggers": False,
|
||||||
|
"formatters": {
|
||||||
|
"verbose": {
|
||||||
|
"format": "%(levelname)s %(asctime)s %(module)s "
|
||||||
|
"%(process)d %(thread)d %(message)s"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"handlers": {
|
||||||
|
"console": {
|
||||||
|
"level": "DEBUG",
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
"formatter": "verbose",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {"level": "INFO", "handlers": ["console"]},
|
||||||
|
}
|
||||||
|
|
||||||
{% if cookiecutter.use_celery == 'y' -%}
|
{% if cookiecutter.use_celery == 'y' -%}
|
||||||
# Celery
|
# Celery
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
INSTALLED_APPS += ["{{cookiecutter.project_slug}}.taskapp.celery.CeleryAppConfig"]
|
|
||||||
if USE_TZ:
|
if USE_TZ:
|
||||||
# http://docs.celeryproject.org/en/latest/userguide/configuration.html#std:setting-timezone
|
# http://docs.celeryproject.org/en/latest/userguide/configuration.html#std:setting-timezone
|
||||||
CELERY_TIMEZONE = TIME_ZONE
|
CELERY_TIMEZONE = TIME_ZONE
|
||||||
|
@ -240,10 +266,10 @@ CELERY_TASK_SERIALIZER = "json"
|
||||||
CELERY_RESULT_SERIALIZER = "json"
|
CELERY_RESULT_SERIALIZER = "json"
|
||||||
# http://docs.celeryproject.org/en/latest/userguide/configuration.html#task-time-limit
|
# http://docs.celeryproject.org/en/latest/userguide/configuration.html#task-time-limit
|
||||||
# TODO: set to whatever value is adequate in your circumstances
|
# 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
|
# http://docs.celeryproject.org/en/latest/userguide/configuration.html#task-soft-time-limit
|
||||||
# TODO: set to whatever value is adequate in your circumstances
|
# TODO: set to whatever value is adequate in your circumstances
|
||||||
CELERYD_TASK_SOFT_TIME_LIMIT = 60
|
CELERY_TASK_SOFT_TIME_LIMIT = 60
|
||||||
|
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
# django-allauth
|
# django-allauth
|
||||||
|
|
|
@ -66,10 +66,12 @@ SECURE_CONTENT_TYPE_NOSNIFF = env.bool(
|
||||||
"DJANGO_SECURE_CONTENT_TYPE_NOSNIFF", default=True
|
"DJANGO_SECURE_CONTENT_TYPE_NOSNIFF", default=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
{% if cookiecutter.cloud_provider != 'None' -%}
|
||||||
# STORAGES
|
# STORAGES
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# https://django-storages.readthedocs.io/en/latest/#installation
|
# https://django-storages.readthedocs.io/en/latest/#installation
|
||||||
INSTALLED_APPS += ["storages"] # noqa F405
|
INSTALLED_APPS += ["storages"] # noqa F405
|
||||||
|
{%- endif -%}
|
||||||
{% if cookiecutter.cloud_provider == 'AWS' %}
|
{% if cookiecutter.cloud_provider == 'AWS' %}
|
||||||
# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings
|
# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings
|
||||||
AWS_ACCESS_KEY_ID = env("DJANGO_AWS_ACCESS_KEY_ID")
|
AWS_ACCESS_KEY_ID = env("DJANGO_AWS_ACCESS_KEY_ID")
|
||||||
|
@ -89,23 +91,24 @@ AWS_S3_OBJECT_PARAMETERS = {
|
||||||
AWS_DEFAULT_ACL = None
|
AWS_DEFAULT_ACL = None
|
||||||
# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings
|
# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings
|
||||||
AWS_S3_REGION_NAME = env("DJANGO_AWS_S3_REGION_NAME", default=None)
|
AWS_S3_REGION_NAME = env("DJANGO_AWS_S3_REGION_NAME", default=None)
|
||||||
{% elif cookiecutter.cloud_provider == 'GCE' %}
|
{% elif cookiecutter.cloud_provider == 'GCP' %}
|
||||||
DEFAULT_FILE_STORAGE = "storages.backends.gcloud.GoogleCloudStorage"
|
DEFAULT_FILE_STORAGE = "storages.backends.gcloud.GoogleCloudStorage"
|
||||||
GS_BUCKET_NAME = env("DJANGO_GCE_STORAGE_BUCKET_NAME")
|
GS_BUCKET_NAME = env("DJANGO_GCP_STORAGE_BUCKET_NAME")
|
||||||
GS_DEFAULT_ACL = "publicRead"
|
GS_DEFAULT_ACL = "publicRead"
|
||||||
{% endif %}
|
{% endif -%}
|
||||||
|
|
||||||
|
{% if cookiecutter.cloud_provider != 'None' or cookiecutter.use_whitenoise == 'y' -%}
|
||||||
# STATIC
|
# STATIC
|
||||||
# ------------------------
|
# ------------------------
|
||||||
|
{% endif -%}
|
||||||
{% if cookiecutter.use_whitenoise == 'y' -%}
|
{% if cookiecutter.use_whitenoise == 'y' -%}
|
||||||
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
||||||
{%- endif -%}
|
{% elif cookiecutter.cloud_provider == 'AWS' -%}
|
||||||
{%- if cookiecutter.cloud_provider == 'AWS' %}
|
|
||||||
STATICFILES_STORAGE = "config.settings.production.StaticRootS3Boto3Storage"
|
STATICFILES_STORAGE = "config.settings.production.StaticRootS3Boto3Storage"
|
||||||
STATIC_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/static/"
|
STATIC_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/static/"
|
||||||
{%- elif cookiecutter.cloud_provider == 'GCE' %}
|
{% elif cookiecutter.cloud_provider == 'GCP' -%}
|
||||||
STATIC_URL = "https://storage.googleapis.com/{}/static/".format(GS_BUCKET_NAME)
|
STATIC_URL = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/static/"
|
||||||
{%- endif %}
|
{% endif -%}
|
||||||
|
|
||||||
# MEDIA
|
# MEDIA
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -117,6 +120,7 @@ from storages.backends.s3boto3 import S3Boto3Storage # noqa E402
|
||||||
|
|
||||||
class StaticRootS3Boto3Storage(S3Boto3Storage):
|
class StaticRootS3Boto3Storage(S3Boto3Storage):
|
||||||
location = "static"
|
location = "static"
|
||||||
|
default_acl = "public-read"
|
||||||
|
|
||||||
|
|
||||||
class MediaRootS3Boto3Storage(S3Boto3Storage):
|
class MediaRootS3Boto3Storage(S3Boto3Storage):
|
||||||
|
@ -127,9 +131,9 @@ class MediaRootS3Boto3Storage(S3Boto3Storage):
|
||||||
# endregion
|
# endregion
|
||||||
DEFAULT_FILE_STORAGE = "config.settings.production.MediaRootS3Boto3Storage"
|
DEFAULT_FILE_STORAGE = "config.settings.production.MediaRootS3Boto3Storage"
|
||||||
MEDIA_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/media/"
|
MEDIA_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/media/"
|
||||||
{%- elif cookiecutter.cloud_provider == 'GCE' %}
|
{%- elif cookiecutter.cloud_provider == 'GCP' %}
|
||||||
MEDIA_URL = "https://storage.googleapis.com/{}/media/".format(GS_BUCKET_NAME)
|
MEDIA_URL = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/media/"
|
||||||
MEDIA_ROOT = "https://storage.googleapis.com/{}/media/".format(GS_BUCKET_NAME)
|
MEDIA_ROOT = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/media/"
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
# TEMPLATES
|
# TEMPLATES
|
||||||
|
@ -172,6 +176,7 @@ EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend"
|
||||||
ANYMAIL = {
|
ANYMAIL = {
|
||||||
"MAILGUN_API_KEY": env("MAILGUN_API_KEY"),
|
"MAILGUN_API_KEY": env("MAILGUN_API_KEY"),
|
||||||
"MAILGUN_SENDER_DOMAIN": env("MAILGUN_DOMAIN"),
|
"MAILGUN_SENDER_DOMAIN": env("MAILGUN_DOMAIN"),
|
||||||
|
"MAILGUN_API_URL": env("MAILGUN_API_URL", default="https://api.mailgun.net/v3"),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Gunicorn
|
# Gunicorn
|
||||||
|
@ -193,7 +198,7 @@ COMPRESS_ENABLED = env.bool("COMPRESS_ENABLED", default=True)
|
||||||
# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_STORAGE
|
# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_STORAGE
|
||||||
COMPRESS_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
|
COMPRESS_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
|
||||||
# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_URL
|
# 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 %}
|
{% endif %}
|
||||||
{%- if cookiecutter.use_whitenoise == 'n' -%}
|
{%- if cookiecutter.use_whitenoise == 'n' -%}
|
||||||
# Collectfast
|
# Collectfast
|
||||||
|
@ -233,6 +238,7 @@ LOGGING = {
|
||||||
"formatter": "verbose",
|
"formatter": "verbose",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"root": {"level": "INFO", "handlers": ["console"]},
|
||||||
"loggers": {
|
"loggers": {
|
||||||
"django.request": {
|
"django.request": {
|
||||||
"handlers": ["mail_admins"],
|
"handlers": ["mail_admins"],
|
||||||
|
@ -263,6 +269,7 @@ LOGGING = {
|
||||||
"formatter": "verbose",
|
"formatter": "verbose",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"root": {"level": "INFO", "handlers": ["console"]},
|
||||||
"loggers": {
|
"loggers": {
|
||||||
"django.db.backends": {
|
"django.db.backends": {
|
||||||
"level": "ERROR",
|
"level": "ERROR",
|
||||||
|
@ -286,7 +293,7 @@ SENTRY_LOG_LEVEL = env.int("DJANGO_SENTRY_LOG_LEVEL", logging.INFO)
|
||||||
|
|
||||||
sentry_logging = LoggingIntegration(
|
sentry_logging = LoggingIntegration(
|
||||||
level=SENTRY_LOG_LEVEL, # Capture info and above as breadcrumbs
|
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' %}
|
{%- if cookiecutter.use_celery == 'y' %}
|
||||||
|
|
|
@ -45,7 +45,7 @@ services:
|
||||||
{%- if cookiecutter.use_celery == 'y' %}
|
{%- if cookiecutter.use_celery == 'y' %}
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:3.2
|
image: redis:5.0
|
||||||
|
|
||||||
celeryworker:
|
celeryworker:
|
||||||
<<: *django
|
<<: *django
|
||||||
|
|
|
@ -44,7 +44,7 @@ services:
|
||||||
- "0.0.0.0:443:443"
|
- "0.0.0.0:443:443"
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:3.2
|
image: redis:5.0
|
||||||
{%- if cookiecutter.use_celery == 'y' %}
|
{%- if cookiecutter.use_celery == 'y' %}
|
||||||
|
|
||||||
celeryworker:
|
celeryworker:
|
||||||
|
|
|
@ -18,13 +18,13 @@ flower==0.9.3 # https://github.com/mher/flower
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
django==2.1.8 # pyup: < 2.2 # https://www.djangoproject.com/
|
django==2.2.2 # pyup: < 3.0 # https://www.djangoproject.com/
|
||||||
django-environ==0.4.5 # https://github.com/joke2k/django-environ
|
django-environ==0.4.5 # https://github.com/joke2k/django-environ
|
||||||
django-model-utils==3.1.2 # https://github.com/jazzband/django-model-utils
|
django-model-utils==3.1.2 # https://github.com/jazzband/django-model-utils
|
||||||
django-allauth==0.39.1 # https://github.com/pennersr/django-allauth
|
django-allauth==0.39.1 # https://github.com/pennersr/django-allauth
|
||||||
django-crispy-forms==1.7.2 # https://github.com/django-crispy-forms/django-crispy-forms
|
django-crispy-forms==1.7.2 # https://github.com/django-crispy-forms/django-crispy-forms
|
||||||
{%- if cookiecutter.use_compressor == "y" %}
|
{%- if cookiecutter.use_compressor == "y" %}
|
||||||
django-compressor==2.2 # https://github.com/django-compressor/django-compressor
|
django-compressor==2.3 # https://github.com/django-compressor/django-compressor
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
django-redis==4.10.0 # https://github.com/niwinz/django-redis
|
django-redis==4.10.0 # https://github.com/niwinz/django-redis
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
-r ./base.txt
|
-r ./base.txt
|
||||||
|
|
||||||
Werkzeug==0.15.2 # 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
|
ipdb==0.12 # https://github.com/gotcha/ipdb
|
||||||
Sphinx==2.0.1 # https://github.com/sphinx-doc/sphinx
|
Sphinx==2.1.0 # https://github.com/sphinx-doc/sphinx
|
||||||
{%- if cookiecutter.use_docker == 'y' %}
|
{%- 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 %}
|
{%- else %}
|
||||||
psycopg2-binary==2.8.2 # https://github.com/psycopg/psycopg2
|
psycopg2-binary==2.8.2 # https://github.com/psycopg/psycopg2
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
@ -12,12 +12,12 @@ psycopg2-binary==2.8.2 # https://github.com/psycopg/psycopg2
|
||||||
# Testing
|
# Testing
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
mypy==0.701 # https://github.com/python/mypy
|
mypy==0.701 # https://github.com/python/mypy
|
||||||
pytest==4.4.2 # https://github.com/pytest-dev/pytest
|
pytest==4.6.0 # 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.5 # https://github.com/PyCQA/flake8
|
flake8==3.7.7 # https://github.com/PyCQA/flake8
|
||||||
coverage==4.5.3 # https://github.com/nedbat/coveragepy
|
coverage==4.5.3 # https://github.com/nedbat/coveragepy
|
||||||
black==19.3b0 # https://github.com/ambv/black
|
black==19.3b0 # https://github.com/ambv/black
|
||||||
pylint-django==2.0.9 # https://github.com/PyCQA/pylint-django
|
pylint-django==2.0.9 # https://github.com/PyCQA/pylint-django
|
||||||
|
@ -27,9 +27,9 @@ pylint-celery==0.3 # https://github.com/PyCQA/pylint-celery
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
factory-boy==2.11.1 # https://github.com/FactoryBoy/factory_boy
|
factory-boy==2.12.0 # https://github.com/FactoryBoy/factory_boy
|
||||||
|
|
||||||
django-debug-toolbar==1.11 # https://github.com/jazzband/django-debug-toolbar
|
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
|
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
|
pytest-django==3.4.8 # https://github.com/pytest-dev/pytest-django
|
||||||
|
|
|
@ -3,19 +3,19 @@
|
||||||
-r ./base.txt
|
-r ./base.txt
|
||||||
|
|
||||||
gunicorn==19.9.0 # https://github.com/benoitc/gunicorn
|
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' %}
|
{%- if cookiecutter.use_whitenoise == 'n' %}
|
||||||
Collectfast==0.6.2 # https://github.com/antonagestam/collectfast
|
Collectfast==0.6.2 # https://github.com/antonagestam/collectfast
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{%- if cookiecutter.use_sentry == "y" %}
|
{%- if cookiecutter.use_sentry == "y" %}
|
||||||
sentry-sdk==0.7.14 # https://github.com/getsentry/sentry-python
|
sentry-sdk==0.9.0 # https://github.com/getsentry/sentry-python
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
{%- if cookiecutter.cloud_provider == 'AWS' %}
|
{%- if cookiecutter.cloud_provider == 'AWS' %}
|
||||||
django-storages[boto3]==1.7.1 # https://github.com/jschneier/django-storages
|
django-storages[boto3]==1.7.1 # https://github.com/jschneier/django-storages
|
||||||
{%- elif cookiecutter.cloud_provider == 'GCE' %}
|
{%- elif cookiecutter.cloud_provider == 'GCP' %}
|
||||||
django-storages[google]==1.7.1 # https://github.com/jschneier/django-storages
|
django-storages[google]==1.7.1 # https://github.com/jschneier/django-storages
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
django-anymail[mailgun]==6.0 # https://github.com/anymail/django-anymail
|
django-anymail[mailgun]==6.0.1 # https://github.com/anymail/django-anymail
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
{% if cookiecutter.use_celery == 'y' -%}
|
|
||||||
import os
|
|
||||||
from celery import Celery
|
|
||||||
from django.apps import apps, AppConfig
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
|
|
||||||
if not settings.configured:
|
|
||||||
# set the default Django settings module for the 'celery' program.
|
|
||||||
os.environ.setdefault(
|
|
||||||
"DJANGO_SETTINGS_MODULE", "config.settings.local"
|
|
||||||
) # pragma: no cover
|
|
||||||
|
|
||||||
|
|
||||||
app = Celery("{{cookiecutter.project_slug}}")
|
|
||||||
# Using a string here means the worker will not have to
|
|
||||||
# pickle the object when using Windows.
|
|
||||||
# - namespace='CELERY' means all celery-related configuration keys
|
|
||||||
# should have a `CELERY_` prefix.
|
|
||||||
app.config_from_object("django.conf:settings", namespace="CELERY")
|
|
||||||
|
|
||||||
|
|
||||||
class CeleryAppConfig(AppConfig):
|
|
||||||
name = "{{cookiecutter.project_slug}}.taskapp"
|
|
||||||
verbose_name = "Celery Config"
|
|
||||||
|
|
||||||
def ready(self):
|
|
||||||
installed_apps = [app_config.name for app_config in apps.get_app_configs()]
|
|
||||||
app.autodiscover_tasks(lambda: installed_apps, force=True)
|
|
||||||
|
|
||||||
|
|
||||||
@app.task(bind=True)
|
|
||||||
def debug_task(self):
|
|
||||||
print(f"Request: {self.request!r}") # pragma: no cover
|
|
||||||
{% else %}
|
|
||||||
# Use this as a starting point for your project with celery.
|
|
||||||
# If you are not using celery, you can remove this app
|
|
||||||
{% endif -%}
|
|
|
@ -1,10 +1,10 @@
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
class UsersAppConfig(AppConfig):
|
class UsersConfig(AppConfig):
|
||||||
|
|
||||||
name = "{{ cookiecutter.project_slug }}.users"
|
name = "{{ cookiecutter.project_slug }}.users"
|
||||||
verbose_name = "Users"
|
verbose_name = _("Users")
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
from config import celery_app
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
@celery_app.task()
|
||||||
|
def get_users_count():
|
||||||
|
"""A pointless Celery task to demonstrate usage."""
|
||||||
|
return User.objects.count()
|
|
@ -0,0 +1,16 @@
|
||||||
|
import pytest
|
||||||
|
from celery.result import EagerResult
|
||||||
|
|
||||||
|
|
||||||
|
from {{ cookiecutter.project_slug }}.users.tasks import get_users_count
|
||||||
|
from {{ cookiecutter.project_slug }}.users.tests.factories import UserFactory
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_user_count(settings):
|
||||||
|
"""A basic test to execute the get_users_count Celery task."""
|
||||||
|
UserFactory.create_batch(3)
|
||||||
|
settings.CELERY_TASK_ALWAYS_EAGER = True
|
||||||
|
task_result = get_users_count.delay()
|
||||||
|
assert isinstance(task_result, EagerResult)
|
||||||
|
assert task_result.result == 3
|
|
@ -13,11 +13,6 @@ def test_detail(user: settings.AUTH_USER_MODEL):
|
||||||
assert resolve(f"/users/{user.username}/").view_name == "users:detail"
|
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():
|
def test_update():
|
||||||
assert reverse("users:update") == "/users/~update/"
|
assert reverse("users:update") == "/users/~update/"
|
||||||
assert resolve("/users/~update/").view_name == "users:update"
|
assert resolve("/users/~update/").view_name == "users:update"
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from {{ cookiecutter.project_slug }}.users.views import (
|
from {{ cookiecutter.project_slug }}.users.views import (
|
||||||
user_list_view,
|
|
||||||
user_redirect_view,
|
user_redirect_view,
|
||||||
user_update_view,
|
user_update_view,
|
||||||
user_detail_view,
|
user_detail_view,
|
||||||
|
@ -9,7 +8,6 @@ from {{ cookiecutter.project_slug }}.users.views import (
|
||||||
|
|
||||||
app_name = "users"
|
app_name = "users"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", view=user_list_view, name="list"),
|
|
||||||
path("~redirect/", view=user_redirect_view, name="redirect"),
|
path("~redirect/", view=user_redirect_view, name="redirect"),
|
||||||
path("~update/", view=user_update_view, name="update"),
|
path("~update/", view=user_update_view, name="update"),
|
||||||
path("<str:username>/", view=user_detail_view, name="detail"),
|
path("<str:username>/", view=user_detail_view, name="detail"),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.urls import reverse
|
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()
|
User = get_user_model()
|
||||||
|
|
||||||
|
@ -16,16 +16,6 @@ class UserDetailView(LoginRequiredMixin, DetailView):
|
||||||
user_detail_view = UserDetailView.as_view()
|
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):
|
class UserUpdateView(LoginRequiredMixin, UpdateView):
|
||||||
|
|
||||||
model = User
|
model = User
|
||||||
|
|
Loading…
Reference in New Issue
Block a user