mirror of
https://github.com/cookiecutter/cookiecutter-django.git
synced 2025-07-11 08:32:21 +03:00
Merge branch 'master' into pathlib-migration-updated
This commit is contained in:
commit
d2988040d5
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: pydanny
|
||||||
|
patreon: roygreenfeld
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
otechie: # Replace with a single Otechie username
|
||||||
|
custom: ['https://www.patreon.com/browniebroke']
|
|
@ -5,7 +5,7 @@ services:
|
||||||
|
|
||||||
language: python
|
language: python
|
||||||
|
|
||||||
python: 3.6
|
python: 3.7
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- docker-compose -v
|
- docker-compose -v
|
||||||
|
@ -14,11 +14,7 @@ before_install:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- name: Test results
|
- name: Test results
|
||||||
script: tox -e py36
|
script: tox -e py37
|
||||||
- name: Run flake8 on result
|
|
||||||
script: tox -e flake8
|
|
||||||
- name: Run black on result
|
|
||||||
script: tox -e black
|
|
||||||
- name: Black template
|
- name: Black template
|
||||||
script: tox -e black-template
|
script: tox -e black-template
|
||||||
- name: Basic Docker
|
- name: Basic Docker
|
||||||
|
|
64
CHANGELOG.md
64
CHANGELOG.md
|
@ -1,6 +1,68 @@
|
||||||
# Change Log
|
# Change Log
|
||||||
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/).
|
|
||||||
|
## [2020-01-23]
|
||||||
|
### Changed
|
||||||
|
- Fix UserFactory to set the password if provided (@BoPeng)
|
||||||
|
- Update documentation files with latest Sphinx (@howiezhao)
|
||||||
|
|
||||||
|
## [2020-01-12]
|
||||||
|
### Changed
|
||||||
|
- Fix mypy setup and added django-stubs (@danifus)
|
||||||
|
- Add Gitlab CI as option (@ikhomutov)
|
||||||
|
|
||||||
|
## [2020-01-11]
|
||||||
|
### Changed
|
||||||
|
- Speed up & reduce size for production Django image (@maxp)
|
||||||
|
- Bumped runtime version for Heroku (@Isaac12x)
|
||||||
|
- Added Debian 10 (Buster) OS dependencies (@ddiazpinto)
|
||||||
|
- Update Traefik to v2 (@blaxpy)
|
||||||
|
- Switched Docker images from Alpine based to Debian based (@trungdong)
|
||||||
|
|
||||||
|
## [2019-10-06]
|
||||||
|
### Changed
|
||||||
|
- Default Python version is now 3.7 (@nicolas471)
|
||||||
|
|
||||||
|
## [2019-10-04]
|
||||||
|
### Fixed
|
||||||
|
- Fix static files handling on GCP (@caioariede)
|
||||||
|
|
||||||
|
## [2019-10-03]
|
||||||
|
### Fixed
|
||||||
|
- Fix incompatible combination between Whitenoise and no cloud provider (@caioariede)
|
||||||
|
|
||||||
|
## [2019-07-09]
|
||||||
|
### Fixed
|
||||||
|
- Always use test settings in pytest (@danihodovic)
|
||||||
|
- Remove gunicorn from `INSTALLED_APPS` (@danihodovic)
|
||||||
|
- Remove `EMAIL_HOST` and `EMAIL_PORT` with locmem backend (@danihodovic)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Add `EMAIL_TIMEOUT` (@danihodovic)
|
||||||
|
|
||||||
|
## [2019-06-22]
|
||||||
|
### Fixed
|
||||||
|
- Remove redundant template debug setting (@danihodovic)
|
||||||
|
|
||||||
|
## [2019-06-19]
|
||||||
|
### Fixed
|
||||||
|
- Fix removal carriage returns in docker scripts (@timclaessens)
|
||||||
|
|
||||||
|
## [2019-06-15]
|
||||||
|
### Fixed
|
||||||
|
- Issue with Pycharm setup for running things in Docker compose (@foarsitter)
|
||||||
|
|
||||||
|
## [2019-06-06]
|
||||||
|
### Changed
|
||||||
|
- Update generated Travis config (@browniebroke)
|
||||||
|
|
||||||
|
## [2019-06-03]
|
||||||
|
### Added
|
||||||
|
- Installed `django-celery-beat` to keep scheduled tasks in DB (@keyvanm)
|
||||||
|
|
||||||
|
## [2019-05-28]
|
||||||
|
### Changed
|
||||||
|
- Use GCP acronym rather than inconsistent GCE/GCS (@tanoabeleyra)
|
||||||
|
|
||||||
## [2019-05-27]
|
## [2019-05-27]
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
@ -39,9 +39,9 @@ To run all tests using various versions of python in virtualenvs defined in tox.
|
||||||
It is possible to test with a specific version of python. To do this, the command
|
It is possible to test with a specific version of python. To do this, the command
|
||||||
is::
|
is::
|
||||||
|
|
||||||
$ tox -e py36
|
$ tox -e py37
|
||||||
|
|
||||||
This will run py.test with the python3.6 interpreter, for example.
|
This will run py.test with the python3.7 interpreter, for example.
|
||||||
|
|
||||||
To run a particular test with tox for against your current Python version::
|
To run a particular test with tox for against your current Python version::
|
||||||
|
|
||||||
|
|
|
@ -42,12 +42,14 @@ Listed in alphabetical order.
|
||||||
Name Github Twitter
|
Name Github Twitter
|
||||||
========================== ============================ ==============
|
========================== ============================ ==============
|
||||||
18 `@dezoito`_
|
18 `@dezoito`_
|
||||||
|
2O4 `@2O4`_
|
||||||
a7p `@a7p`_
|
a7p `@a7p`_
|
||||||
Aaron Eikenberry `@aeikenberry`_
|
Aaron Eikenberry `@aeikenberry`_
|
||||||
Adam Bogdał `@bogdal`_
|
Adam Bogdał `@bogdal`_
|
||||||
Adam Dobrawy `@ad-m`_
|
Adam Dobrawy `@ad-m`_
|
||||||
Adam Steele `@adammsteele`_
|
Adam Steele `@adammsteele`_
|
||||||
Agam Dua
|
Agam Dua
|
||||||
|
Agustín Scaramuzza `@scaramagus`_ @scaramagus
|
||||||
Alberto Sanchez `@alb3rto`_
|
Alberto Sanchez `@alb3rto`_
|
||||||
Alex Tsai `@caffodian`_
|
Alex Tsai `@caffodian`_
|
||||||
Alvaro [Andor] `@andor-pierdelacabeza`_
|
Alvaro [Andor] `@andor-pierdelacabeza`_
|
||||||
|
@ -55,6 +57,7 @@ Listed in alphabetical order.
|
||||||
Andreas Meistad `@ameistad`_
|
Andreas Meistad `@ameistad`_
|
||||||
Andres Gonzalez `@andresgz`_
|
Andres Gonzalez `@andresgz`_
|
||||||
Andrew Mikhnevich `@zcho`_
|
Andrew Mikhnevich `@zcho`_
|
||||||
|
Andrew Chen Wang `@Andrew-Chen-Wang`_
|
||||||
Andy Rose
|
Andy Rose
|
||||||
Anna Callahan `@jazztpt`_
|
Anna Callahan `@jazztpt`_
|
||||||
Anna Sidwell `@takkaria`_
|
Anna Sidwell `@takkaria`_
|
||||||
|
@ -70,9 +73,12 @@ 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`_
|
||||||
Burhan Khalid `@burhan`_ @burhan
|
Burhan Khalid `@burhan`_ @burhan
|
||||||
|
Caio Ariede `@caioariede`_ @caioariede
|
||||||
Carl Johnson `@carlmjohnson`_ @carlmjohnson
|
Carl Johnson `@carlmjohnson`_ @carlmjohnson
|
||||||
Catherine Devlin `@catherinedevlin`_
|
Catherine Devlin `@catherinedevlin`_
|
||||||
Cédric Gaspoz `@cgaspoz`_
|
Cédric Gaspoz `@cgaspoz`_
|
||||||
|
@ -83,14 +89,16 @@ Listed in alphabetical order.
|
||||||
Chris Pappalardo `@ChrisPappalardo`_
|
Chris Pappalardo `@ChrisPappalardo`_
|
||||||
Christopher Clarke `@chrisdev`_
|
Christopher Clarke `@chrisdev`_
|
||||||
Cole Mackenzie `@cmackenzie1`_
|
Cole Mackenzie `@cmackenzie1`_
|
||||||
|
Cole Maclean `@cole`_ @cole
|
||||||
Collederas `@Collederas`_
|
Collederas `@Collederas`_
|
||||||
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
|
Curtis St Pierre `@curtisstpierre`_ @cstpierre1388
|
||||||
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`_
|
||||||
|
@ -99,6 +107,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`_
|
||||||
|
@ -107,17 +116,26 @@ Listed in alphabetical order.
|
||||||
Eric Groom `@ericgroom`_
|
Eric Groom `@ericgroom`_
|
||||||
Eyad Al Sibai `@eyadsibai`_
|
Eyad Al Sibai `@eyadsibai`_
|
||||||
Felipe Arruda `@arruda`_
|
Felipe Arruda `@arruda`_
|
||||||
|
Florian Idelberger `@step21`_ @windrush
|
||||||
Garry Cairns `@garry-cairns`_
|
Garry Cairns `@garry-cairns`_
|
||||||
Garry Polley `@garrypolley`_
|
Garry Polley `@garrypolley`_
|
||||||
|
Gilbishkosma `@Gilbishkosma`_
|
||||||
|
Guilherme Guy `@guilherme1guy`_
|
||||||
Hamish Durkin `@durkode`_
|
Hamish Durkin `@durkode`_
|
||||||
Hana Quadara `@hanaquadara`_
|
Hana Quadara `@hanaquadara`_
|
||||||
|
Harry Moreno `@morenoh149`_ @morenoh149
|
||||||
Harry Percival `@hjwp`_
|
Harry Percival `@hjwp`_
|
||||||
Hendrik Schneider `@hendrikschneider`_
|
Hendrik Schneider `@hendrikschneider`_
|
||||||
Henrique G. G. Pereira `@ikkebr`_
|
Henrique G. G. Pereira `@ikkebr`_
|
||||||
|
Howie Zhao `@howiezhao`_
|
||||||
Ian Lee `@IanLee1521`_
|
Ian Lee `@IanLee1521`_
|
||||||
Irfan Ahmad `@erfaan`_ @erfaan
|
Irfan Ahmad `@erfaan`_ @erfaan
|
||||||
|
Isaac12x `@Isaac12x`_
|
||||||
|
Ivan Khomutov `@ikhomutov`_
|
||||||
|
James Williams `@jameswilliams1`_
|
||||||
Jan Van Bruggen `@jvanbrug`_
|
Jan Van Bruggen `@jvanbrug`_
|
||||||
Jelmer Draaijer `@foarsitter`_
|
Jelmer Draaijer `@foarsitter`_
|
||||||
|
Jerome Caisip `@jeromecaisip`_
|
||||||
Jens Nilsson `@phiberjenz`_
|
Jens Nilsson `@phiberjenz`_
|
||||||
Jerome Leclanche `@jleclanche`_ @Adys
|
Jerome Leclanche `@jleclanche`_ @Adys
|
||||||
Jimmy Gitonga `@afrowave`_ @afrowave
|
Jimmy Gitonga `@afrowave`_ @afrowave
|
||||||
|
@ -135,6 +153,7 @@ Listed in alphabetical order.
|
||||||
Keyvan Mosharraf `@keyvanm`_
|
Keyvan Mosharraf `@keyvanm`_
|
||||||
Krzysztof Szumny `@noisy`_
|
Krzysztof Szumny `@noisy`_
|
||||||
Krzysztof Żuraw `@krzysztofzuraw`_
|
Krzysztof Żuraw `@krzysztofzuraw`_
|
||||||
|
Leo won `@leollon`_
|
||||||
Leo Zhou `@glasslion`_
|
Leo Zhou `@glasslion`_
|
||||||
Leonardo Jimenez `@xpostudio4`_
|
Leonardo Jimenez `@xpostudio4`_
|
||||||
Lin Xianyi `@iynaix`_
|
Lin Xianyi `@iynaix`_
|
||||||
|
@ -155,11 +174,14 @@ Listed in alphabetical order.
|
||||||
Meghan Heintz `@dot2dotseurat`_
|
Meghan Heintz `@dot2dotseurat`_
|
||||||
Mesut Yılmaz `@myilmaz`_
|
Mesut Yılmaz `@myilmaz`_
|
||||||
Michael Gecht `@mimischi`_ @_mischi
|
Michael Gecht `@mimischi`_ @_mischi
|
||||||
|
Michael Samoylov `@msamoylov`_
|
||||||
Min ho Kim `@minho42`_
|
Min ho Kim `@minho42`_
|
||||||
mozillazg `@mozillazg`_
|
mozillazg `@mozillazg`_
|
||||||
|
Nico Stefani `@nicolas471`_ @moby_dick91
|
||||||
Oleg Russkin `@rolep`_
|
Oleg Russkin `@rolep`_
|
||||||
Pablo `@oubiga`_
|
Pablo `@oubiga`_
|
||||||
Parbhat Puri `@parbhat`_
|
Parbhat Puri `@parbhat`_
|
||||||
|
Pawan Chaurasia `@rjsnh1522`_
|
||||||
Peter Bittner `@bittner`_
|
Peter Bittner `@bittner`_
|
||||||
Peter Coles `@mrcoles`_
|
Peter Coles `@mrcoles`_
|
||||||
Philipp Matthies `@canonnervio`_
|
Philipp Matthies `@canonnervio`_
|
||||||
|
@ -190,6 +212,7 @@ Listed in alphabetical order.
|
||||||
Tubo Shi `@Tubo`_
|
Tubo Shi `@Tubo`_
|
||||||
Umair Ashraf `@umrashrf`_ @fabumair
|
Umair Ashraf `@umrashrf`_ @fabumair
|
||||||
Vadim Iskuchekov `@Egregors`_ @egregors
|
Vadim Iskuchekov `@Egregors`_ @egregors
|
||||||
|
Vicente G. Reyes `@reyesvicente`_ @highcenburg
|
||||||
Vitaly Babiy
|
Vitaly Babiy
|
||||||
Vivian Guillen `@viviangb`_
|
Vivian Guillen `@viviangb`_
|
||||||
Vlad Doster `@vladdoster`_
|
Vlad Doster `@vladdoster`_
|
||||||
|
@ -197,9 +220,11 @@ Listed in alphabetical order.
|
||||||
William Archinal `@archinal`_
|
William Archinal `@archinal`_
|
||||||
Xaver Y.R. Chen `@yrchen`_ @yrchen
|
Xaver Y.R. Chen `@yrchen`_ @yrchen
|
||||||
Yaroslav Halchenko
|
Yaroslav Halchenko
|
||||||
|
Yuchen Xie `@mapx`_
|
||||||
========================== ============================ ==============
|
========================== ============================ ==============
|
||||||
|
|
||||||
.. _@a7p: https://github.com/a7p
|
.. _@a7p: https://github.com/a7p
|
||||||
|
.. _@2O4: https://github.com/2O4
|
||||||
.. _@ad-m: https://github.com/ad-m
|
.. _@ad-m: https://github.com/ad-m
|
||||||
.. _@adammsteele: https://github.com/adammsteele
|
.. _@adammsteele: https://github.com/adammsteele
|
||||||
.. _@aeikenberry: https://github.com/aeikenberry
|
.. _@aeikenberry: https://github.com/aeikenberry
|
||||||
|
@ -211,15 +236,19 @@ Listed in alphabetical order.
|
||||||
.. _@andor-pierdelacabeza: https://github.com/andor-pierdelacabeza
|
.. _@andor-pierdelacabeza: https://github.com/andor-pierdelacabeza
|
||||||
.. _@andresgz: https://github.com/andresgz
|
.. _@andresgz: https://github.com/andresgz
|
||||||
.. _@antoniablair: https://github.com/antoniablair
|
.. _@antoniablair: https://github.com/antoniablair
|
||||||
|
.. _@Andrew-Chen-Wang: https://github.com/Andrew-Chen-Wang
|
||||||
.. _@apirobot: https://github.com/apirobot
|
.. _@apirobot: https://github.com/apirobot
|
||||||
.. _@archinal: https://github.com/archinal
|
.. _@archinal: https://github.com/archinal
|
||||||
.. _@areski: https://github.com/areski
|
.. _@areski: https://github.com/areski
|
||||||
.. _@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
|
||||||
|
.. _@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
|
||||||
|
@ -227,6 +256,7 @@ Listed in alphabetical order.
|
||||||
.. _@c-rhodes: https://github.com/c-rhodes
|
.. _@c-rhodes: https://github.com/c-rhodes
|
||||||
.. _@caffodian: https://github.com/caffodian
|
.. _@caffodian: https://github.com/caffodian
|
||||||
.. _@canonnervio: https://github.com/canonnervio
|
.. _@canonnervio: https://github.com/canonnervio
|
||||||
|
.. _@caioariede: https://github.com/caioariede
|
||||||
.. _@carlmjohnson: https://github.com/carlmjohnson
|
.. _@carlmjohnson: https://github.com/carlmjohnson
|
||||||
.. _@catherinedevlin: https://github.com/catherinedevlin
|
.. _@catherinedevlin: https://github.com/catherinedevlin
|
||||||
.. _@ccurvey: https://github.com/ccurvey
|
.. _@ccurvey: https://github.com/ccurvey
|
||||||
|
@ -237,10 +267,12 @@ Listed in alphabetical order.
|
||||||
.. _@chuckus: https://github.com/chuckus
|
.. _@chuckus: https://github.com/chuckus
|
||||||
.. _@cmackenzie1: https://github.com/cmackenzie1
|
.. _@cmackenzie1: https://github.com/cmackenzie1
|
||||||
.. _@cmargieson: https://github.com/cmargieson
|
.. _@cmargieson: https://github.com/cmargieson
|
||||||
|
.. _@cole: https://github.com/cole
|
||||||
.. _@Collederas: https://github.com/Collederas
|
.. _@Collederas: https://github.com/Collederas
|
||||||
.. _@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
|
||||||
|
@ -249,6 +281,7 @@ Listed in alphabetical order.
|
||||||
.. _@dhepper: https://github.com/dhepper
|
.. _@dhepper: https://github.com/dhepper
|
||||||
.. _@dot2dotseurat: https://github.com/dot2dotseurat
|
.. _@dot2dotseurat: https://github.com/dot2dotseurat
|
||||||
.. _@dsclementsen: https://github.com/dsclementsen
|
.. _@dsclementsen: https://github.com/dsclementsen
|
||||||
|
.. _@guilherme1guy: https://github.com/guilherme1guy
|
||||||
.. _@durkode: https://github.com/durkode
|
.. _@durkode: https://github.com/durkode
|
||||||
.. _@Egregors: https://github.com/Egregors
|
.. _@Egregors: https://github.com/Egregors
|
||||||
.. _@epileptic-fish: https://gihub.com/epileptic-fish
|
.. _@epileptic-fish: https://gihub.com/epileptic-fish
|
||||||
|
@ -261,6 +294,7 @@ Listed in alphabetical order.
|
||||||
.. _@foarsitter: https://github.com/foarsitter
|
.. _@foarsitter: https://github.com/foarsitter
|
||||||
.. _@garry-cairns: https://github.com/garry-cairns
|
.. _@garry-cairns: https://github.com/garry-cairns
|
||||||
.. _@garrypolley: https://github.com/garrypolley
|
.. _@garrypolley: https://github.com/garrypolley
|
||||||
|
.. _@Gilbishkosma: https://github.com/Gilbishkosma
|
||||||
.. _@glasslion: https://github.com/glasslion
|
.. _@glasslion: https://github.com/glasslion
|
||||||
.. _@goldhand: https://github.com/goldhand
|
.. _@goldhand: https://github.com/goldhand
|
||||||
.. _@hackebrot: https://github.com/hackebrot
|
.. _@hackebrot: https://github.com/hackebrot
|
||||||
|
@ -268,12 +302,17 @@ Listed in alphabetical order.
|
||||||
.. _@hanaquadara: https://github.com/hanaquadara
|
.. _@hanaquadara: https://github.com/hanaquadara
|
||||||
.. _@hendrikschneider: https://github.com/hendrikschneider
|
.. _@hendrikschneider: https://github.com/hendrikschneider
|
||||||
.. _@hjwp: https://github.com/hjwp
|
.. _@hjwp: https://github.com/hjwp
|
||||||
|
.. _@howiezhao: https://github.com/howiezhao
|
||||||
.. _@IanLee1521: https://github.com/IanLee1521
|
.. _@IanLee1521: https://github.com/IanLee1521
|
||||||
|
.. _@ikhomutov: https://github.com/ikhomutov
|
||||||
|
.. _@jameswilliams1: https://github.com/jameswilliams1
|
||||||
.. _@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
|
||||||
.. _@jcass77: https://github.com/jcass77
|
.. _@jcass77: https://github.com/jcass77
|
||||||
|
.. _@jeromecaisip: https://github.com/jeromecaisip
|
||||||
.. _@jleclanche: https://github.com/jleclanche
|
.. _@jleclanche: https://github.com/jleclanche
|
||||||
.. _@jules-ch: https://github.com/jules-ch
|
.. _@jules-ch: https://github.com/jules-ch
|
||||||
.. _@juliocc: https://github.com/juliocc
|
.. _@juliocc: https://github.com/juliocc
|
||||||
|
@ -286,7 +325,9 @@ Listed in alphabetical order.
|
||||||
.. _@keyvanm: https://github.com/keyvanm
|
.. _@keyvanm: https://github.com/keyvanm
|
||||||
.. _@knitatoms: https://github.com/knitatoms
|
.. _@knitatoms: https://github.com/knitatoms
|
||||||
.. _@krzysztofzuraw: https://github.com/krzysztofzuraw
|
.. _@krzysztofzuraw: https://github.com/krzysztofzuraw
|
||||||
|
.. _@leollon: https://github.com/leollon
|
||||||
.. _@MathijsHoogland: https://github.com/MathijsHoogland
|
.. _@MathijsHoogland: https://github.com/MathijsHoogland
|
||||||
|
.. _@mapx: https://github.com/mapx
|
||||||
.. _@mattayes: https://github.com/mattayes
|
.. _@mattayes: https://github.com/mattayes
|
||||||
.. _@menzenski: https://github.com/menzenski
|
.. _@menzenski: https://github.com/menzenski
|
||||||
.. _@mfwarren: https://github.com/mfwarren
|
.. _@mfwarren: https://github.com/mfwarren
|
||||||
|
@ -295,24 +336,30 @@ Listed in alphabetical order.
|
||||||
.. _@minho42: https://github.com/minho42
|
.. _@minho42: https://github.com/minho42
|
||||||
.. _@mjsisley: https://github.com/mjsisley
|
.. _@mjsisley: https://github.com/mjsisley
|
||||||
.. _@mknapper1: https://github.com/mknapper1
|
.. _@mknapper1: https://github.com/mknapper1
|
||||||
|
.. _@morenoh149: https://github.com/morenoh149
|
||||||
.. _@mostaszewski: https://github.com/mostaszewski
|
.. _@mostaszewski: https://github.com/mostaszewski
|
||||||
.. _@mozillazg: https://github.com/mozillazg
|
.. _@mozillazg: https://github.com/mozillazg
|
||||||
.. _@mrcoles: https://github.com/mrcoles
|
.. _@mrcoles: https://github.com/mrcoles
|
||||||
.. _@msaizar: https://github.com/msaizar
|
.. _@msaizar: https://github.com/msaizar
|
||||||
|
.. _@msamoylov: https://github.com/msamoylov
|
||||||
.. _@myilmaz: https://github.com/myilmaz
|
.. _@myilmaz: https://github.com/myilmaz
|
||||||
|
.. _@nicolas471: https://github.com/nicolas471
|
||||||
.. _@noisy: https://github.com/noisy
|
.. _@noisy: https://github.com/noisy
|
||||||
.. _@originell: https://github.com/originell
|
.. _@originell: https://github.com/originell
|
||||||
.. _@oubiga: https://github.com/oubiga
|
.. _@oubiga: https://github.com/oubiga
|
||||||
.. _@parbhat: https://github.com/parbhat
|
.. _@parbhat: https://github.com/parbhat
|
||||||
|
.. _@rjsnh1522: https://github.com/rjsnh1522
|
||||||
.. _@pchiquet: https://github.com/pchiquet
|
.. _@pchiquet: https://github.com/pchiquet
|
||||||
.. _@phiberjenz: https://github.com/phiberjenz
|
.. _@phiberjenz: https://github.com/phiberjenz
|
||||||
.. _@purplediane: https://github.com/purplediane
|
.. _@purplediane: https://github.com/purplediane
|
||||||
.. _@raonyguimaraes: https://github.com/raonyguimaraes
|
.. _@raonyguimaraes: https://github.com/raonyguimaraes
|
||||||
.. _@reggieriser: https://github.com/reggieriser
|
.. _@reggieriser: https://github.com/reggieriser
|
||||||
|
.. _@reyesvicente: https://github.com/reyesvicente
|
||||||
.. _@rm--: https://github.com/rm--
|
.. _@rm--: https://github.com/rm--
|
||||||
.. _@rolep: https://github.com/rolep
|
.. _@rolep: https://github.com/rolep
|
||||||
.. _@romanosipenko: https://github.com/romanosipenko
|
.. _@romanosipenko: https://github.com/romanosipenko
|
||||||
.. _@saschalalala: https://github.com/saschalalala
|
.. _@saschalalala: https://github.com/saschalalala
|
||||||
|
.. _@scaramagus: https://github.com/scaramagus
|
||||||
.. _@shireenrao: https://github.com/shireenrao
|
.. _@shireenrao: https://github.com/shireenrao
|
||||||
.. _@show0k: https://github.com/show0k
|
.. _@show0k: https://github.com/show0k
|
||||||
.. _@shultz: https://github.com/shultz
|
.. _@shultz: https://github.com/shultz
|
||||||
|
@ -320,6 +367,7 @@ Listed in alphabetical order.
|
||||||
.. _@sladinji: https://github.com/sladinji
|
.. _@sladinji: https://github.com/sladinji
|
||||||
.. _@slafs: https://github.com/slafs
|
.. _@slafs: https://github.com/slafs
|
||||||
.. _@ssteinerX: https://github.com/ssteinerx
|
.. _@ssteinerX: https://github.com/ssteinerx
|
||||||
|
.. _@step21: https://github.com/step21
|
||||||
.. _@stepmr: https://github.com/stepmr
|
.. _@stepmr: https://github.com/stepmr
|
||||||
.. _@suledev: https://github.com/suledev
|
.. _@suledev: https://github.com/suledev
|
||||||
.. _@takkaria: https://github.com/takkaria
|
.. _@takkaria: https://github.com/takkaria
|
||||||
|
|
28
README.rst
28
README.rst
|
@ -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
|
||||||
|
@ -37,7 +37,7 @@ Features
|
||||||
---------
|
---------
|
||||||
|
|
||||||
* For Django 2.2
|
* For Django 2.2
|
||||||
* Works with Python 3.6
|
* Works with Python 3.7
|
||||||
* 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)
|
||||||
* 12-Factor_ based settings via django-environ_
|
* 12-Factor_ based settings via django-environ_
|
||||||
|
@ -46,13 +46,14 @@ Features
|
||||||
* Registration via django-allauth_
|
* Registration via django-allauth_
|
||||||
* Comes with custom user model ready to go
|
* Comes with custom user model ready to go
|
||||||
* Optional custom static build using Gulp and livereload
|
* Optional custom static build using Gulp and livereload
|
||||||
* Send emails via Anymail_ (using Mailgun_ by default, but switchable)
|
* Send emails via Anymail_ (using Mailgun_ by default or Amazon SES if AWS is selected cloud provider, but switchable)
|
||||||
* Media storage using Amazon S3 or Google Cloud Storage
|
* Media storage using Amazon S3 or Google Cloud Storage
|
||||||
* Docker support using docker-compose_ for development and production (using Traefik_ with LetsEncrypt_ support)
|
* Docker support using docker-compose_ for development and production (using Traefik_ with LetsEncrypt_ support)
|
||||||
* Procfile_ for deploying to Heroku
|
* Procfile_ for deploying to Heroku
|
||||||
* Instructions for deploying to PythonAnywhere_
|
* Instructions for deploying to PythonAnywhere_
|
||||||
* Run tests with unittest or pytest
|
* Run tests with unittest or pytest
|
||||||
* Customizable PostgreSQL version
|
* Customizable PostgreSQL version
|
||||||
|
* Default integration with pre-commit_ for identifying simple issues before submission to code review
|
||||||
|
|
||||||
.. _`maintained Foundation fork`: https://github.com/Parbhat/cookiecutter-django-foundation
|
.. _`maintained Foundation fork`: https://github.com/Parbhat/cookiecutter-django-foundation
|
||||||
|
|
||||||
|
@ -84,6 +85,7 @@ Optional Integrations
|
||||||
.. _PythonAnywhere: https://www.pythonanywhere.com/
|
.. _PythonAnywhere: https://www.pythonanywhere.com/
|
||||||
.. _Traefik: https://traefik.io/
|
.. _Traefik: https://traefik.io/
|
||||||
.. _LetsEncrypt: https://letsencrypt.org/
|
.. _LetsEncrypt: https://letsencrypt.org/
|
||||||
|
.. _pre-commit: https://github.com/pre-commit/pre-commit
|
||||||
|
|
||||||
Constraints
|
Constraints
|
||||||
-----------
|
-----------
|
||||||
|
@ -103,16 +105,16 @@ This project is run by volunteers. Please support them in their efforts to maint
|
||||||
|
|
||||||
Projects that provide financial support to the maintainers:
|
Projects that provide financial support to the maintainers:
|
||||||
|
|
||||||
Two Scoops of Django 1.11
|
Django Crash Course
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. image:: https://cdn.shopify.com/s/files/1/0304/6901/products/tsd-111-alpha_medium.jpg?v=1499531513
|
.. image:: https://cdn.shopify.com/s/files/1/0304/6901/files/Django-Crash-Course-300x436.jpg
|
||||||
:name: Two Scoops of Django 1.11 Cover
|
:name: Django Crash Course: Covers Django 3.0 and Python 3.8
|
||||||
:align: center
|
:align: center
|
||||||
:alt: Two Scoops of Django
|
:alt: Django Crash Course
|
||||||
:target: http://twoscoopspress.com/products/two-scoops-of-django-1-11
|
:target: https://www.roygreenfeld.com/products/django-crash-course
|
||||||
|
|
||||||
Two Scoops of Django is the best dessert-themed Django reference in the universe
|
Django Crash Course for Django 3.0 and Python 3.8 is the best cheese-themed Django reference in the universe!
|
||||||
|
|
||||||
pyup
|
pyup
|
||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -133,7 +135,7 @@ and then editing the results to include your name, email, and various configurat
|
||||||
|
|
||||||
First, get Cookiecutter. Trust me, it's awesome::
|
First, get Cookiecutter. Trust me, it's awesome::
|
||||||
|
|
||||||
$ pip install "cookiecutter>=1.4.0"
|
$ pip install "cookiecutter>=1.7.0"
|
||||||
|
|
||||||
Now run it against this repo::
|
Now run it against this repo::
|
||||||
|
|
||||||
|
@ -224,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
|
||||||
--------------------------------------------
|
--------------------------------------------
|
||||||
|
|
|
@ -33,6 +33,18 @@
|
||||||
"GCP",
|
"GCP",
|
||||||
"None"
|
"None"
|
||||||
],
|
],
|
||||||
|
"mail_service": [
|
||||||
|
"Mailgun",
|
||||||
|
"Amazon SES",
|
||||||
|
"Mailjet",
|
||||||
|
"Mandrill",
|
||||||
|
"Postmark",
|
||||||
|
"Sendgrid",
|
||||||
|
"SendinBlue",
|
||||||
|
"SparkPost",
|
||||||
|
"Other SMTP"
|
||||||
|
],
|
||||||
|
"use_drf": "n",
|
||||||
"custom_bootstrap_compilation": "n",
|
"custom_bootstrap_compilation": "n",
|
||||||
"use_compressor": "n",
|
"use_compressor": "n",
|
||||||
"use_celery": "n",
|
"use_celery": "n",
|
||||||
|
@ -40,7 +52,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"
|
||||||
|
|
|
@ -35,7 +35,7 @@ Make sure your project is fully committed and pushed up to Bitbucket or Github o
|
||||||
|
|
||||||
git clone <my-repo-url> # you can also use hg
|
git clone <my-repo-url> # you can also use hg
|
||||||
cd my-project-name
|
cd my-project-name
|
||||||
mkvirtualenv --python=/usr/bin/python3.6 my-project-name
|
mkvirtualenv --python=/usr/bin/python3.7 my-project-name
|
||||||
pip install -r requirements/production.txt # may take a few minutes
|
pip install -r requirements/production.txt # may take a few minutes
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,9 @@ Provided you have opted for Celery (via setting ``use_celery`` to ``y``) there a
|
||||||
|
|
||||||
* ``celeryworker`` running a Celery worker process;
|
* ``celeryworker`` running a Celery worker process;
|
||||||
* ``celerybeat`` running a Celery beat process;
|
* ``celerybeat`` running a Celery beat process;
|
||||||
* ``flower`` running Flower_ (for more info, check out :ref:`CeleryFlower` instructions for local environment).
|
* ``flower`` running Flower_.
|
||||||
|
|
||||||
|
The ``flower`` service is served by Traefik over HTTPS, through the port ``5555``. For more information about Flower and its login credentials, check out :ref:`CeleryFlower` instructions for local environment.
|
||||||
|
|
||||||
.. _`Flower`: https://github.com/mher/flower
|
.. _`Flower`: https://github.com/mher/flower
|
||||||
|
|
||||||
|
@ -152,6 +154,7 @@ If you are using ``supervisor``, you can use this file as a starting point::
|
||||||
Move it to ``/etc/supervisor/conf.d/{{cookiecutter.project_slug}}.conf`` and run::
|
Move it to ``/etc/supervisor/conf.d/{{cookiecutter.project_slug}}.conf`` and run::
|
||||||
|
|
||||||
supervisorctl reread
|
supervisorctl reread
|
||||||
|
supervisorctl update
|
||||||
supervisorctl start {{cookiecutter.project_slug}}
|
supervisorctl start {{cookiecutter.project_slug}}
|
||||||
|
|
||||||
For status check, run::
|
For status check, run::
|
||||||
|
|
|
@ -9,7 +9,7 @@ Setting Up Development Environment
|
||||||
|
|
||||||
Make sure to have the following on your host:
|
Make sure to have the following on your host:
|
||||||
|
|
||||||
* Python 3.6
|
* Python 3.7
|
||||||
* PostgreSQL_.
|
* PostgreSQL_.
|
||||||
* Redis_, if using Celery
|
* Redis_, if using Celery
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ First things first.
|
||||||
|
|
||||||
#. Create a virtualenv: ::
|
#. Create a virtualenv: ::
|
||||||
|
|
||||||
$ python3.6 -m venv <virtual env path>
|
$ python3.7 -m venv <virtual env path>
|
||||||
|
|
||||||
#. Activate the virtualenv you have just created: ::
|
#. Activate the virtualenv you have just created: ::
|
||||||
|
|
||||||
|
@ -26,6 +26,12 @@ First things first.
|
||||||
#. Install development requirements: ::
|
#. Install development requirements: ::
|
||||||
|
|
||||||
$ pip install -r requirements/local.txt
|
$ pip install -r requirements/local.txt
|
||||||
|
$ pre-commit install
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
the `pre-commit` exists in the generated project as default.
|
||||||
|
for the details of `pre-commit`, follow the [site of pre-commit](https://pre-commit.com/).
|
||||||
|
|
||||||
#. Create a new PostgreSQL database using createdb_: ::
|
#. Create a new PostgreSQL database using createdb_: ::
|
||||||
|
|
||||||
|
|
45
docs/document.rst
Normal file
45
docs/document.rst
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
.. _document:
|
||||||
|
|
||||||
|
Document
|
||||||
|
=========
|
||||||
|
|
||||||
|
This project uses Sphinx_ documentation generator.
|
||||||
|
After you have set up to `develop locally`_, run the following commands to generate the HTML documentation: ::
|
||||||
|
|
||||||
|
$ sphinx-build docs/ docs/_build/html/
|
||||||
|
|
||||||
|
If you set up your project to `develop locally with docker`_, run the following command: ::
|
||||||
|
|
||||||
|
$ docker-compose -f local.yml run --rm django sphinx-build docs/ docs/_build/html/
|
||||||
|
|
||||||
|
Generate API documentation
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
Sphinx can automatically generate documentation from docstrings, to enable this feature, follow these steps:
|
||||||
|
|
||||||
|
1. Add Sphinx extension in ``docs/conf.py`` file, like below: ::
|
||||||
|
|
||||||
|
extensions = [
|
||||||
|
'sphinx.ext.autodoc',
|
||||||
|
]
|
||||||
|
|
||||||
|
2. Uncomment the following lines in the ``docs/conf.py`` file: ::
|
||||||
|
|
||||||
|
# import django
|
||||||
|
# sys.path.insert(0, os.path.abspath('..'))
|
||||||
|
# os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
|
||||||
|
# django.setup()
|
||||||
|
|
||||||
|
3. Run the following command: ::
|
||||||
|
|
||||||
|
$ sphinx-apidoc -f -o ./docs/modules/ ./tpub/ migrations/*
|
||||||
|
|
||||||
|
If you set up your project to `develop locally with docker`_, run the following command: ::
|
||||||
|
|
||||||
|
$ docker-compose -f local.yml run --rm django sphinx-apidoc -f -o ./docs/modules ./tpub/ migrations/*
|
||||||
|
|
||||||
|
4. Regenerate HTML documentation as written above.
|
||||||
|
|
||||||
|
.. _Sphinx: https://www.sphinx-doc.org/en/master/index.html
|
||||||
|
.. _develop locally: ./developing-locally.html
|
||||||
|
.. _develop locally with docker: ./developing-locally-docker.html
|
|
@ -18,6 +18,7 @@ Contents:
|
||||||
settings
|
settings
|
||||||
linters
|
linters
|
||||||
testing
|
testing
|
||||||
|
document
|
||||||
deployment-on-pythonanywhere
|
deployment-on-pythonanywhere
|
||||||
deployment-on-heroku
|
deployment-on-heroku
|
||||||
deployment-with-docker
|
deployment-with-docker
|
||||||
|
|
|
@ -70,6 +70,22 @@ cloud_provider:
|
||||||
|
|
||||||
Note that if you choose no cloud provider, media files won't work.
|
Note that if you choose no cloud provider, media files won't work.
|
||||||
|
|
||||||
|
mail_service:
|
||||||
|
Select an email service that Django-Anymail provides
|
||||||
|
|
||||||
|
1. Amazon SES_
|
||||||
|
2. Mailgun_
|
||||||
|
3. Mailjet_
|
||||||
|
4. Mandrill_
|
||||||
|
5. Postmark_
|
||||||
|
6. SendGrid_
|
||||||
|
7. SendinBlue_
|
||||||
|
8. SparkPost_
|
||||||
|
9. Plain/Vanilla Django-Anymail_
|
||||||
|
|
||||||
|
use_drf:
|
||||||
|
Indicates whether the project should be configured to use `Django Rest Framework`_.
|
||||||
|
|
||||||
custom_bootstrap_compilation:
|
custom_bootstrap_compilation:
|
||||||
Indicates whether the project should support Bootstrap recompilation
|
Indicates whether the project should support Bootstrap recompilation
|
||||||
via the selected JavaScript task runner's task. This can be useful
|
via the selected JavaScript task runner's task. This can be useful
|
||||||
|
@ -94,8 +110,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
|
||||||
|
@ -125,6 +145,18 @@ debug:
|
||||||
.. _AWS: https://aws.amazon.com/s3/
|
.. _AWS: https://aws.amazon.com/s3/
|
||||||
.. _GCP: https://cloud.google.com/storage/
|
.. _GCP: https://cloud.google.com/storage/
|
||||||
|
|
||||||
|
.. _SES: https://aws.amazon.com/ses/
|
||||||
|
.. _Mailgun: https://www.mailgun.com
|
||||||
|
.. _Mailjet: https://www.mailjet.com
|
||||||
|
.. _Mandrill: http://mandrill.com
|
||||||
|
.. _Postmark: https://postmarkapp.com
|
||||||
|
.. _SendGrid: https://sendgrid.com
|
||||||
|
.. _SendinBlue: https://www.sendinblue.com
|
||||||
|
.. _SparkPost: https://www.sparkpost.com
|
||||||
|
.. _Django-Anymail: https://anymail.readthedocs.io/en/stable/
|
||||||
|
|
||||||
|
.. _Django Rest Framework: https://github.com/encode/django-rest-framework/
|
||||||
|
|
||||||
.. _Django Compressor: https://github.com/django-compressor/django-compressor
|
.. _Django Compressor: https://github.com/django-compressor/django-compressor
|
||||||
|
|
||||||
.. _Celery: https://github.com/celery/celery
|
.. _Celery: https://github.com/celery/celery
|
||||||
|
@ -138,3 +170,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/
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,21 @@ DJANGO_SENTRY_LOG_LEVEL SENTRY_LOG_LEVEL n/a
|
||||||
MAILGUN_API_KEY MAILGUN_API_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"
|
MAILGUN_API_URL n/a n/a "https://api.mailgun.net/v3"
|
||||||
|
MAILJET_API_KEY MAILJET_API_KEY n/a raises error
|
||||||
|
MAILJET_SECRET_KEY MAILJET_SECRET_KEY n/a raises error
|
||||||
|
MAILJET_API_URL n/a n/a "https://api.mailjet.com/v3"
|
||||||
|
MANDRILL_API_KEY MANDRILL_API_KEY n/a raises error
|
||||||
|
MANDRILL_API_URL n/a n/a "https://mandrillapp.com/api/1.0"
|
||||||
|
POSTMARK_SERVER_TOKEN POSTMARK_SERVER_TOKEN n/a raises error
|
||||||
|
POSTMARK_API_URL n/a n/a "https://api.postmarkapp.com/"
|
||||||
|
SENDGRID_API_KEY SENDGRID_API_KEY n/a raises error
|
||||||
|
SENDGRID_GENERATE_MESSAGE_ID True n/a raises error
|
||||||
|
SENDGRID_MERGE_FIELD_FORMAT None n/a raises error
|
||||||
|
SENDGRID_API_URL n/a n/a "https://api.sendgrid.com/v3/"
|
||||||
|
SENDINBLUE_API_KEY SENDINBLUE_API_KEY n/a raises error
|
||||||
|
SENDINBLUE_API_URL n/a n/a "https://api.sendinblue.com/v3/"
|
||||||
|
SPARKPOST_API_KEY SPARKPOST_API_KEY n/a raises error
|
||||||
|
SPARKPOST_API_URL n/a n/a "https://api.sparkpost.com/api/v1"
|
||||||
======================================= =========================== ============================================== ======================================================================
|
======================================= =========================== ============================================== ======================================================================
|
||||||
|
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
|
@ -19,7 +19,7 @@ You will get a readout of the `users` app that has already been set up with test
|
||||||
|
|
||||||
If you set up your project to `develop locally with docker`_, run the following command: ::
|
If you set up your project to `develop locally with docker`_, run the following command: ::
|
||||||
|
|
||||||
$ docker-compose -f local.yml run django pytest
|
$ docker-compose -f local.yml run --rm django pytest
|
||||||
|
|
||||||
Targeting particular apps for testing in ``docker`` follows a similar pattern as previously shown above.
|
Targeting particular apps for testing in ``docker`` follows a similar pattern as previously shown above.
|
||||||
|
|
||||||
|
@ -28,11 +28,11 @@ Coverage
|
||||||
|
|
||||||
You should build your tests to provide the highest level of **code coverage**. You can run the ``pytest`` with code ``coverage`` by typing in the following command: ::
|
You should build your tests to provide the highest level of **code coverage**. You can run the ``pytest`` with code ``coverage`` by typing in the following command: ::
|
||||||
|
|
||||||
$ docker-compose -f local.yml run django coverage run -m pytest
|
$ docker-compose -f local.yml run --rm django coverage run -m pytest
|
||||||
|
|
||||||
Once the tests are complete, in order to see the code coverage, run the following command: ::
|
Once the tests are complete, in order to see the code coverage, run the following command: ::
|
||||||
|
|
||||||
$ docker-compose -f local.yml run django coverage report
|
$ docker-compose -f local.yml run --rm django coverage report
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
@ -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
|
|
@ -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:
|
||||||
|
@ -279,6 +283,15 @@ def remove_node_dockerfile():
|
||||||
shutil.rmtree(os.path.join("compose", "local", "node"))
|
shutil.rmtree(os.path.join("compose", "local", "node"))
|
||||||
|
|
||||||
|
|
||||||
|
def remove_aws_dockerfile():
|
||||||
|
shutil.rmtree(os.path.join("compose", "production", "aws"))
|
||||||
|
|
||||||
|
|
||||||
|
def remove_drf_starter_files():
|
||||||
|
os.remove(os.path.join("config", "api_router.py"))
|
||||||
|
shutil.rmtree(os.path.join("{{cookiecutter.project_slug}}", "users", "api"))
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
debug = "{{ cookiecutter.debug }}".lower() == "y"
|
debug = "{{ cookiecutter.debug }}".lower() == "y"
|
||||||
|
|
||||||
|
@ -302,6 +315,12 @@ def main():
|
||||||
else:
|
else:
|
||||||
remove_docker_files()
|
remove_docker_files()
|
||||||
|
|
||||||
|
if (
|
||||||
|
"{{ cookiecutter.use_docker }}".lower() == "y"
|
||||||
|
and "{{ cookiecutter.cloud_provider}}".lower() != "aws"
|
||||||
|
):
|
||||||
|
remove_aws_dockerfile()
|
||||||
|
|
||||||
if "{{ cookiecutter.use_heroku }}".lower() == "n":
|
if "{{ cookiecutter.use_heroku }}".lower() == "n":
|
||||||
remove_heroku_files()
|
remove_heroku_files()
|
||||||
|
|
||||||
|
@ -339,9 +358,15 @@ 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()
|
||||||
|
|
||||||
|
if "{{ cookiecutter.use_drf }}".lower() == "n":
|
||||||
|
remove_drf_starter_files()
|
||||||
|
|
||||||
print(SUCCESS + "Project initialized, keep up the good work!" + TERMINATOR)
|
print(SUCCESS + "Project initialized, keep up the good work!" + TERMINATOR)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ if "{{ cookiecutter.use_docker }}".lower() == "n":
|
||||||
if python_major_version == 2:
|
if python_major_version == 2:
|
||||||
print(
|
print(
|
||||||
WARNING + "You're running cookiecutter under Python 2, but the generated "
|
WARNING + "You're running cookiecutter under Python 2, but the generated "
|
||||||
"project requires Python 3.6+. Do you want to proceed (y/n)? " + TERMINATOR
|
"project requires Python 3.7+. Do you want to proceed (y/n)? " + TERMINATOR
|
||||||
)
|
)
|
||||||
yes_options, no_options = frozenset(["y"]), frozenset(["n"])
|
yes_options, no_options = frozenset(["y"]), frozenset(["n"])
|
||||||
while True:
|
while True:
|
||||||
|
@ -59,3 +59,12 @@ if "{{ cookiecutter.use_docker }}".lower() == "n":
|
||||||
)
|
)
|
||||||
+ TERMINATOR
|
+ TERMINATOR
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
"{{ cookiecutter.use_whitenoise }}".lower() == "n"
|
||||||
|
and "{{ cookiecutter.cloud_provider }}" == "None"
|
||||||
|
):
|
||||||
|
print(
|
||||||
|
"You should either use Whitenoise or select a Cloud Provider to serve static files"
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
[pytest]
|
[pytest]
|
||||||
addopts = -x --tb=short
|
addopts = -v --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
|
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
cookiecutter==1.6.0
|
cookiecutter==1.7.0
|
||||||
sh==1.12.14
|
sh==1.12.14
|
||||||
binaryornot==0.4.4
|
binaryornot==0.4.4
|
||||||
|
|
||||||
# Code quality
|
# Code quality
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
black==19.3b0
|
black==19.10b0
|
||||||
flake8==3.7.8
|
flake8==3.7.9
|
||||||
|
flake8-isort==2.9.0
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
tox==3.13.2
|
tox==3.14.5
|
||||||
pytest==5.0.1
|
pytest==5.4.1
|
||||||
pytest_cases==1.10.1
|
pytest-cookies==0.5.1
|
||||||
pytest-cookies==0.4.0
|
pytest-instafail==0.4.1.post0
|
||||||
pytest-xdist==1.29.0
|
pyyaml==5.3
|
||||||
pyyaml==5.1.1
|
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -40,7 +40,7 @@ setup(
|
||||||
"License :: OSI Approved :: BSD License",
|
"License :: OSI Approved :: BSD License",
|
||||||
"Programming Language :: Python",
|
"Programming Language :: Python",
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"Programming Language :: Python :: 3.6",
|
"Programming Language :: Python :: 3.7",
|
||||||
"Programming Language :: Python :: Implementation :: CPython",
|
"Programming Language :: Python :: Implementation :: CPython",
|
||||||
"Topic :: Software Development",
|
"Topic :: Software Development",
|
||||||
],
|
],
|
||||||
|
|
|
@ -3,7 +3,6 @@ import re
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from cookiecutter.exceptions import FailedHookException
|
from cookiecutter.exceptions import FailedHookException
|
||||||
from pytest_cases import pytest_fixture_plus
|
|
||||||
import sh
|
import sh
|
||||||
import yaml
|
import yaml
|
||||||
from binaryornot.check import is_binary
|
from binaryornot.check import is_binary
|
||||||
|
@ -11,9 +10,6 @@ from binaryornot.check import is_binary
|
||||||
PATTERN = r"{{(\s?cookiecutter)[.](.*?)}}"
|
PATTERN = r"{{(\s?cookiecutter)[.](.*?)}}"
|
||||||
RE_OBJ = re.compile(PATTERN)
|
RE_OBJ = re.compile(PATTERN)
|
||||||
|
|
||||||
YN_CHOICES = ["y", "n"]
|
|
||||||
CLOUD_CHOICES = ["AWS", "GCE", "None"]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def context():
|
def context():
|
||||||
|
@ -29,36 +25,73 @@ def context():
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest_fixture_plus
|
SUPPORTED_COMBINATIONS = [
|
||||||
@pytest.mark.parametrize("windows", YN_CHOICES, ids=lambda yn: f"win:{yn}")
|
{"open_source_license": "MIT"},
|
||||||
@pytest.mark.parametrize("use_docker", YN_CHOICES, ids=lambda yn: f"docker:{yn}")
|
{"open_source_license": "BSD"},
|
||||||
@pytest.mark.parametrize("use_celery", YN_CHOICES, ids=lambda yn: f"celery:{yn}")
|
{"open_source_license": "GPLv3"},
|
||||||
@pytest.mark.parametrize("use_mailhog", YN_CHOICES, ids=lambda yn: f"mailhog:{yn}")
|
{"open_source_license": "Apache Software License 2.0"},
|
||||||
@pytest.mark.parametrize("use_sentry", YN_CHOICES, ids=lambda yn: f"sentry:{yn}")
|
{"open_source_license": "Not open source"},
|
||||||
@pytest.mark.parametrize("use_compressor", YN_CHOICES, ids=lambda yn: f"cmpr:{yn}")
|
{"windows": "y"},
|
||||||
@pytest.mark.parametrize("use_whitenoise", YN_CHOICES, ids=lambda yn: f"wnoise:{yn}")
|
{"windows": "n"},
|
||||||
@pytest.mark.parametrize("cloud_provider", CLOUD_CHOICES, ids=lambda yn: f"cloud:{yn}")
|
{"use_pycharm": "y"},
|
||||||
def context_combination(
|
{"use_pycharm": "n"},
|
||||||
windows,
|
{"use_docker": "y"},
|
||||||
use_docker,
|
{"use_docker": "n"},
|
||||||
use_celery,
|
{"postgresql_version": "11.3"},
|
||||||
use_mailhog,
|
{"postgresql_version": "10.8"},
|
||||||
use_sentry,
|
{"postgresql_version": "9.6"},
|
||||||
use_compressor,
|
{"postgresql_version": "9.5"},
|
||||||
use_whitenoise,
|
{"postgresql_version": "9.4"},
|
||||||
cloud_provider,
|
{"cloud_provider": "AWS", "use_whitenoise": "y"},
|
||||||
):
|
{"cloud_provider": "AWS", "use_whitenoise": "n"},
|
||||||
"""Fixture that parametrize the function where it's used."""
|
{"cloud_provider": "GCP", "use_whitenoise": "y"},
|
||||||
return {
|
{"cloud_provider": "GCP", "use_whitenoise": "n"},
|
||||||
"windows": windows,
|
{"cloud_provider": "None", "use_whitenoise": "y"},
|
||||||
"use_docker": use_docker,
|
# Note: cloud_provider=None AND use_whitenoise=n is not supported
|
||||||
"use_compressor": use_compressor,
|
{"mail_service": "Mailgun"},
|
||||||
"use_celery": use_celery,
|
{"mail_service": "Amazon SES"},
|
||||||
"use_mailhog": use_mailhog,
|
{"mail_service": "Mailjet"},
|
||||||
"use_sentry": use_sentry,
|
{"mail_service": "Mandrill"},
|
||||||
"use_whitenoise": use_whitenoise,
|
{"mail_service": "Postmark"},
|
||||||
"cloud_provider": cloud_provider,
|
{"mail_service": "Sendgrid"},
|
||||||
}
|
{"mail_service": "SendinBlue"},
|
||||||
|
{"mail_service": "SparkPost"},
|
||||||
|
{"mail_service": "Other SMTP"},
|
||||||
|
{"use_drf": "y"},
|
||||||
|
{"use_drf": "n"},
|
||||||
|
{"js_task_runner": "None"},
|
||||||
|
{"js_task_runner": "Gulp"},
|
||||||
|
{"custom_bootstrap_compilation": "y"},
|
||||||
|
{"custom_bootstrap_compilation": "n"},
|
||||||
|
{"use_compressor": "y"},
|
||||||
|
{"use_compressor": "n"},
|
||||||
|
{"use_celery": "y"},
|
||||||
|
{"use_celery": "n"},
|
||||||
|
{"use_mailhog": "y"},
|
||||||
|
{"use_mailhog": "n"},
|
||||||
|
{"use_sentry": "y"},
|
||||||
|
{"use_sentry": "n"},
|
||||||
|
{"use_whitenoise": "y"},
|
||||||
|
{"use_whitenoise": "n"},
|
||||||
|
{"use_heroku": "y"},
|
||||||
|
{"use_heroku": "n"},
|
||||||
|
{"ci_tool": "None"},
|
||||||
|
{"ci_tool": "Travis"},
|
||||||
|
{"ci_tool": "Gitlab"},
|
||||||
|
{"keep_local_envs_in_vcs": "y"},
|
||||||
|
{"keep_local_envs_in_vcs": "n"},
|
||||||
|
{"debug": "y"},
|
||||||
|
{"debug": "n"},
|
||||||
|
]
|
||||||
|
|
||||||
|
UNSUPPORTED_COMBINATIONS = [
|
||||||
|
{"cloud_provider": "None", "use_whitenoise": "n"},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _fixture_id(ctx):
|
||||||
|
"""Helper to get a user friendly test name from the parametrized context."""
|
||||||
|
return "-".join(f"{key}:{value}" for key, value in ctx.items())
|
||||||
|
|
||||||
|
|
||||||
def build_files_list(root_dir):
|
def build_files_list(root_dir):
|
||||||
|
@ -71,9 +104,7 @@ def build_files_list(root_dir):
|
||||||
|
|
||||||
|
|
||||||
def check_paths(paths):
|
def check_paths(paths):
|
||||||
"""Method to check all paths have correct substitutions,
|
"""Method to check all paths have correct substitutions."""
|
||||||
used by other tests cases
|
|
||||||
"""
|
|
||||||
# Assert that no match is found in any of the files
|
# Assert that no match is found in any of the files
|
||||||
for path in paths:
|
for path in paths:
|
||||||
if is_binary(path):
|
if is_binary(path):
|
||||||
|
@ -85,13 +116,10 @@ def check_paths(paths):
|
||||||
assert match is None, msg.format(path)
|
assert match is None, msg.format(path)
|
||||||
|
|
||||||
|
|
||||||
def test_project_generation(cookies, context, context_combination):
|
@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id)
|
||||||
"""
|
def test_project_generation(cookies, context, context_override):
|
||||||
Test that project is generated and fully rendered.
|
"""Test that project is generated and fully rendered."""
|
||||||
|
result = cookies.bake(extra_context={**context, **context_override})
|
||||||
This is parametrized for each combination from ``context_combination`` fixture
|
|
||||||
"""
|
|
||||||
result = cookies.bake(extra_context={**context, **context_combination})
|
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert result.exception is None
|
assert result.exception is None
|
||||||
assert result.project.basename == context["project_slug"]
|
assert result.project.basename == context["project_slug"]
|
||||||
|
@ -102,38 +130,30 @@ def test_project_generation(cookies, context, context_combination):
|
||||||
check_paths(paths)
|
check_paths(paths)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.flake8
|
@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id)
|
||||||
def test_flake8_passes(cookies, context_combination):
|
def test_flake8_passes(cookies, context_override):
|
||||||
"""
|
"""Generated project should pass flake8."""
|
||||||
Generated project should pass flake8.
|
result = cookies.bake(extra_context=context_override)
|
||||||
|
|
||||||
This is parametrized for each combination from ``context_combination`` fixture
|
|
||||||
"""
|
|
||||||
result = cookies.bake(extra_context=context_combination)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sh.flake8(str(result.project))
|
sh.flake8(str(result.project))
|
||||||
except sh.ErrorReturnCode as e:
|
except sh.ErrorReturnCode as e:
|
||||||
pytest.fail(e)
|
pytest.fail(e.stdout.decode())
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.black
|
@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id)
|
||||||
def test_black_passes(cookies, context_combination):
|
def test_black_passes(cookies, context_override):
|
||||||
"""
|
"""Generated project should pass black."""
|
||||||
Generated project should pass black.
|
result = cookies.bake(extra_context=context_override)
|
||||||
|
|
||||||
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:
|
||||||
pytest.fail(e)
|
pytest.fail(e.stdout.decode())
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
|
@ -148,6 +168,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."""
|
||||||
|
@ -157,3 +195,13 @@ def test_invalid_slug(cookies, context, slug):
|
||||||
|
|
||||||
assert result.exit_code != 0
|
assert result.exit_code != 0
|
||||||
assert isinstance(result.exception, FailedHookException)
|
assert isinstance(result.exception, FailedHookException)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("invalid_context", UNSUPPORTED_COMBINATIONS)
|
||||||
|
def test_error_if_incompatible(cookies, context, invalid_context):
|
||||||
|
"""It should not generate project an incompatible combination is selected."""
|
||||||
|
context.update(invalid_context)
|
||||||
|
result = cookies.bake(extra_context=context)
|
||||||
|
|
||||||
|
assert result.exit_code != 0
|
||||||
|
assert isinstance(result.exception, FailedHookException)
|
||||||
|
|
12
tox.ini
12
tox.ini
|
@ -1,18 +1,10 @@
|
||||||
[tox]
|
[tox]
|
||||||
skipsdist = true
|
skipsdist = true
|
||||||
envlist = py36,flake8,black,black-template
|
envlist = py37,black-template
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
deps = -rrequirements.txt
|
deps = -rrequirements.txt
|
||||||
commands = pytest -m "not flake8" -m "not black" {posargs:./tests}
|
commands = pytest {posargs:./tests}
|
||||||
|
|
||||||
[testenv:flake8]
|
|
||||||
deps = -rrequirements.txt
|
|
||||||
commands = pytest -m flake8 {posargs:./tests}
|
|
||||||
|
|
||||||
[testenv:black]
|
|
||||||
deps = -rrequirements.txt
|
|
||||||
commands = pytest -m black {posargs:./tests}
|
|
||||||
|
|
||||||
[testenv:black-template]
|
[testenv:black-template]
|
||||||
deps = black
|
deps = black
|
||||||
|
|
|
@ -13,10 +13,16 @@ indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
[*.py]
|
[*.py]
|
||||||
line_length=120
|
line_length = 88
|
||||||
known_first_party={{ cookiecutter.project_slug }}
|
known_first_party = {{cookiecutter.project_slug}},config
|
||||||
multi_line_output=3
|
multi_line_output = 3
|
||||||
default_section=THIRDPARTY
|
default_section = THIRDPARTY
|
||||||
|
recursive = true
|
||||||
|
skip = venv/
|
||||||
|
skip_glob = **/migrations/*.py
|
||||||
|
include_trailing_comma = true
|
||||||
|
force_grid_wrap = 0
|
||||||
|
use_parentheses = true
|
||||||
|
|
||||||
[*.{html,css,scss,json,yml}]
|
[*.{html,css,scss,json,yml}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
|
|
@ -13,9 +13,26 @@ DJANGO_SECURE_SSL_REDIRECT=False
|
||||||
|
|
||||||
# Email
|
# Email
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
MAILGUN_API_KEY=
|
|
||||||
DJANGO_SERVER_EMAIL=
|
DJANGO_SERVER_EMAIL=
|
||||||
|
{% if cookiecutter.mail_service == 'Mailgun' %}
|
||||||
|
MAILGUN_API_KEY=
|
||||||
MAILGUN_DOMAIN=
|
MAILGUN_DOMAIN=
|
||||||
|
{% elif cookiecutter.mail_service == 'Mailjet' %}
|
||||||
|
MAILJET_API_KEY=
|
||||||
|
MAILJET_SECRET_KEY=
|
||||||
|
{% elif cookiecutter.mail_service == 'Mandrill' %}
|
||||||
|
MANDRILL_API_KEY=
|
||||||
|
{% elif cookiecutter.mail_service == 'Postmark' %}
|
||||||
|
POSTMARK_SERVER_TOKEN=
|
||||||
|
{% elif cookiecutter.mail_service == 'Sendgrid' %}
|
||||||
|
SENDGRID_API_KEY=
|
||||||
|
SENDGRID_GENERATE_MESSAGE_ID=True
|
||||||
|
SENDGRID_MERGE_FIELD_FORMAT=None
|
||||||
|
{% elif cookiecutter.mail_service == 'SendinBlue' %}
|
||||||
|
SENDINBLUE_API_KEY=
|
||||||
|
{% elif cookiecutter.mail_service == 'SparkPost' %}
|
||||||
|
SPARKPOST_API_KEY=
|
||||||
|
{% endif %}
|
||||||
{% if cookiecutter.cloud_provider == 'AWS' %}
|
{% if cookiecutter.cloud_provider == 'AWS' %}
|
||||||
# AWS
|
# AWS
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -31,11 +48,7 @@ DJANGO_GCP_STORAGE_BUCKET_NAME=
|
||||||
# django-allauth
|
# django-allauth
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
DJANGO_ACCOUNT_ALLOW_REGISTRATION=True
|
DJANGO_ACCOUNT_ALLOW_REGISTRATION=True
|
||||||
{% if cookiecutter.use_compressor == 'y' %}
|
|
||||||
# django-compressor
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
COMPRESS_ENABLED=
|
|
||||||
{% endif %}
|
|
||||||
# Gunicorn
|
# Gunicorn
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
WEB_CONCURRENCY=4
|
WEB_CONCURRENCY=4
|
||||||
|
|
34
{{cookiecutter.project_slug}}/.gitlab-ci.yml
Normal file
34
{{cookiecutter.project_slug}}/.gitlab-ci.yml
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
stages:
|
||||||
|
- lint
|
||||||
|
- test
|
||||||
|
|
||||||
|
variables:
|
||||||
|
POSTGRES_USER: '{{ cookiecutter.project_slug }}'
|
||||||
|
POSTGRES_PASSWORD: ''
|
||||||
|
POSTGRES_DB: 'test_{{ cookiecutter.project_slug }}'
|
||||||
|
POSTGRES_HOST_AUTH_METHOD: trust
|
||||||
|
|
||||||
|
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
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
{%- if cookiecutter.use_celery == 'y' %}
|
{%- if cookiecutter.use_docker == 'n' %}
|
||||||
|
<component name="DjangoConsoleOptions"
|
||||||
|
custom-start-script="import sys; print('Python %s on %s' % (sys.version, sys.platform)) import django; print('Django %s' % django.get_version()) import os sys.path.extend([WORKING_DIR_AND_PYTHON_PATHS]) if 'setup' in dir(django): django.setup() import django_manage_shell; django_manage_shell.run(PROJECT_ROOT)"
|
||||||
|
module-name="{{ cookiecutter.project_slug }}" is-module-sdk="true">
|
||||||
|
</component>
|
||||||
|
{%- elif cookiecutter.use_celery == 'y' %}
|
||||||
<component name="DjangoConsoleOptions"
|
<component name="DjangoConsoleOptions"
|
||||||
custom-start-script="import sys; print('Python %s on %s' % (sys.version, sys.platform)) import django; print('Django %s' % django.get_version()) import os os.environ.setdefault("DATABASE_URL","postgres://{}:{}@{}:{}/{}".format(os.environ['POSTGRES_USER'], os.environ['POSTGRES_PASSWORD'], os.environ['POSTGRES_HOST'], os.environ['POSTGRES_PORT'], os.environ['POSTGRES_DB'])) os.environ.setdefault("CELERY_BROKER_URL", os.environ['REDIS_URL']) sys.path.extend([WORKING_DIR_AND_PYTHON_PATHS]) if 'setup' in dir(django): django.setup() import django_manage_shell; django_manage_shell.run(PROJECT_ROOT)"
|
custom-start-script="import sys; print('Python %s on %s' % (sys.version, sys.platform)) import django; print('Django %s' % django.get_version()) import os os.environ.setdefault("DATABASE_URL","postgres://{}:{}@{}:{}/{}".format(os.environ['POSTGRES_USER'], os.environ['POSTGRES_PASSWORD'], os.environ['POSTGRES_HOST'], os.environ['POSTGRES_PORT'], os.environ['POSTGRES_DB'])) os.environ.setdefault("CELERY_BROKER_URL", os.environ['REDIS_URL']) sys.path.extend([WORKING_DIR_AND_PYTHON_PATHS]) if 'setup' in dir(django): django.setup() import django_manage_shell; django_manage_shell.run(PROJECT_ROOT)"
|
||||||
module-name="{{ cookiecutter.project_slug }}" is-module-sdk="true">
|
module-name="{{ cookiecutter.project_slug }}" is-module-sdk="true">
|
||||||
|
|
20
{{cookiecutter.project_slug}}/.pre-commit-config.yaml
Normal file
20
{{cookiecutter.project_slug}}/.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
exclude: 'docs|node_modules|migrations|.git|.tox'
|
||||||
|
default_stages: [commit]
|
||||||
|
fail_fast: true
|
||||||
|
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: master
|
||||||
|
hooks:
|
||||||
|
- id: trailing-whitespace
|
||||||
|
files: (^|/)a/.+\.(py|html|sh|css|js)$
|
||||||
|
|
||||||
|
- repo: local
|
||||||
|
hooks:
|
||||||
|
- id: flake8
|
||||||
|
name: flake8
|
||||||
|
entry: flake8
|
||||||
|
language: python
|
||||||
|
types: [python]
|
||||||
|
args: ['--config=setup.cfg']
|
||||||
|
|
|
@ -10,7 +10,7 @@ before_install:
|
||||||
- sudo apt-get install -qq libsqlite3-dev libxml2 libxml2-dev libssl-dev libbz2-dev wget curl llvm
|
- sudo apt-get install -qq libsqlite3-dev libxml2 libxml2-dev libssl-dev libbz2-dev wget curl llvm
|
||||||
language: python
|
language: python
|
||||||
python:
|
python:
|
||||||
- "3.6"
|
- "3.7"
|
||||||
install:
|
install:
|
||||||
- pip install -r requirements/local.txt
|
- pip install -r requirements/local.txt
|
||||||
script:
|
script:
|
||||||
|
|
|
@ -2,4 +2,5 @@ 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=config.celery_app --loglevel=info
|
worker: celery worker --app=config.celery_app --loglevel=info
|
||||||
|
beat: celery beat --app=config.celery_app --loglevel=info
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
FROM python:3.6-alpine
|
FROM python:3.7-slim-buster
|
||||||
|
|
||||||
ENV PYTHONUNBUFFERED 1
|
ENV PYTHONUNBUFFERED 1
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE 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
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
set -o errexit
|
set -o errexit
|
||||||
set -o nounset
|
set -o nounset
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
set -o errexit
|
set -o errexit
|
||||||
set -o nounset
|
set -o nounset
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
set -o errexit
|
set -o errexit
|
||||||
set -o nounset
|
set -o nounset
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
set -o errexit
|
set -o errexit
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
|
@ -9,21 +9,23 @@ RUN npm run build
|
||||||
|
|
||||||
# Python build stage
|
# Python build stage
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
FROM python:3.6-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
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
set -o errexit
|
set -o errexit
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
set -o errexit
|
set -o errexit
|
||||||
set -o nounset
|
set -o nounset
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
set -o errexit
|
set -o errexit
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
set -o errexit
|
set -o errexit
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
set -o errexit
|
set -o errexit
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
@ -6,4 +6,25 @@ set -o nounset
|
||||||
|
|
||||||
|
|
||||||
python /app/manage.py collectstatic --noinput
|
python /app/manage.py collectstatic --noinput
|
||||||
|
{% if cookiecutter.use_whitenoise == 'y' and cookiecutter.use_compressor == 'y' %}
|
||||||
|
compress_enabled() {
|
||||||
|
python << END
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from environ import Env
|
||||||
|
|
||||||
|
env = Env(COMPRESS_ENABLED=(bool, True))
|
||||||
|
if env('COMPRESS_ENABLED'):
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
END
|
||||||
|
}
|
||||||
|
|
||||||
|
if compress_enabled; then
|
||||||
|
# NOTE this command will fail if django-compressor is disabled
|
||||||
|
python /app/manage.py compress
|
||||||
|
fi
|
||||||
|
{%- endif %}
|
||||||
/usr/local/bin/gunicorn config.wsgi --bind 0.0.0.0:5000 --chdir=/app
|
/usr/local/bin/gunicorn config.wsgi --bind 0.0.0.0:5000 --chdir=/app
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
FROM traefik: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
|
||||||
|
|
|
@ -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 }}"
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
log:
|
||||||
|
level: INFO
|
||||||
|
|
||||||
|
entryPoints:
|
||||||
|
web:
|
||||||
|
# http
|
||||||
|
address: ":80"
|
||||||
|
|
||||||
|
web-secure:
|
||||||
|
# https
|
||||||
|
address: ":443"
|
||||||
|
{%- if cookiecutter.use_celery == 'y' %}
|
||||||
|
|
||||||
|
flower:
|
||||||
|
address: ":5555"
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
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
|
||||||
|
{%- if cookiecutter.use_celery == 'y' %}
|
||||||
|
|
||||||
|
flower-secure-router:
|
||||||
|
rule: "Host(`{{ cookiecutter.domain_name }}`)"
|
||||||
|
entryPoints:
|
||||||
|
- flower
|
||||||
|
service: flower
|
||||||
|
tls:
|
||||||
|
# https://docs.traefik.io/master/routing/routers/#certresolver
|
||||||
|
certResolver: letsencrypt
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
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
|
||||||
|
{%- if cookiecutter.use_celery == 'y' %}
|
||||||
|
|
||||||
|
flower:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: http://flower:5555
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
providers:
|
||||||
|
# https://docs.traefik.io/master/providers/file/
|
||||||
|
file:
|
||||||
|
filename: /etc/traefik/traefik.yml
|
||||||
|
watch: true
|
15
{{cookiecutter.project_slug}}/config/api_router.py
Normal file
15
{{cookiecutter.project_slug}}/config/api_router.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
from django.conf import settings
|
||||||
|
from rest_framework.routers import DefaultRouter, SimpleRouter
|
||||||
|
|
||||||
|
from {{ cookiecutter.project_slug }}.users.api.views import UserViewSet
|
||||||
|
|
||||||
|
if settings.DEBUG:
|
||||||
|
router = DefaultRouter()
|
||||||
|
else:
|
||||||
|
router = SimpleRouter()
|
||||||
|
|
||||||
|
router.register("users", UserViewSet)
|
||||||
|
|
||||||
|
|
||||||
|
app_name = "api"
|
||||||
|
urlpatterns = router.urls
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from celery import Celery
|
from celery import Celery
|
||||||
|
|
||||||
# set the default Django settings module for the 'celery' program.
|
# set the default Django settings module for the 'celery' program.
|
||||||
|
|
|
@ -68,16 +68,20 @@ DJANGO_APPS = [
|
||||||
"django.contrib.staticfiles",
|
"django.contrib.staticfiles",
|
||||||
# "django.contrib.humanize", # Handy template tags
|
# "django.contrib.humanize", # Handy template tags
|
||||||
"django.contrib.admin",
|
"django.contrib.admin",
|
||||||
|
"django.forms",
|
||||||
]
|
]
|
||||||
THIRD_PARTY_APPS = [
|
THIRD_PARTY_APPS = [
|
||||||
"crispy_forms",
|
"crispy_forms",
|
||||||
"allauth",
|
"allauth",
|
||||||
"allauth.account",
|
"allauth.account",
|
||||||
"allauth.socialaccount",
|
"allauth.socialaccount",
|
||||||
"rest_framework",
|
|
||||||
{%- if cookiecutter.use_celery == 'y' %}
|
{%- if cookiecutter.use_celery == 'y' %}
|
||||||
"django_celery_beat",
|
"django_celery_beat",
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
{%- if cookiecutter.use_drf == "y" %}
|
||||||
|
"rest_framework",
|
||||||
|
"rest_framework.authtoken",
|
||||||
|
{%- endif %}
|
||||||
]
|
]
|
||||||
|
|
||||||
LOCAL_APPS = [
|
LOCAL_APPS = [
|
||||||
|
@ -131,12 +135,16 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||||
# https://docs.djangoproject.com/en/dev/ref/settings/#middleware
|
# https://docs.djangoproject.com/en/dev/ref/settings/#middleware
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
"django.middleware.security.SecurityMiddleware",
|
"django.middleware.security.SecurityMiddleware",
|
||||||
|
{%- if cookiecutter.use_whitenoise == 'y' %}
|
||||||
|
"whitenoise.middleware.WhiteNoiseMiddleware",
|
||||||
|
{%- endif %}
|
||||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||||
"django.middleware.locale.LocaleMiddleware",
|
"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",
|
||||||
"django.contrib.messages.middleware.MessageMiddleware",
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
|
"django.middleware.common.BrokenLinkEmailsMiddleware",
|
||||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -187,10 +195,15 @@ TEMPLATES = [
|
||||||
"django.template.context_processors.static",
|
"django.template.context_processors.static",
|
||||||
"django.template.context_processors.tz",
|
"django.template.context_processors.tz",
|
||||||
"django.contrib.messages.context_processors.messages",
|
"django.contrib.messages.context_processors.messages",
|
||||||
|
"{{ cookiecutter.project_slug }}.utils.context_processors.settings_context",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#form-renderer
|
||||||
|
FORM_RENDERER = "django.forms.renderers.TemplatesSetting"
|
||||||
|
|
||||||
# http://django-crispy-forms.readthedocs.io/en/latest/install.html#template-packs
|
# http://django-crispy-forms.readthedocs.io/en/latest/install.html#template-packs
|
||||||
CRISPY_TEMPLATE_PACK = "bootstrap4"
|
CRISPY_TEMPLATE_PACK = "bootstrap4"
|
||||||
|
|
||||||
|
@ -291,14 +304,24 @@ ACCOUNT_EMAIL_VERIFICATION = "mandatory"
|
||||||
ACCOUNT_ADAPTER = "{{cookiecutter.project_slug}}.users.adapters.AccountAdapter"
|
ACCOUNT_ADAPTER = "{{cookiecutter.project_slug}}.users.adapters.AccountAdapter"
|
||||||
# https://django-allauth.readthedocs.io/en/latest/configuration.html
|
# https://django-allauth.readthedocs.io/en/latest/configuration.html
|
||||||
SOCIALACCOUNT_ADAPTER = "{{cookiecutter.project_slug}}.users.adapters.SocialAccountAdapter"
|
SOCIALACCOUNT_ADAPTER = "{{cookiecutter.project_slug}}.users.adapters.SocialAccountAdapter"
|
||||||
|
|
||||||
{% if cookiecutter.use_compressor == 'y' -%}
|
{% if cookiecutter.use_compressor == 'y' -%}
|
||||||
# django-compressor
|
# django-compressor
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# https://django-compressor.readthedocs.io/en/latest/quickstart/#installation
|
# https://django-compressor.readthedocs.io/en/latest/quickstart/#installation
|
||||||
INSTALLED_APPS += ["compressor"]
|
INSTALLED_APPS += ["compressor"]
|
||||||
STATICFILES_FINDERS += ["compressor.finders.CompressorFinder"]
|
STATICFILES_FINDERS += ["compressor.finders.CompressorFinder"]
|
||||||
|
{%- endif %}
|
||||||
|
{% if cookiecutter.use_drf == "y" -%}
|
||||||
|
# django-reset-framework
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# django-rest-framework - https://www.django-rest-framework.org/api-guide/settings/
|
||||||
|
REST_FRAMEWORK = {
|
||||||
|
"DEFAULT_AUTHENTICATION_CLASSES": (
|
||||||
|
"rest_framework.authentication.SessionAuthentication",
|
||||||
|
"rest_framework.authentication.TokenAuthentication",
|
||||||
|
),
|
||||||
|
"DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
|
||||||
|
}
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
# Your stuff...
|
# Your stuff...
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -28,19 +28,27 @@ CACHES = {
|
||||||
{% if cookiecutter.use_mailhog == 'y' and cookiecutter.use_docker == 'y' -%}
|
{% if cookiecutter.use_mailhog == 'y' and cookiecutter.use_docker == 'y' -%}
|
||||||
# https://docs.djangoproject.com/en/dev/ref/settings/#email-host
|
# https://docs.djangoproject.com/en/dev/ref/settings/#email-host
|
||||||
EMAIL_HOST = env("EMAIL_HOST", default="mailhog")
|
EMAIL_HOST = env("EMAIL_HOST", default="mailhog")
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#email-port
|
||||||
|
EMAIL_PORT = 1025
|
||||||
{%- elif cookiecutter.use_mailhog == 'y' and cookiecutter.use_docker == 'n' -%}
|
{%- elif cookiecutter.use_mailhog == 'y' and cookiecutter.use_docker == 'n' -%}
|
||||||
# https://docs.djangoproject.com/en/dev/ref/settings/#email-host
|
# https://docs.djangoproject.com/en/dev/ref/settings/#email-host
|
||||||
EMAIL_HOST = "localhost"
|
EMAIL_HOST = "localhost"
|
||||||
|
# https://docs.djangoproject.com/en/dev/ref/settings/#email-port
|
||||||
|
EMAIL_PORT = 1025
|
||||||
{%- else -%}
|
{%- else -%}
|
||||||
# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
|
# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
|
||||||
EMAIL_BACKEND = env(
|
EMAIL_BACKEND = env(
|
||||||
"DJANGO_EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend"
|
"DJANGO_EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend"
|
||||||
)
|
)
|
||||||
# https://docs.djangoproject.com/en/dev/ref/settings/#email-host
|
|
||||||
EMAIL_HOST = "localhost"
|
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
# https://docs.djangoproject.com/en/dev/ref/settings/#email-port
|
|
||||||
EMAIL_PORT = 1025
|
{%- if cookiecutter.use_whitenoise == 'y' %}
|
||||||
|
|
||||||
|
# WhiteNoise
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# http://whitenoise.evans.io/en/latest/django.html#using-whitenoise-in-development
|
||||||
|
INSTALLED_APPS = ["whitenoise.runserver_nostatic"] + INSTALLED_APPS # noqa F405
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
# django-debug-toolbar
|
# django-debug-toolbar
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
"""isort:skip_file"""
|
||||||
{% if cookiecutter.use_sentry == 'y' -%}
|
{% if cookiecutter.use_sentry == 'y' -%}
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -92,7 +93,6 @@ 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 == 'GCP' %}
|
{% elif cookiecutter.cloud_provider == 'GCP' %}
|
||||||
DEFAULT_FILE_STORAGE = "storages.backends.gcloud.GoogleCloudStorage"
|
|
||||||
GS_BUCKET_NAME = env("DJANGO_GCP_STORAGE_BUCKET_NAME")
|
GS_BUCKET_NAME = env("DJANGO_GCP_STORAGE_BUCKET_NAME")
|
||||||
GS_DEFAULT_ACL = "publicRead"
|
GS_DEFAULT_ACL = "publicRead"
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
|
@ -105,8 +105,11 @@ GS_DEFAULT_ACL = "publicRead"
|
||||||
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
||||||
{% elif cookiecutter.cloud_provider == 'AWS' -%}
|
{% elif cookiecutter.cloud_provider == 'AWS' -%}
|
||||||
STATICFILES_STORAGE = "config.settings.production.StaticRootS3Boto3Storage"
|
STATICFILES_STORAGE = "config.settings.production.StaticRootS3Boto3Storage"
|
||||||
|
COLLECTFAST_STRATEGY = "collectfast.strategies.boto3.Boto3Strategy"
|
||||||
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 == 'GCP' -%}
|
{% elif cookiecutter.cloud_provider == 'GCP' -%}
|
||||||
|
STATICFILES_STORAGE = "config.settings.production.StaticRootGoogleCloudStorage"
|
||||||
|
COLLECTFAST_STRATEGY = "collectfast.strategies.gcloud.GoogleCloudStrategy"
|
||||||
STATIC_URL = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/static/"
|
STATIC_URL = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/static/"
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
|
|
||||||
|
@ -132,14 +135,27 @@ class MediaRootS3Boto3Storage(S3Boto3Storage):
|
||||||
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 == 'GCP' %}
|
{%- elif cookiecutter.cloud_provider == 'GCP' %}
|
||||||
|
from storages.backends.gcloud import GoogleCloudStorage # noqa E402
|
||||||
|
|
||||||
|
|
||||||
|
class StaticRootGoogleCloudStorage(GoogleCloudStorage):
|
||||||
|
location = "static"
|
||||||
|
default_acl = "publicRead"
|
||||||
|
|
||||||
|
|
||||||
|
class MediaRootGoogleCloudStorage(GoogleCloudStorage):
|
||||||
|
location = "media"
|
||||||
|
file_overwrite = False
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_FILE_STORAGE = "config.settings.production.MediaRootGoogleCloudStorage"
|
||||||
MEDIA_URL = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/media/"
|
MEDIA_URL = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/media/"
|
||||||
MEDIA_ROOT = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/media/"
|
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
# 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",
|
||||||
[
|
[
|
||||||
|
@ -167,41 +183,114 @@ EMAIL_SUBJECT_PREFIX = env(
|
||||||
# Django Admin URL regex.
|
# Django Admin URL regex.
|
||||||
ADMIN_URL = env("DJANGO_ADMIN_URL")
|
ADMIN_URL = env("DJANGO_ADMIN_URL")
|
||||||
|
|
||||||
# Anymail (Mailgun)
|
# Anymail
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# https://anymail.readthedocs.io/en/stable/installation/#installing-anymail
|
# https://anymail.readthedocs.io/en/stable/installation/#installing-anymail
|
||||||
INSTALLED_APPS += ["anymail"] # noqa F405
|
INSTALLED_APPS += ["anymail"] # noqa F405
|
||||||
EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend"
|
# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
|
||||||
# https://anymail.readthedocs.io/en/stable/installation/#anymail-settings-reference
|
# https://anymail.readthedocs.io/en/stable/installation/#anymail-settings-reference
|
||||||
|
{%- if cookiecutter.mail_service == 'Mailgun' %}
|
||||||
|
# https://anymail.readthedocs.io/en/stable/esps/mailgun/
|
||||||
|
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"),
|
"MAILGUN_API_URL": env("MAILGUN_API_URL", default="https://api.mailgun.net/v3"),
|
||||||
}
|
}
|
||||||
|
{%- elif cookiecutter.mail_service == 'Amazon SES' %}
|
||||||
|
# https://anymail.readthedocs.io/en/stable/esps/amazon_ses/
|
||||||
|
EMAIL_BACKEND = "anymail.backends.amazon_ses.EmailBackend"
|
||||||
|
ANYMAIL = {}
|
||||||
|
{%- elif cookiecutter.mail_service == 'Mailjet' %}
|
||||||
|
# https://anymail.readthedocs.io/en/stable/esps/mailjet/
|
||||||
|
EMAIL_BACKEND = "anymail.backends.mailjet.EmailBackend"
|
||||||
|
ANYMAIL = {
|
||||||
|
"MAILJET_API_KEY": env("MAILJET_API_KEY"),
|
||||||
|
"MAILJET_SECRET_KEY": env("MAILJET_SECRET_KEY"),
|
||||||
|
"MAILJET_API_URL": env("MAILJET_API_URL", default="https://api.mailjet.com/v3"),
|
||||||
|
}
|
||||||
|
{%- elif cookiecutter.mail_service == 'Mandrill' %}
|
||||||
|
# https://anymail.readthedocs.io/en/stable/esps/mandrill/
|
||||||
|
EMAIL_BACKEND = "anymail.backends.mandrill.EmailBackend"
|
||||||
|
ANYMAIL = {
|
||||||
|
"MANDRILL_API_KEY": env("MANDRILL_API_KEY"),
|
||||||
|
"MANDRILL_API_URL": env(
|
||||||
|
"MANDRILL_API_URL", default="https://mandrillapp.com/api/1.0"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
{%- elif cookiecutter.mail_service == 'Postmark' %}
|
||||||
|
# https://anymail.readthedocs.io/en/stable/esps/postmark/
|
||||||
|
EMAIL_BACKEND = "anymail.backends.postmark.EmailBackend"
|
||||||
|
ANYMAIL = {
|
||||||
|
"POSTMARK_SERVER_TOKEN": env("POSTMARK_SERVER_TOKEN"),
|
||||||
|
"POSTMARK_API_URL": env("POSTMARK_API_URL", default="https://api.postmarkapp.com/"),
|
||||||
|
}
|
||||||
|
{%- elif cookiecutter.mail_service == 'Sendgrid' %}
|
||||||
|
# https://anymail.readthedocs.io/en/stable/esps/sendgrid/
|
||||||
|
EMAIL_BACKEND = "anymail.backends.sendgrid.EmailBackend"
|
||||||
|
ANYMAIL = {
|
||||||
|
"SENDGRID_API_KEY": env("SENDGRID_API_KEY"),
|
||||||
|
"SENDGRID_GENERATE_MESSAGE_ID": env("SENDGRID_GENERATE_MESSAGE_ID"),
|
||||||
|
"SENDGRID_MERGE_FIELD_FORMAT": env("SENDGRID_MERGE_FIELD_FORMAT"),
|
||||||
|
"SENDGRID_API_URL": env("SENDGRID_API_URL", default="https://api.sendgrid.com/v3/"),
|
||||||
|
}
|
||||||
|
{%- elif cookiecutter.mail_service == 'SendinBlue' %}
|
||||||
|
# https://anymail.readthedocs.io/en/stable/esps/sendinblue/
|
||||||
|
EMAIL_BACKEND = "anymail.backends.sendinblue.EmailBackend"
|
||||||
|
ANYMAIL = {
|
||||||
|
"SENDINBLUE_API_KEY": env("SENDINBLUE_API_KEY"),
|
||||||
|
"SENDINBLUE_API_URL": env(
|
||||||
|
"SENDINBLUE_API_URL", default="https://api.sendinblue.com/v3/"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
{%- elif cookiecutter.mail_service == 'SparkPost' %}
|
||||||
|
# https://anymail.readthedocs.io/en/stable/esps/sparkpost/
|
||||||
|
EMAIL_BACKEND = "anymail.backends.sparkpost.EmailBackend"
|
||||||
|
ANYMAIL = {
|
||||||
|
"SPARKPOST_API_KEY": env("SPARKPOST_API_KEY"),
|
||||||
|
"SPARKPOST_API_URL": env(
|
||||||
|
"SPARKPOST_API_URL", default="https://api.sparkpost.com/api/v1"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
{%- elif cookiecutter.mail_service == 'Other SMTP' %}
|
||||||
|
# https://anymail.readthedocs.io/en/stable/esps
|
||||||
|
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
|
||||||
|
ANYMAIL = {}
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
{% if cookiecutter.use_whitenoise == 'y' -%}
|
{% if cookiecutter.use_compressor == 'y' -%}
|
||||||
# WhiteNoise
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# http://whitenoise.evans.io/en/latest/django.html#enable-whitenoise
|
|
||||||
MIDDLEWARE.insert(1, "whitenoise.middleware.WhiteNoiseMiddleware") # noqa F405
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
{%- if cookiecutter.use_compressor == 'y' -%}
|
|
||||||
# django-compressor
|
# django-compressor
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_ENABLED
|
# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_ENABLED
|
||||||
COMPRESS_ENABLED = env.bool("COMPRESS_ENABLED", default=True)
|
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
|
||||||
|
{%- if cookiecutter.cloud_provider == 'AWS' %}
|
||||||
COMPRESS_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
|
COMPRESS_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
|
||||||
|
{%- elif cookiecutter.cloud_provider == 'GCP' %}
|
||||||
|
COMPRESS_STORAGE = "storages.backends.gcloud.GoogleCloudStorage"
|
||||||
|
{%- elif cookiecutter.cloud_provider == 'None' %}
|
||||||
|
COMPRESS_STORAGE = "compressor.storage.GzipCompressorFileStorage"
|
||||||
|
{%- endif %}
|
||||||
# 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' or cookiecutter.cloud_provider == 'None' %} # noqa F405{% endif %}
|
COMPRESS_URL = STATIC_URL{% if cookiecutter.use_whitenoise == 'y' or cookiecutter.cloud_provider == 'None' %} # noqa F405{% endif %}
|
||||||
|
{%- if cookiecutter.use_whitenoise == 'y' %}
|
||||||
|
# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_OFFLINE
|
||||||
|
COMPRESS_OFFLINE = True # Offline compression is required when using Whitenoise
|
||||||
|
{%- endif %}
|
||||||
|
# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_FILTERS
|
||||||
|
COMPRESS_FILTERS = {
|
||||||
|
"css": [
|
||||||
|
"compressor.filters.css_default.CssAbsoluteFilter",
|
||||||
|
"compressor.filters.cssmin.rCSSMinFilter",
|
||||||
|
],
|
||||||
|
"js": ["compressor.filters.jsmin.JSMinFilter"],
|
||||||
|
}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- if cookiecutter.use_whitenoise == 'n' -%}
|
{%- if cookiecutter.use_whitenoise == 'n' -%}
|
||||||
# Collectfast
|
# Collectfast
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# https://github.com/antonagestam/collectfast#installation
|
# https://github.com/antonagestam/collectfast#installation
|
||||||
INSTALLED_APPS = ["collectfast"] + INSTALLED_APPS # noqa F405
|
INSTALLED_APPS = ["collectfast"] + INSTALLED_APPS # noqa F405
|
||||||
AWS_PRELOAD_METADATA = True
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
# LOGGING
|
# LOGGING
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -7,8 +7,6 @@ from .base import env
|
||||||
|
|
||||||
# GENERAL
|
# GENERAL
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# https://docs.djangoproject.com/en/dev/ref/settings/#debug
|
|
||||||
DEBUG = False
|
|
||||||
# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
|
# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
|
||||||
SECRET_KEY = env(
|
SECRET_KEY = env(
|
||||||
"DJANGO_SECRET_KEY",
|
"DJANGO_SECRET_KEY",
|
||||||
|
@ -34,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",
|
||||||
[
|
[
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.urls import include, path
|
|
||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.views.generic import TemplateView
|
from django.urls import include, path
|
||||||
from django.views import defaults as default_views
|
from django.views import defaults as default_views
|
||||||
|
from django.views.generic import TemplateView
|
||||||
|
{%- if cookiecutter.use_drf == 'y' %}
|
||||||
|
from rest_framework.authtoken.views import obtain_auth_token
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", TemplateView.as_view(template_name="pages/home.html"), name="home"),
|
path("", TemplateView.as_view(template_name="pages/home.html"), name="home"),
|
||||||
|
@ -17,6 +20,15 @@ urlpatterns = [
|
||||||
path("accounts/", include("allauth.urls")),
|
path("accounts/", include("allauth.urls")),
|
||||||
# Your stuff: custom urls includes go here
|
# Your stuff: custom urls includes go here
|
||||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
{% if cookiecutter.use_drf == 'y' -%}
|
||||||
|
# API URLS
|
||||||
|
urlpatterns += [
|
||||||
|
# API base url
|
||||||
|
path("api/", include("config.api_router")),
|
||||||
|
# DRF auth token
|
||||||
|
path("auth-token/", obtain_auth_token),
|
||||||
|
]
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
# This allows the error pages to be debugged during development, just visit
|
# This allows the error pages to be debugged during development, just visit
|
||||||
|
|
|
@ -1,153 +1,20 @@
|
||||||
# Makefile for Sphinx documentation
|
# Minimal makefile for Sphinx documentation
|
||||||
#
|
#
|
||||||
|
|
||||||
# You can set these variables from the command line.
|
# You can set these variables from the command line, and also
|
||||||
SPHINXOPTS =
|
# from the environment for the first two.
|
||||||
SPHINXBUILD = sphinx-build
|
SPHINXOPTS ?=
|
||||||
PAPER =
|
SPHINXBUILD ?= sphinx-build
|
||||||
|
SOURCEDIR = .
|
||||||
BUILDDIR = _build
|
BUILDDIR = _build
|
||||||
|
|
||||||
# Internal variables.
|
# Put it first so that "make" without argument is like "make help".
|
||||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
|
||||||
PAPEROPT_letter = -D latex_paper_size=letter
|
|
||||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
|
||||||
# the i18n builder cannot share the environment and doctrees with the others
|
|
||||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
|
||||||
|
|
||||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
|
||||||
|
|
||||||
help:
|
help:
|
||||||
@echo "Please use \`make <target>' where <target> is one of"
|
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
@echo " html to make standalone HTML files"
|
|
||||||
@echo " dirhtml to make HTML files named index.html in directories"
|
|
||||||
@echo " singlehtml to make a single large HTML file"
|
|
||||||
@echo " pickle to make pickle files"
|
|
||||||
@echo " json to make JSON files"
|
|
||||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
|
||||||
@echo " qthelp to make HTML files and a qthelp project"
|
|
||||||
@echo " devhelp to make HTML files and a Devhelp project"
|
|
||||||
@echo " epub to make an epub"
|
|
||||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
|
||||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
|
||||||
@echo " text to make text files"
|
|
||||||
@echo " man to make manual pages"
|
|
||||||
@echo " texinfo to make Texinfo files"
|
|
||||||
@echo " info to make Texinfo files and run them through makeinfo"
|
|
||||||
@echo " gettext to make PO message catalogs"
|
|
||||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
|
||||||
@echo " linkcheck to check all external links for integrity"
|
|
||||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
|
||||||
|
|
||||||
clean:
|
.PHONY: help Makefile
|
||||||
-rm -rf $(BUILDDIR)/*
|
|
||||||
|
|
||||||
html:
|
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||||
@echo
|
%: Makefile
|
||||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
|
|
||||||
dirhtml:
|
|
||||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
|
||||||
|
|
||||||
singlehtml:
|
|
||||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
|
||||||
|
|
||||||
pickle:
|
|
||||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can process the pickle files."
|
|
||||||
|
|
||||||
json:
|
|
||||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can process the JSON files."
|
|
||||||
|
|
||||||
htmlhelp:
|
|
||||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
|
||||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
|
||||||
|
|
||||||
qthelp:
|
|
||||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
|
||||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
|
||||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/{{ cookiecutter.project_slug }}.qhcp"
|
|
||||||
@echo "To view the help file:"
|
|
||||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/{{ cookiecutter.project_slug }}.qhc"
|
|
||||||
|
|
||||||
devhelp:
|
|
||||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
|
||||||
@echo
|
|
||||||
@echo "Build finished."
|
|
||||||
@echo "To view the help file:"
|
|
||||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/{{ cookiecutter.project_slug }}"
|
|
||||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/{{ cookiecutter.project_slug }}"
|
|
||||||
@echo "# devhelp"
|
|
||||||
|
|
||||||
epub:
|
|
||||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
|
||||||
|
|
||||||
latex:
|
|
||||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
|
||||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
|
||||||
"(use \`make latexpdf' here to do that automatically)."
|
|
||||||
|
|
||||||
latexpdf:
|
|
||||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
|
||||||
@echo "Running LaTeX files through pdflatex..."
|
|
||||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
|
||||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
|
||||||
|
|
||||||
text:
|
|
||||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
|
||||||
|
|
||||||
man:
|
|
||||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
|
||||||
|
|
||||||
texinfo:
|
|
||||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
|
||||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
|
||||||
"(use \`make info' here to do that automatically)."
|
|
||||||
|
|
||||||
info:
|
|
||||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
|
||||||
@echo "Running Texinfo files through makeinfo..."
|
|
||||||
make -C $(BUILDDIR)/texinfo info
|
|
||||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
|
||||||
|
|
||||||
gettext:
|
|
||||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
|
||||||
|
|
||||||
changes:
|
|
||||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
|
||||||
@echo
|
|
||||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
|
||||||
|
|
||||||
linkcheck:
|
|
||||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
|
||||||
@echo
|
|
||||||
@echo "Link check complete; look for any errors in the above output " \
|
|
||||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
|
||||||
|
|
||||||
doctest:
|
|
||||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
|
||||||
@echo "Testing of doctests in the sources finished, look at the " \
|
|
||||||
"results in $(BUILDDIR)/doctest/output.txt."
|
|
||||||
|
|
|
@ -1,255 +1,55 @@
|
||||||
# {{ cookiecutter.project_name }} documentation build configuration file, created by
|
# Configuration file for the Sphinx documentation builder.
|
||||||
# sphinx-quickstart.
|
|
||||||
#
|
#
|
||||||
# This file is execfile()d with the current directory set to its containing dir.
|
# This file only contains a selection of the most common options. For a full
|
||||||
#
|
# list see the documentation:
|
||||||
# Note that not all possible configuration values are present in this
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||||
# autogenerated file.
|
|
||||||
#
|
|
||||||
# All configuration values have a default; values that are commented out
|
|
||||||
# serve to show the default.
|
|
||||||
|
|
||||||
import os
|
# -- Path setup --------------------------------------------------------------
|
||||||
import sys
|
|
||||||
|
|
||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
# sys.path.insert(0, os.path.abspath('.'))
|
#
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
# -- General configuration -----------------------------------------------------
|
# import django
|
||||||
|
# sys.path.insert(0, os.path.abspath('..'))
|
||||||
|
# os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
|
||||||
|
# django.setup()
|
||||||
|
|
||||||
# If your documentation needs a minimal Sphinx version, state it here.
|
|
||||||
# needs_sphinx = '1.0'
|
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
# -- Project information -----------------------------------------------------
|
||||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
|
||||||
|
project = "{{ cookiecutter.project_name }}"
|
||||||
|
copyright = """{% now 'utc', '%Y' %}, {{ cookiecutter.author_name }}"""
|
||||||
|
author = "{{ cookiecutter.author_name }}"
|
||||||
|
|
||||||
|
|
||||||
|
# -- General configuration ---------------------------------------------------
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||||
|
# ones.
|
||||||
extensions = []
|
extensions = []
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ["_templates"]
|
templates_path = ["_templates"]
|
||||||
|
|
||||||
# The suffix of source filenames.
|
|
||||||
source_suffix = ".rst"
|
|
||||||
|
|
||||||
# The encoding of source files.
|
|
||||||
# source_encoding = 'utf-8-sig'
|
|
||||||
|
|
||||||
# The master toctree document.
|
|
||||||
master_doc = "index"
|
|
||||||
|
|
||||||
# General information about the project.
|
|
||||||
project = "{{ cookiecutter.project_name }}"
|
|
||||||
copyright = """{% now 'utc', '%Y' %}, {{ cookiecutter.author_name }}"""
|
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
|
||||||
# |version| and |release|, also used in various other places throughout the
|
|
||||||
# built documents.
|
|
||||||
#
|
|
||||||
# The short X.Y version.
|
|
||||||
version = "0.1"
|
|
||||||
# The full version, including alpha/beta/rc tags.
|
|
||||||
release = "0.1"
|
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
|
||||||
# for a list of supported languages.
|
|
||||||
# language = None
|
|
||||||
|
|
||||||
# There are two options for replacing |today|: either, you set today to some
|
|
||||||
# non-false value, then it is used:
|
|
||||||
# today = ''
|
|
||||||
# Else, today_fmt is used as the format for a strftime call.
|
|
||||||
# today_fmt = '%B %d, %Y'
|
|
||||||
|
|
||||||
# List of patterns, relative to source directory, that match files and
|
# List of patterns, relative to source directory, that match files and
|
||||||
# directories to ignore when looking for source files.
|
# directories to ignore when looking for source files.
|
||||||
exclude_patterns = ["_build"]
|
# This pattern also affects html_static_path and html_extra_path.
|
||||||
|
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
||||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
|
||||||
# default_role = None
|
|
||||||
|
|
||||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
|
||||||
# add_function_parentheses = True
|
|
||||||
|
|
||||||
# If true, the current module name will be prepended to all description
|
|
||||||
# unit titles (such as .. function::).
|
|
||||||
# add_module_names = True
|
|
||||||
|
|
||||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
|
||||||
# output. They are ignored by default.
|
|
||||||
# show_authors = False
|
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
|
||||||
pygments_style = "sphinx"
|
|
||||||
|
|
||||||
# A list of ignored prefixes for module index sorting.
|
|
||||||
# modindex_common_prefix = []
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output ---------------------------------------------------
|
# -- Options for HTML output -------------------------------------------------
|
||||||
|
|
||||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
# a list of builtin themes.
|
# a list of builtin themes.
|
||||||
html_theme = "default"
|
#
|
||||||
|
html_theme = "alabaster"
|
||||||
# Theme options are theme-specific and customize the look and feel of a theme
|
|
||||||
# further. For a list of options available for each theme, see the
|
|
||||||
# documentation.
|
|
||||||
# html_theme_options = {}
|
|
||||||
|
|
||||||
# Add any paths that contain custom themes here, relative to this directory.
|
|
||||||
# html_theme_path = []
|
|
||||||
|
|
||||||
# The name for this set of Sphinx documents. If None, it defaults to
|
|
||||||
# "<project> v<release> documentation".
|
|
||||||
# html_title = None
|
|
||||||
|
|
||||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
|
||||||
# html_short_title = None
|
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top
|
|
||||||
# of the sidebar.
|
|
||||||
# html_logo = None
|
|
||||||
|
|
||||||
# The name of an image file (within the static path) to use as favicon of the
|
|
||||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
|
||||||
# pixels large.
|
|
||||||
# html_favicon = None
|
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
html_static_path = ["_static"]
|
html_static_path = ["_static"]
|
||||||
|
|
||||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
|
||||||
# using the given strftime format.
|
|
||||||
# html_last_updated_fmt = '%b %d, %Y'
|
|
||||||
|
|
||||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
|
||||||
# typographically correct entities.
|
|
||||||
# html_use_smartypants = True
|
|
||||||
|
|
||||||
# Custom sidebar templates, maps document names to template names.
|
|
||||||
# html_sidebars = {}
|
|
||||||
|
|
||||||
# Additional templates that should be rendered to pages, maps page names to
|
|
||||||
# template names.
|
|
||||||
# html_additional_pages = {}
|
|
||||||
|
|
||||||
# If false, no module index is generated.
|
|
||||||
# html_domain_indices = True
|
|
||||||
|
|
||||||
# If false, no index is generated.
|
|
||||||
# html_use_index = True
|
|
||||||
|
|
||||||
# If true, the index is split into individual pages for each letter.
|
|
||||||
# html_split_index = False
|
|
||||||
|
|
||||||
# If true, links to the reST sources are added to the pages.
|
|
||||||
# html_show_sourcelink = True
|
|
||||||
|
|
||||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
|
||||||
# html_show_sphinx = True
|
|
||||||
|
|
||||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
|
||||||
# html_show_copyright = True
|
|
||||||
|
|
||||||
# If true, an OpenSearch description file will be output, and all pages will
|
|
||||||
# contain a <link> tag referring to it. The value of this option must be the
|
|
||||||
# base URL from which the finished HTML is served.
|
|
||||||
# html_use_opensearch = ''
|
|
||||||
|
|
||||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
|
||||||
# html_file_suffix = None
|
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
|
||||||
htmlhelp_basename = "{{ cookiecutter.project_slug }}doc"
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for LaTeX output --------------------------------------------------
|
|
||||||
|
|
||||||
latex_elements = {
|
|
||||||
# The paper size ('letterpaper' or 'a4paper').
|
|
||||||
# 'papersize': 'letterpaper',
|
|
||||||
# The font size ('10pt', '11pt' or '12pt').
|
|
||||||
# 'pointsize': '10pt',
|
|
||||||
# Additional stuff for the LaTeX preamble.
|
|
||||||
# 'preamble': '',
|
|
||||||
}
|
|
||||||
|
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
|
||||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
|
||||||
latex_documents = [
|
|
||||||
(
|
|
||||||
"index",
|
|
||||||
"{{ cookiecutter.project_slug }}.tex",
|
|
||||||
"{{ cookiecutter.project_name }} Documentation",
|
|
||||||
"""{{ cookiecutter.author_name }}""",
|
|
||||||
"manual",
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top of
|
|
||||||
# the title page.
|
|
||||||
# latex_logo = None
|
|
||||||
|
|
||||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
|
||||||
# not chapters.
|
|
||||||
# latex_use_parts = False
|
|
||||||
|
|
||||||
# If true, show page references after internal links.
|
|
||||||
# latex_show_pagerefs = False
|
|
||||||
|
|
||||||
# If true, show URL addresses after external links.
|
|
||||||
# latex_show_urls = False
|
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
|
||||||
# latex_appendices = []
|
|
||||||
|
|
||||||
# If false, no module index is generated.
|
|
||||||
# latex_domain_indices = True
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for manual page output --------------------------------------------
|
|
||||||
|
|
||||||
# One entry per manual page. List of tuples
|
|
||||||
# (source start file, name, description, authors, manual section).
|
|
||||||
man_pages = [
|
|
||||||
(
|
|
||||||
"index",
|
|
||||||
"{{ cookiecutter.project_slug }}",
|
|
||||||
"{{ cookiecutter.project_name }} Documentation",
|
|
||||||
["""{{ cookiecutter.author_name }}"""],
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
# If true, show URL addresses after external links.
|
|
||||||
# man_show_urls = False
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for Texinfo output ------------------------------------------------
|
|
||||||
|
|
||||||
# Grouping the document tree into Texinfo files. List of tuples
|
|
||||||
# (source start file, target name, title, author,
|
|
||||||
# dir menu entry, description, category)
|
|
||||||
texinfo_documents = [
|
|
||||||
(
|
|
||||||
"index",
|
|
||||||
"{{ cookiecutter.project_slug }}",
|
|
||||||
"{{ cookiecutter.project_name }} Documentation",
|
|
||||||
"""{{ cookiecutter.author_name }}""",
|
|
||||||
"{{ cookiecutter.project_name }}",
|
|
||||||
"""{{ cookiecutter.description }}""",
|
|
||||||
"Miscellaneous",
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
|
||||||
# texinfo_appendices = []
|
|
||||||
|
|
||||||
# If false, no module index is generated.
|
|
||||||
# texinfo_domain_indices = True
|
|
||||||
|
|
||||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
|
||||||
# texinfo_show_urls = 'footnote'
|
|
||||||
|
|
|
@ -3,17 +3,19 @@
|
||||||
You can adapt this file completely to your liking, but it should at least
|
You can adapt this file completely to your liking, but it should at least
|
||||||
contain the root `toctree` directive.
|
contain the root `toctree` directive.
|
||||||
|
|
||||||
{{ cookiecutter.project_name }} Project Documentation
|
Welcome to {{ cookiecutter.project_name }}'s documentation!
|
||||||
====================================================================
|
======================================================================
|
||||||
|
|
||||||
Table of Contents:
|
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
:caption: Contents:
|
||||||
|
|
||||||
|
pycharm/configuration
|
||||||
|
|
||||||
|
|
||||||
Indices & Tables
|
|
||||||
================
|
Indices and tables
|
||||||
|
==================
|
||||||
|
|
||||||
* :ref:`genindex`
|
* :ref:`genindex`
|
||||||
* :ref:`modindex`
|
* :ref:`modindex`
|
||||||
|
|
|
@ -1,190 +1,35 @@
|
||||||
@ECHO OFF
|
@ECHO OFF
|
||||||
|
|
||||||
|
pushd %~dp0
|
||||||
|
|
||||||
REM Command file for Sphinx documentation
|
REM Command file for Sphinx documentation
|
||||||
|
|
||||||
if "%SPHINXBUILD%" == "" (
|
if "%SPHINXBUILD%" == "" (
|
||||||
set SPHINXBUILD=sphinx-build
|
set SPHINXBUILD=sphinx-build
|
||||||
)
|
)
|
||||||
|
set SOURCEDIR=.
|
||||||
set BUILDDIR=_build
|
set BUILDDIR=_build
|
||||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
|
|
||||||
set I18NSPHINXOPTS=%SPHINXOPTS% .
|
|
||||||
if NOT "%PAPER%" == "" (
|
|
||||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
|
||||||
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "" goto help
|
if "%1" == "" goto help
|
||||||
|
|
||||||
if "%1" == "help" (
|
%SPHINXBUILD% >NUL 2>NUL
|
||||||
:help
|
if errorlevel 9009 (
|
||||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
|
||||||
echo. html to make standalone HTML files
|
|
||||||
echo. dirhtml to make HTML files named index.html in directories
|
|
||||||
echo. singlehtml to make a single large HTML file
|
|
||||||
echo. pickle to make pickle files
|
|
||||||
echo. json to make JSON files
|
|
||||||
echo. htmlhelp to make HTML files and a HTML help project
|
|
||||||
echo. qthelp to make HTML files and a qthelp project
|
|
||||||
echo. devhelp to make HTML files and a Devhelp project
|
|
||||||
echo. epub to make an epub
|
|
||||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
|
||||||
echo. text to make text files
|
|
||||||
echo. man to make manual pages
|
|
||||||
echo. texinfo to make Texinfo files
|
|
||||||
echo. gettext to make PO message catalogs
|
|
||||||
echo. changes to make an overview over all changed/added/deprecated items
|
|
||||||
echo. linkcheck to check all external links for integrity
|
|
||||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "clean" (
|
|
||||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
|
||||||
del /q /s %BUILDDIR%\*
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "html" (
|
|
||||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
echo.
|
||||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||||
goto end
|
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||||
|
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||||
|
echo.may add the Sphinx directory to PATH.
|
||||||
|
echo.
|
||||||
|
echo.If you don't have Sphinx installed, grab it from
|
||||||
|
echo.http://sphinx-doc.org/
|
||||||
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|
||||||
if "%1" == "dirhtml" (
|
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
goto end
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "singlehtml" (
|
:help
|
||||||
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "pickle" (
|
|
||||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can process the pickle files.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "json" (
|
|
||||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can process the JSON files.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "htmlhelp" (
|
|
||||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
|
||||||
.hhp project file in %BUILDDIR%/htmlhelp.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "qthelp" (
|
|
||||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
|
||||||
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
|
||||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\{{ cookiecutter.project_slug }}.qhcp
|
|
||||||
echo.To view the help file:
|
|
||||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\{{ cookiecutter.project_slug }}.ghc
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "devhelp" (
|
|
||||||
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "epub" (
|
|
||||||
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The epub file is in %BUILDDIR%/epub.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "latex" (
|
|
||||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "text" (
|
|
||||||
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The text files are in %BUILDDIR%/text.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "man" (
|
|
||||||
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The manual pages are in %BUILDDIR%/man.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "texinfo" (
|
|
||||||
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "gettext" (
|
|
||||||
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "changes" (
|
|
||||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.The overview file is in %BUILDDIR%/changes.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "linkcheck" (
|
|
||||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Link check complete; look for any errors in the above output ^
|
|
||||||
or in %BUILDDIR%/linkcheck/output.txt.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "doctest" (
|
|
||||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
|
||||||
if errorlevel 1 exit /b 1
|
|
||||||
echo.
|
|
||||||
echo.Testing of doctests in the sources finished, look at the ^
|
|
||||||
results in %BUILDDIR%/doctest/output.txt.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
:end
|
:end
|
||||||
|
popd
|
||||||
|
|
|
@ -36,12 +36,18 @@ After few seconds, all *Run/Debug Configurations* should be ready to use.
|
||||||
**Things you can do with provided configuration**:
|
**Things you can do with provided configuration**:
|
||||||
|
|
||||||
* run and debug python code
|
* run and debug python code
|
||||||
|
|
||||||
.. image:: images/f1.png
|
.. image:: images/f1.png
|
||||||
|
|
||||||
* run and debug tests
|
* run and debug tests
|
||||||
|
|
||||||
.. image:: images/f2.png
|
.. image:: images/f2.png
|
||||||
.. image:: images/f3.png
|
.. image:: images/f3.png
|
||||||
|
|
||||||
* run and debug migrations or different django management commands
|
* run and debug migrations or different django management commands
|
||||||
|
|
||||||
.. image:: images/f4.png
|
.. image:: images/f4.png
|
||||||
|
|
||||||
* and many others..
|
* and many others..
|
||||||
|
|
||||||
Known issues
|
Known issues
|
||||||
|
|
|
@ -42,6 +42,9 @@ services:
|
||||||
ports:
|
ports:
|
||||||
- "0.0.0.0:80:80"
|
- "0.0.0.0:80:80"
|
||||||
- "0.0.0.0:443:443"
|
- "0.0.0.0:443:443"
|
||||||
|
{%- if cookiecutter.use_celery == 'y' %}
|
||||||
|
- "0.0.0.0:5555:5555"
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:5.0
|
image: redis:5.0
|
||||||
|
@ -60,11 +63,11 @@ services:
|
||||||
flower:
|
flower:
|
||||||
<<: *django
|
<<: *django
|
||||||
image: {{ cookiecutter.project_slug }}_production_flower
|
image: {{ cookiecutter.project_slug }}_production_flower
|
||||||
ports:
|
|
||||||
- "5555:5555"
|
|
||||||
command: /start-flower
|
command: /start-flower
|
||||||
|
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
|
{% if cookiecutter.cloud_provider == 'AWS' %}
|
||||||
awscli:
|
awscli:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
|
@ -73,3 +76,4 @@ services:
|
||||||
- ./.envs/.production/.django
|
- ./.envs/.production/.django
|
||||||
volumes:
|
volumes:
|
||||||
- production_postgres_data_backups:/backups
|
- production_postgres_data_backups:/backups
|
||||||
|
{%- endif %}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
[pytest]
|
[pytest]
|
||||||
addopts = --ds=config.settings.test
|
addopts = --ds=config.settings.test --reuse-db
|
||||||
|
python_files = tests.py test_*.py
|
||||||
{%- if cookiecutter.js_task_runner != 'None' %}
|
{%- if cookiecutter.js_task_runner != 'None' %}
|
||||||
norecursedirs = node_modules
|
norecursedirs = node_modules
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
pytz==2019.1 # https://github.com/stub42/pytz
|
pytz==2019.3 # https://github.com/stub42/pytz
|
||||||
python-slugify==3.0.2 # https://github.com/un33k/python-slugify
|
python-slugify==4.0.0 # https://github.com/un33k/python-slugify
|
||||||
Pillow==6.1.0 # 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 %}
|
||||||
argon2-cffi==19.1.0 # https://github.com/hynek/argon2_cffi
|
argon2-cffi==19.2.0 # https://github.com/hynek/argon2_cffi
|
||||||
{%- if cookiecutter.use_whitenoise == 'y' %}
|
{%- if cookiecutter.use_whitenoise == 'y' %}
|
||||||
whitenoise==4.1.2 # https://github.com/evansd/whitenoise
|
whitenoise==5.0.1 # https://github.com/evansd/whitenoise
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
redis==3.2.1 # https://github.com/antirez/redis
|
redis==3.4.1 # https://github.com/andymccurdy/redis-py
|
||||||
{%- if cookiecutter.use_celery == "y" %}
|
{%- if cookiecutter.use_celery == "y" %}
|
||||||
celery==4.3.0 # pyup: < 5.0 # https://github.com/celery/celery
|
celery==4.4.2 # pyup: < 5.0 # https://github.com/celery/celery
|
||||||
django-celery-beat==1.5.0 # https://github.com/celery/django-celery-beat
|
django-celery-beat==2.0.0 # https://github.com/celery/django-celery-beat
|
||||||
{%- if cookiecutter.use_docker == 'y' %}
|
{%- if cookiecutter.use_docker == 'y' %}
|
||||||
flower==0.9.3 # https://github.com/mher/flower
|
flower==0.9.3 # https://github.com/mher/flower
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
@ -19,16 +19,16 @@ flower==0.9.3 # https://github.com/mher/flower
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
django==2.2.3 # pyup: < 3.0 # https://www.djangoproject.com/
|
django==2.2.11 # 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.2.0 # https://github.com/jazzband/django-model-utils
|
django-model-utils==4.0.0 # https://github.com/jazzband/django-model-utils
|
||||||
django-allauth==0.39.1 # https://github.com/pennersr/django-allauth
|
django-allauth==0.41.0 # 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.9.0 # 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.10.0 # https://github.com/niwinz/django-redis
|
django-redis==4.11.0 # https://github.com/niwinz/django-redis
|
||||||
|
{%- if cookiecutter.use_drf == "y" %}
|
||||||
# Django REST Framework
|
# Django REST Framework
|
||||||
djangorestframework==3.9.4 # https://github.com/encode/django-rest-framework
|
djangorestframework==3.11.0 # https://github.com/encode/django-rest-framework
|
||||||
coreapi==2.3.3 # https://github.com/core-api/python-client
|
{%- endif %}
|
||||||
|
|
|
@ -1,35 +1,38 @@
|
||||||
-r ./base.txt
|
-r ./base.txt
|
||||||
|
|
||||||
Werkzeug==0.14.1 # pyup: < 0.15 # https://github.com/pallets/werkzeug
|
Werkzeug==1.0.0 # https://github.com/pallets/werkzeug
|
||||||
ipdb==0.12 # https://github.com/gotcha/ipdb
|
ipdb==0.13.2 # https://github.com/gotcha/ipdb
|
||||||
Sphinx==2.1.2 # https://github.com/sphinx-doc/sphinx
|
Sphinx==2.4.4 # 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.3 # https://github.com/psycopg/psycopg2
|
psycopg2-binary==2.8.4 # https://github.com/psycopg/psycopg2
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
mypy==0.711 # https://github.com/python/mypy
|
mypy==0.770 # https://github.com/python/mypy
|
||||||
pytest==5.0.1 # https://github.com/pytest-dev/pytest
|
django-stubs==1.5.0 # https://github.com/typeddjango/django-stubs
|
||||||
|
pytest==5.3.5 # 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.8 # https://github.com/PyCQA/flake8
|
flake8==3.7.9 # https://github.com/PyCQA/flake8
|
||||||
coverage==4.5.3 # https://github.com/nedbat/coveragepy
|
flake8-isort==2.9.0 # https://github.com/gforcada/flake8-isort
|
||||||
black==19.3b0 # https://github.com/ambv/black
|
coverage==5.0.4 # https://github.com/nedbat/coveragepy
|
||||||
pylint-django==2.0.10 # https://github.com/PyCQA/pylint-django
|
black==19.10b0 # https://github.com/ambv/black
|
||||||
|
pylint-django==2.0.14 # 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==2.2.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.0 # https://github.com/jazzband/django-debug-toolbar
|
django-debug-toolbar==2.2 # https://github.com/jazzband/django-debug-toolbar
|
||||||
django-extensions==2.1.9 # https://github.com/django-extensions/django-extensions
|
django-extensions==2.2.8 # https://github.com/django-extensions/django-extensions
|
||||||
django-coverage-plugin==1.6.0 # https://github.com/nedbat/django_coverage_plugin
|
django-coverage-plugin==1.8.0 # https://github.com/nedbat/django_coverage_plugin
|
||||||
pytest-django==3.5.1 # https://github.com/pytest-dev/pytest-django
|
pytest-django==3.8.0 # https://github.com/pytest-dev/pytest-django
|
||||||
|
|
|
@ -2,20 +2,38 @@
|
||||||
|
|
||||||
-r ./base.txt
|
-r ./base.txt
|
||||||
|
|
||||||
gunicorn==19.9.0 # 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==0.6.2 # https://github.com/antonagestam/collectfast
|
Collectfast==2.1.0 # https://github.com/antonagestam/collectfast
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{%- if cookiecutter.use_sentry == "y" %}
|
{%- if cookiecutter.use_sentry == "y" %}
|
||||||
sentry-sdk==0.10.1 # https://github.com/getsentry/sentry-python
|
sentry-sdk==0.14.2 # 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.9.1 # https://github.com/jschneier/django-storages
|
||||||
{%- elif cookiecutter.cloud_provider == 'GCP' %}
|
{%- elif cookiecutter.cloud_provider == 'GCP' %}
|
||||||
django-storages[google]==1.7.1 # https://github.com/jschneier/django-storages
|
django-storages[google]==1.9.1 # https://github.com/jschneier/django-storages
|
||||||
|
{%- endif %}
|
||||||
|
{%- if cookiecutter.mail_service == 'Mailgun' %}
|
||||||
|
django-anymail[mailgun]==7.0.0 # https://github.com/anymail/django-anymail
|
||||||
|
{%- elif cookiecutter.mail_service == 'Amazon SES' %}
|
||||||
|
django-anymail[amazon_ses]==7.0.0 # https://github.com/anymail/django-anymail
|
||||||
|
{%- elif cookiecutter.mail_service == 'Mailjet' %}
|
||||||
|
django-anymail[mailjet]==7.0.0 # https://github.com/anymail/django-anymail
|
||||||
|
{%- elif cookiecutter.mail_service == 'Mandrill' %}
|
||||||
|
django-anymail[mandrill]==7.0.0 # https://github.com/anymail/django-anymail
|
||||||
|
{%- elif cookiecutter.mail_service == 'Postmark' %}
|
||||||
|
django-anymail[postmark]==7.0.0 # https://github.com/anymail/django-anymail
|
||||||
|
{%- elif cookiecutter.mail_service == 'Sendgrid' %}
|
||||||
|
django-anymail[sendgrid]==7.0.0 # https://github.com/anymail/django-anymail
|
||||||
|
{%- elif cookiecutter.mail_service == 'SendinBlue' %}
|
||||||
|
django-anymail[sendinblue]==7.0.0 # https://github.com/anymail/django-anymail
|
||||||
|
{%- elif cookiecutter.mail_service == 'SparkPost' %}
|
||||||
|
django-anymail[sparkpost]==7.0.0 # https://github.com/anymail/django-anymail
|
||||||
|
{%- elif cookiecutter.mail_service == 'Other SMTP' %}
|
||||||
|
django-anymail==7.0.0 # https://github.com/anymail/django-anymail
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
django-anymail[mailgun]==6.1.0 # https://github.com/anymail/django-anymail
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
python-3.6.8
|
python-3.7.6
|
||||||
|
|
|
@ -7,14 +7,16 @@ max-line-length = 120
|
||||||
exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules
|
exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules
|
||||||
|
|
||||||
[mypy]
|
[mypy]
|
||||||
python_version = 3.6
|
python_version = 3.7
|
||||||
check_untyped_defs = True
|
check_untyped_defs = True
|
||||||
ignore_errors = False
|
|
||||||
ignore_missing_imports = True
|
ignore_missing_imports = True
|
||||||
strict_optional = 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]
|
||||||
|
django_settings_module = config.settings.test
|
||||||
|
|
||||||
[mypy-*.migrations.*]
|
[mypy-*.migrations.*]
|
||||||
# Django migrations should not produce any errors:
|
# Django migrations should not produce any errors:
|
||||||
|
|
|
@ -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
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<form method="POST" action=".">
|
<form method="POST" action=".">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form|crispy }}
|
{{ form|crispy }}
|
||||||
<input type="submit" name="action" value="{% trans 'change password' %}"/>
|
<input class="btn btn-primary" type="submit" name="action" value="{% trans 'change password' %}"/>
|
||||||
</form>
|
</form>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>{% trans 'Your password is now changed.' %}</p>
|
<p>{% trans 'Your password is now changed.' %}</p>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<form method="POST" action="{% url 'account_set_password' %}" class="password_set">
|
<form method="POST" action="{% url 'account_set_password' %}" class="password_set">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form|crispy }}
|
{{ form|crispy }}
|
||||||
<input type="submit" name="action" value="{% trans 'Set Password' %}"/>
|
<input class="btn btn-primary" type="submit" name="action" value="{% trans 'Set Password' %}"/>
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
|
@ -80,7 +80,7 @@
|
||||||
|
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
{% for message in messages %}
|
{% for message in messages %}
|
||||||
<div class="alert {% if message.tags %}alert-{{ message.tags }}{% endif %}">{{ message }}</div>
|
<div class="alert {% if message.tags %}alert-{{ message.tags }}{% endif %}">{{ message }}<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button></div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
{{ form|crispy }}
|
{{ form|crispy }}
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<button type="submit" class="btn">Update</button>
|
<button type="submit" class="btn btn-primary">Update</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
{% raw %}{% extends "base.html" %}
|
|
||||||
{% load static i18n %}
|
|
||||||
{% block title %}Members{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="container">
|
|
||||||
<h2>Users</h2>
|
|
||||||
|
|
||||||
<div class="list-group">
|
|
||||||
{% for user in user_list %}
|
|
||||||
<a href="{% url 'users:detail' user.username %}" class="list-group-item">
|
|
||||||
<h4 class="list-group-item-heading">{{ user.username }}</h4>
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock content %}{% endraw %}
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from {{ cookiecutter.project_slug }}.users.models import User
|
||||||
|
|
||||||
|
|
||||||
|
class UserSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ["username", "email", "name", "url"]
|
||||||
|
|
||||||
|
extra_kwargs = {
|
||||||
|
"url": {"view_name": "api:user-detail", "lookup_field": "username"}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from rest_framework import status
|
||||||
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin, UpdateModelMixin
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.viewsets import GenericViewSet
|
||||||
|
|
||||||
|
from .serializers import UserSerializer
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class UserViewSet(RetrieveModelMixin, ListModelMixin, UpdateModelMixin, GenericViewSet):
|
||||||
|
serializer_class = UserSerializer
|
||||||
|
queryset = User.objects.all()
|
||||||
|
lookup_field = "username"
|
||||||
|
|
||||||
|
def get_queryset(self, *args, **kwargs):
|
||||||
|
return self.queryset.filter(id=self.request.user.id)
|
||||||
|
|
||||||
|
@action(detail=False, methods=["GET"])
|
||||||
|
def me(self, request):
|
||||||
|
serializer = UserSerializer(request.user, context={"request": request})
|
||||||
|
return Response(status=status.HTTP_200_OK, data=serializer.data)
|
|
@ -1,4 +1,4 @@
|
||||||
from django.contrib.auth import get_user_model, forms
|
from django.contrib.auth import forms, get_user_model
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
|
@ -12,14 +12,18 @@ 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 = (
|
||||||
"password",
|
extracted
|
||||||
length=42,
|
if extracted
|
||||||
special_chars=True,
|
else Faker(
|
||||||
digits=True,
|
"password",
|
||||||
upper_case=True,
|
length=42,
|
||||||
lower_case=True,
|
special_chars=True,
|
||||||
).generate(extra_kwargs={})
|
digits=True,
|
||||||
|
upper_case=True,
|
||||||
|
lower_case=True,
|
||||||
|
).generate(extra_kwargs={})
|
||||||
|
)
|
||||||
self.set_password(password)
|
self.set_password(password)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -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}/"
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import pytest
|
import pytest
|
||||||
from celery.result import EagerResult
|
from celery.result import EagerResult
|
||||||
|
|
||||||
|
|
||||||
from {{ cookiecutter.project_slug }}.users.tasks import get_users_count
|
from {{ cookiecutter.project_slug }}.users.tasks import get_users_count
|
||||||
from {{ cookiecutter.project_slug }}.users.tests.factories import UserFactory
|
from {{ cookiecutter.project_slug }}.users.tests.factories import UserFactory
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import pytest
|
import pytest
|
||||||
from django.conf import settings
|
from django.urls import resolve, reverse
|
||||||
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}/"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
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_detail_view,
|
||||||
user_redirect_view,
|
user_redirect_view,
|
||||||
user_update_view,
|
user_update_view,
|
||||||
user_detail_view,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
app_name = "users"
|
app_name = "users"
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
from django.contrib import messages
|
||||||
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.utils.translation import ugettext_lazy as _
|
||||||
from django.views.generic import DetailView, RedirectView, UpdateView
|
from django.views.generic import DetailView, RedirectView, UpdateView
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
@ -27,6 +29,12 @@ class UserUpdateView(LoginRequiredMixin, UpdateView):
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
return User.objects.get(username=self.request.user.username)
|
return User.objects.get(username=self.request.user.username)
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
messages.add_message(
|
||||||
|
self.request, messages.INFO, _("Infos successfully updated")
|
||||||
|
)
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
user_update_view = UserUpdateView.as_view()
|
user_update_view = UserUpdateView.as_view()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
def settings_context(_request):
|
||||||
|
return {"settings": settings}
|
Loading…
Reference in New Issue
Block a user