mirror of
https://github.com/cookiecutter/cookiecutter-django.git
synced 2025-03-03 10:45:49 +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
|
||||
|
||||
python: 3.6
|
||||
python: 3.7
|
||||
|
||||
before_install:
|
||||
- docker-compose -v
|
||||
|
@ -14,11 +14,7 @@ before_install:
|
|||
matrix:
|
||||
include:
|
||||
- name: Test results
|
||||
script: tox -e py36
|
||||
- name: Run flake8 on result
|
||||
script: tox -e flake8
|
||||
- name: Run black on result
|
||||
script: tox -e black
|
||||
script: tox -e py37
|
||||
- name: Black template
|
||||
script: tox -e black-template
|
||||
- name: Basic Docker
|
||||
|
|
64
CHANGELOG.md
64
CHANGELOG.md
|
@ -1,6 +1,68 @@
|
|||
# Change Log
|
||||
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]
|
||||
### 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
|
||||
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::
|
||||
|
||||
|
|
|
@ -42,12 +42,14 @@ Listed in alphabetical order.
|
|||
Name Github Twitter
|
||||
========================== ============================ ==============
|
||||
18 `@dezoito`_
|
||||
2O4 `@2O4`_
|
||||
a7p `@a7p`_
|
||||
Aaron Eikenberry `@aeikenberry`_
|
||||
Adam Bogdał `@bogdal`_
|
||||
Adam Dobrawy `@ad-m`_
|
||||
Adam Steele `@adammsteele`_
|
||||
Agam Dua
|
||||
Agustín Scaramuzza `@scaramagus`_ @scaramagus
|
||||
Alberto Sanchez `@alb3rto`_
|
||||
Alex Tsai `@caffodian`_
|
||||
Alvaro [Andor] `@andor-pierdelacabeza`_
|
||||
|
@ -55,6 +57,7 @@ Listed in alphabetical order.
|
|||
Andreas Meistad `@ameistad`_
|
||||
Andres Gonzalez `@andresgz`_
|
||||
Andrew Mikhnevich `@zcho`_
|
||||
Andrew Chen Wang `@Andrew-Chen-Wang`_
|
||||
Andy Rose
|
||||
Anna Callahan `@jazztpt`_
|
||||
Anna Sidwell `@takkaria`_
|
||||
|
@ -70,9 +73,12 @@ Listed in alphabetical order.
|
|||
Benjamin Abel
|
||||
Bert de Miranda `@bertdemiranda`_
|
||||
Bo Lopker `@blopker`_
|
||||
Bo Peng `@BoPeng`_
|
||||
Bouke Haarsma
|
||||
Brent Payne `@brentpayne`_ @brentpayne
|
||||
Bruce Olivier `@bolivierjr`_
|
||||
Burhan Khalid `@burhan`_ @burhan
|
||||
Caio Ariede `@caioariede`_ @caioariede
|
||||
Carl Johnson `@carlmjohnson`_ @carlmjohnson
|
||||
Catherine Devlin `@catherinedevlin`_
|
||||
Cédric Gaspoz `@cgaspoz`_
|
||||
|
@ -83,14 +89,16 @@ Listed in alphabetical order.
|
|||
Chris Pappalardo `@ChrisPappalardo`_
|
||||
Christopher Clarke `@chrisdev`_
|
||||
Cole Mackenzie `@cmackenzie1`_
|
||||
Cole Maclean `@cole`_ @cole
|
||||
Collederas `@Collederas`_
|
||||
Craig Margieson `@cmargieson`_
|
||||
Cristian Vargas `@cdvv7788`_
|
||||
Cullen Rhodes `@c-rhodes`_
|
||||
Curtis St Pierre `@curtisstpierre`_ @cstpierre1388
|
||||
Dan Shultz `@shultz`_
|
||||
Dani Hodovic `@danihodovic`
|
||||
Dani Hodovic `@danihodovic`_
|
||||
Daniel Hepper `@dhepper`_ @danielhepper
|
||||
Daniel Hillier `@danifus`_
|
||||
Daniele Tricoli `@eriol`_
|
||||
David Díaz `@ddiazpinto`_ @DavidDiazPinto
|
||||
Davit Tovmasyan `@davitovmasyan`_
|
||||
|
@ -99,6 +107,7 @@ Listed in alphabetical order.
|
|||
Demetris Stavrou `@demestav`_
|
||||
Denis Bobrov `@delneg`_
|
||||
Denis Orehovsky `@apirobot`_
|
||||
Denis Savran `@blaxpy`_
|
||||
Diane Chen `@purplediane`_ @purplediane88
|
||||
Dónal Adams `@epileptic-fish`_
|
||||
Dong Huynh `@trungdong`_
|
||||
|
@ -107,17 +116,26 @@ Listed in alphabetical order.
|
|||
Eric Groom `@ericgroom`_
|
||||
Eyad Al Sibai `@eyadsibai`_
|
||||
Felipe Arruda `@arruda`_
|
||||
Florian Idelberger `@step21`_ @windrush
|
||||
Garry Cairns `@garry-cairns`_
|
||||
Garry Polley `@garrypolley`_
|
||||
Gilbishkosma `@Gilbishkosma`_
|
||||
Guilherme Guy `@guilherme1guy`_
|
||||
Hamish Durkin `@durkode`_
|
||||
Hana Quadara `@hanaquadara`_
|
||||
Harry Moreno `@morenoh149`_ @morenoh149
|
||||
Harry Percival `@hjwp`_
|
||||
Hendrik Schneider `@hendrikschneider`_
|
||||
Henrique G. G. Pereira `@ikkebr`_
|
||||
Howie Zhao `@howiezhao`_
|
||||
Ian Lee `@IanLee1521`_
|
||||
Irfan Ahmad `@erfaan`_ @erfaan
|
||||
Isaac12x `@Isaac12x`_
|
||||
Ivan Khomutov `@ikhomutov`_
|
||||
James Williams `@jameswilliams1`_
|
||||
Jan Van Bruggen `@jvanbrug`_
|
||||
Jelmer Draaijer `@foarsitter`_
|
||||
Jerome Caisip `@jeromecaisip`_
|
||||
Jens Nilsson `@phiberjenz`_
|
||||
Jerome Leclanche `@jleclanche`_ @Adys
|
||||
Jimmy Gitonga `@afrowave`_ @afrowave
|
||||
|
@ -135,6 +153,7 @@ Listed in alphabetical order.
|
|||
Keyvan Mosharraf `@keyvanm`_
|
||||
Krzysztof Szumny `@noisy`_
|
||||
Krzysztof Żuraw `@krzysztofzuraw`_
|
||||
Leo won `@leollon`_
|
||||
Leo Zhou `@glasslion`_
|
||||
Leonardo Jimenez `@xpostudio4`_
|
||||
Lin Xianyi `@iynaix`_
|
||||
|
@ -155,11 +174,14 @@ Listed in alphabetical order.
|
|||
Meghan Heintz `@dot2dotseurat`_
|
||||
Mesut Yılmaz `@myilmaz`_
|
||||
Michael Gecht `@mimischi`_ @_mischi
|
||||
Michael Samoylov `@msamoylov`_
|
||||
Min ho Kim `@minho42`_
|
||||
mozillazg `@mozillazg`_
|
||||
Nico Stefani `@nicolas471`_ @moby_dick91
|
||||
Oleg Russkin `@rolep`_
|
||||
Pablo `@oubiga`_
|
||||
Parbhat Puri `@parbhat`_
|
||||
Pawan Chaurasia `@rjsnh1522`_
|
||||
Peter Bittner `@bittner`_
|
||||
Peter Coles `@mrcoles`_
|
||||
Philipp Matthies `@canonnervio`_
|
||||
|
@ -190,6 +212,7 @@ Listed in alphabetical order.
|
|||
Tubo Shi `@Tubo`_
|
||||
Umair Ashraf `@umrashrf`_ @fabumair
|
||||
Vadim Iskuchekov `@Egregors`_ @egregors
|
||||
Vicente G. Reyes `@reyesvicente`_ @highcenburg
|
||||
Vitaly Babiy
|
||||
Vivian Guillen `@viviangb`_
|
||||
Vlad Doster `@vladdoster`_
|
||||
|
@ -197,9 +220,11 @@ Listed in alphabetical order.
|
|||
William Archinal `@archinal`_
|
||||
Xaver Y.R. Chen `@yrchen`_ @yrchen
|
||||
Yaroslav Halchenko
|
||||
Yuchen Xie `@mapx`_
|
||||
========================== ============================ ==============
|
||||
|
||||
.. _@a7p: https://github.com/a7p
|
||||
.. _@2O4: https://github.com/2O4
|
||||
.. _@ad-m: https://github.com/ad-m
|
||||
.. _@adammsteele: https://github.com/adammsteele
|
||||
.. _@aeikenberry: https://github.com/aeikenberry
|
||||
|
@ -211,15 +236,19 @@ Listed in alphabetical order.
|
|||
.. _@andor-pierdelacabeza: https://github.com/andor-pierdelacabeza
|
||||
.. _@andresgz: https://github.com/andresgz
|
||||
.. _@antoniablair: https://github.com/antoniablair
|
||||
.. _@Andrew-Chen-Wang: https://github.com/Andrew-Chen-Wang
|
||||
.. _@apirobot: https://github.com/apirobot
|
||||
.. _@archinal: https://github.com/archinal
|
||||
.. _@areski: https://github.com/areski
|
||||
.. _@arruda: https://github.com/arruda
|
||||
.. _@bertdemiranda: https://github.com/bertdemiranda
|
||||
.. _@bittner: https://github.com/bittner
|
||||
.. _@blaxpy: https://github.com/blaxpy
|
||||
.. _@bloodpet: https://github.com/bloodpet
|
||||
.. _@blopker: https://github.com/blopker
|
||||
.. _@bogdal: https://github.com/bogdal
|
||||
.. _@bolivierjr: https://github.com/bolivierjr
|
||||
.. _@BoPeng: https://github.com/BoPeng
|
||||
.. _@brentpayne: https://github.com/brentpayne
|
||||
.. _@btknu: https://github.com/btknu
|
||||
.. _@burhan: https://github.com/burhan
|
||||
|
@ -227,6 +256,7 @@ Listed in alphabetical order.
|
|||
.. _@c-rhodes: https://github.com/c-rhodes
|
||||
.. _@caffodian: https://github.com/caffodian
|
||||
.. _@canonnervio: https://github.com/canonnervio
|
||||
.. _@caioariede: https://github.com/caioariede
|
||||
.. _@carlmjohnson: https://github.com/carlmjohnson
|
||||
.. _@catherinedevlin: https://github.com/catherinedevlin
|
||||
.. _@ccurvey: https://github.com/ccurvey
|
||||
|
@ -237,10 +267,12 @@ Listed in alphabetical order.
|
|||
.. _@chuckus: https://github.com/chuckus
|
||||
.. _@cmackenzie1: https://github.com/cmackenzie1
|
||||
.. _@cmargieson: https://github.com/cmargieson
|
||||
.. _@cole: https://github.com/cole
|
||||
.. _@Collederas: https://github.com/Collederas
|
||||
.. _@curtisstpierre: https://github.com/curtisstpierre
|
||||
.. _@dadokkio: https://github.com/dadokkio
|
||||
.. _@danihodovic: https://github.com/danihodovic
|
||||
.. _@danifus: https://github.com/danifus
|
||||
.. _@davitovmasyan: https://github.com/davitovmasyan
|
||||
.. _@ddiazpinto: https://github.com/ddiazpinto
|
||||
.. _@delneg: https://github.com/delneg
|
||||
|
@ -249,6 +281,7 @@ Listed in alphabetical order.
|
|||
.. _@dhepper: https://github.com/dhepper
|
||||
.. _@dot2dotseurat: https://github.com/dot2dotseurat
|
||||
.. _@dsclementsen: https://github.com/dsclementsen
|
||||
.. _@guilherme1guy: https://github.com/guilherme1guy
|
||||
.. _@durkode: https://github.com/durkode
|
||||
.. _@Egregors: https://github.com/Egregors
|
||||
.. _@epileptic-fish: https://gihub.com/epileptic-fish
|
||||
|
@ -261,6 +294,7 @@ Listed in alphabetical order.
|
|||
.. _@foarsitter: https://github.com/foarsitter
|
||||
.. _@garry-cairns: https://github.com/garry-cairns
|
||||
.. _@garrypolley: https://github.com/garrypolley
|
||||
.. _@Gilbishkosma: https://github.com/Gilbishkosma
|
||||
.. _@glasslion: https://github.com/glasslion
|
||||
.. _@goldhand: https://github.com/goldhand
|
||||
.. _@hackebrot: https://github.com/hackebrot
|
||||
|
@ -268,12 +302,17 @@ Listed in alphabetical order.
|
|||
.. _@hanaquadara: https://github.com/hanaquadara
|
||||
.. _@hendrikschneider: https://github.com/hendrikschneider
|
||||
.. _@hjwp: https://github.com/hjwp
|
||||
.. _@howiezhao: https://github.com/howiezhao
|
||||
.. _@IanLee1521: https://github.com/IanLee1521
|
||||
.. _@ikhomutov: https://github.com/ikhomutov
|
||||
.. _@jameswilliams1: https://github.com/jameswilliams1
|
||||
.. _@ikkebr: https://github.com/ikkebr
|
||||
.. _@Isaac12x: https://github.com/Isaac12x
|
||||
.. _@iynaix: https://github.com/iynaix
|
||||
.. _@jangeador: https://github.com/jangeador
|
||||
.. _@jazztpt: https://github.com/jazztpt
|
||||
.. _@jcass77: https://github.com/jcass77
|
||||
.. _@jeromecaisip: https://github.com/jeromecaisip
|
||||
.. _@jleclanche: https://github.com/jleclanche
|
||||
.. _@jules-ch: https://github.com/jules-ch
|
||||
.. _@juliocc: https://github.com/juliocc
|
||||
|
@ -286,7 +325,9 @@ Listed in alphabetical order.
|
|||
.. _@keyvanm: https://github.com/keyvanm
|
||||
.. _@knitatoms: https://github.com/knitatoms
|
||||
.. _@krzysztofzuraw: https://github.com/krzysztofzuraw
|
||||
.. _@leollon: https://github.com/leollon
|
||||
.. _@MathijsHoogland: https://github.com/MathijsHoogland
|
||||
.. _@mapx: https://github.com/mapx
|
||||
.. _@mattayes: https://github.com/mattayes
|
||||
.. _@menzenski: https://github.com/menzenski
|
||||
.. _@mfwarren: https://github.com/mfwarren
|
||||
|
@ -295,24 +336,30 @@ Listed in alphabetical order.
|
|||
.. _@minho42: https://github.com/minho42
|
||||
.. _@mjsisley: https://github.com/mjsisley
|
||||
.. _@mknapper1: https://github.com/mknapper1
|
||||
.. _@morenoh149: https://github.com/morenoh149
|
||||
.. _@mostaszewski: https://github.com/mostaszewski
|
||||
.. _@mozillazg: https://github.com/mozillazg
|
||||
.. _@mrcoles: https://github.com/mrcoles
|
||||
.. _@msaizar: https://github.com/msaizar
|
||||
.. _@msamoylov: https://github.com/msamoylov
|
||||
.. _@myilmaz: https://github.com/myilmaz
|
||||
.. _@nicolas471: https://github.com/nicolas471
|
||||
.. _@noisy: https://github.com/noisy
|
||||
.. _@originell: https://github.com/originell
|
||||
.. _@oubiga: https://github.com/oubiga
|
||||
.. _@parbhat: https://github.com/parbhat
|
||||
.. _@rjsnh1522: https://github.com/rjsnh1522
|
||||
.. _@pchiquet: https://github.com/pchiquet
|
||||
.. _@phiberjenz: https://github.com/phiberjenz
|
||||
.. _@purplediane: https://github.com/purplediane
|
||||
.. _@raonyguimaraes: https://github.com/raonyguimaraes
|
||||
.. _@reggieriser: https://github.com/reggieriser
|
||||
.. _@reyesvicente: https://github.com/reyesvicente
|
||||
.. _@rm--: https://github.com/rm--
|
||||
.. _@rolep: https://github.com/rolep
|
||||
.. _@romanosipenko: https://github.com/romanosipenko
|
||||
.. _@saschalalala: https://github.com/saschalalala
|
||||
.. _@scaramagus: https://github.com/scaramagus
|
||||
.. _@shireenrao: https://github.com/shireenrao
|
||||
.. _@show0k: https://github.com/show0k
|
||||
.. _@shultz: https://github.com/shultz
|
||||
|
@ -320,6 +367,7 @@ Listed in alphabetical order.
|
|||
.. _@sladinji: https://github.com/sladinji
|
||||
.. _@slafs: https://github.com/slafs
|
||||
.. _@ssteinerX: https://github.com/ssteinerx
|
||||
.. _@step21: https://github.com/step21
|
||||
.. _@stepmr: https://github.com/stepmr
|
||||
.. _@suledev: https://github.com/suledev
|
||||
.. _@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/
|
||||
:alt: Updates
|
||||
|
||||
.. image:: https://badges.gitter.im/Join Chat.svg
|
||||
:target: https://gitter.im/pydanny/cookiecutter-django?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
.. image:: https://img.shields.io/badge/cookiecutter-Join%20on%20Slack-green?style=flat&logo=slack
|
||||
:target: https://join.slack.com/t/cookie-cutter/shared_invite/enQtNzI0Mzg5NjE5Nzk5LTRlYWI2YTZhYmQ4YmU1Y2Q2NmE1ZjkwOGM0NDQyNTIwY2M4ZTgyNDVkNjMxMDdhZGI5ZGE5YmJjM2M3ODJlY2U
|
||||
|
||||
.. image:: https://www.codetriage.com/pydanny/cookiecutter-django/badges/users.svg
|
||||
:target: https://www.codetriage.com/pydanny/cookiecutter-django
|
||||
|
@ -37,7 +37,7 @@ Features
|
|||
---------
|
||||
|
||||
* For Django 2.2
|
||||
* Works with Python 3.6
|
||||
* Works with Python 3.7
|
||||
* Renders Django projects with 100% starting test coverage
|
||||
* Twitter Bootstrap_ v4 (`maintained Foundation fork`_ also available)
|
||||
* 12-Factor_ based settings via django-environ_
|
||||
|
@ -46,13 +46,14 @@ Features
|
|||
* Registration via django-allauth_
|
||||
* Comes with custom user model ready to go
|
||||
* 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
|
||||
* Docker support using docker-compose_ for development and production (using Traefik_ with LetsEncrypt_ support)
|
||||
* Procfile_ for deploying to Heroku
|
||||
* Instructions for deploying to PythonAnywhere_
|
||||
* Run tests with unittest or pytest
|
||||
* 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
|
||||
|
||||
|
@ -84,6 +85,7 @@ Optional Integrations
|
|||
.. _PythonAnywhere: https://www.pythonanywhere.com/
|
||||
.. _Traefik: https://traefik.io/
|
||||
.. _LetsEncrypt: https://letsencrypt.org/
|
||||
.. _pre-commit: https://github.com/pre-commit/pre-commit
|
||||
|
||||
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:
|
||||
|
||||
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
|
||||
:name: Two Scoops of Django 1.11 Cover
|
||||
.. image:: https://cdn.shopify.com/s/files/1/0304/6901/files/Django-Crash-Course-300x436.jpg
|
||||
:name: Django Crash Course: Covers Django 3.0 and Python 3.8
|
||||
:align: center
|
||||
:alt: Two Scoops of Django
|
||||
:target: http://twoscoopspress.com/products/two-scoops-of-django-1-11
|
||||
:alt: Django Crash Course
|
||||
: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
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
@ -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::
|
||||
|
||||
$ pip install "cookiecutter>=1.4.0"
|
||||
$ pip install "cookiecutter>=1.7.0"
|
||||
|
||||
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.
|
||||
* 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
|
||||
.. _`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
|
||||
--------------------------------------------
|
||||
|
|
|
@ -33,6 +33,18 @@
|
|||
"GCP",
|
||||
"None"
|
||||
],
|
||||
"mail_service": [
|
||||
"Mailgun",
|
||||
"Amazon SES",
|
||||
"Mailjet",
|
||||
"Mandrill",
|
||||
"Postmark",
|
||||
"Sendgrid",
|
||||
"SendinBlue",
|
||||
"SparkPost",
|
||||
"Other SMTP"
|
||||
],
|
||||
"use_drf": "n",
|
||||
"custom_bootstrap_compilation": "n",
|
||||
"use_compressor": "n",
|
||||
"use_celery": "n",
|
||||
|
@ -40,7 +52,11 @@
|
|||
"use_sentry": "n",
|
||||
"use_whitenoise": "n",
|
||||
"use_heroku": "n",
|
||||
"use_travisci": "n",
|
||||
"ci_tool": [
|
||||
"None",
|
||||
"Travis",
|
||||
"Gitlab"
|
||||
],
|
||||
"keep_local_envs_in_vcs": "y",
|
||||
|
||||
"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
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -25,7 +25,9 @@ Provided you have opted for Celery (via setting ``use_celery`` to ``y``) there a
|
|||
|
||||
* ``celeryworker`` running a Celery worker 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
|
||||
|
||||
|
@ -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::
|
||||
|
||||
supervisorctl reread
|
||||
supervisorctl update
|
||||
supervisorctl start {{cookiecutter.project_slug}}
|
||||
|
||||
For status check, run::
|
||||
|
|
|
@ -9,7 +9,7 @@ Setting Up Development Environment
|
|||
|
||||
Make sure to have the following on your host:
|
||||
|
||||
* Python 3.6
|
||||
* Python 3.7
|
||||
* PostgreSQL_.
|
||||
* Redis_, if using Celery
|
||||
|
||||
|
@ -17,7 +17,7 @@ First things first.
|
|||
|
||||
#. Create a virtualenv: ::
|
||||
|
||||
$ python3.6 -m venv <virtual env path>
|
||||
$ python3.7 -m venv <virtual env path>
|
||||
|
||||
#. Activate the virtualenv you have just created: ::
|
||||
|
||||
|
@ -26,6 +26,12 @@ First things first.
|
|||
#. Install development requirements: ::
|
||||
|
||||
$ 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_: ::
|
||||
|
||||
|
|
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
|
||||
linters
|
||||
testing
|
||||
document
|
||||
deployment-on-pythonanywhere
|
||||
deployment-on-heroku
|
||||
deployment-with-docker
|
||||
|
|
|
@ -70,6 +70,22 @@ cloud_provider:
|
|||
|
||||
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:
|
||||
Indicates whether the project should support Bootstrap recompilation
|
||||
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
|
||||
to Heroku_.
|
||||
|
||||
use_travisci:
|
||||
Indicates whether the project should be configured to use `Travis CI`_.
|
||||
ci_tool:
|
||||
Select a CI tool for running tests. The choices are:
|
||||
|
||||
1. None
|
||||
2. Travis_
|
||||
3. Gitlab_
|
||||
|
||||
keep_local_envs_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/
|
||||
.. _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
|
||||
|
||||
.. _Celery: https://github.com/celery/celery
|
||||
|
@ -138,3 +170,6 @@ debug:
|
|||
.. _Heroku: https://github.com/heroku/heroku-buildpack-python
|
||||
|
||||
.. _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_DOMAIN MAILGUN_SENDER_DOMAIN n/a raises error
|
||||
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: ::
|
||||
|
||||
$ 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.
|
||||
|
||||
|
@ -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: ::
|
||||
|
||||
$ 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: ::
|
||||
|
||||
$ docker-compose -f local.yml run django coverage report
|
||||
$ docker-compose -f local.yml run --rm django coverage report
|
||||
|
||||
.. 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.
|
||||
|
||||
.. _Pytest: https://docs.pytest.org/en/latest/example/simple.html
|
||||
.. _develop locally: ../developing-locally.rst
|
||||
.. _develop locally with docker: ..../developing-locally-docker.rst
|
||||
.. _develop locally: ./developing-locally.html
|
||||
.. _develop locally with docker: ./developing-locally-docker.html
|
||||
.. _customize: https://docs.pytest.org/en/latest/customize.html
|
||||
.. _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:
|
||||
if (
|
||||
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
|
||||
continue
|
||||
|
@ -105,6 +105,10 @@ def remove_dottravisyml_file():
|
|||
os.remove(".travis.yml")
|
||||
|
||||
|
||||
def remove_dotgitlabciyml_file():
|
||||
os.remove(".gitlab-ci.yml")
|
||||
|
||||
|
||||
def append_to_project_gitignore(path):
|
||||
gitignore_file_path = ".gitignore"
|
||||
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"))
|
||||
|
||||
|
||||
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():
|
||||
debug = "{{ cookiecutter.debug }}".lower() == "y"
|
||||
|
||||
|
@ -302,6 +315,12 @@ def main():
|
|||
else:
|
||||
remove_docker_files()
|
||||
|
||||
if (
|
||||
"{{ cookiecutter.use_docker }}".lower() == "y"
|
||||
and "{{ cookiecutter.cloud_provider}}".lower() != "aws"
|
||||
):
|
||||
remove_aws_dockerfile()
|
||||
|
||||
if "{{ cookiecutter.use_heroku }}".lower() == "n":
|
||||
remove_heroku_files()
|
||||
|
||||
|
@ -339,9 +358,15 @@ def main():
|
|||
if "{{ cookiecutter.use_docker }}".lower() == "y":
|
||||
remove_celery_compose_dirs()
|
||||
|
||||
if "{{ cookiecutter.use_travisci }}".lower() == "n":
|
||||
if "{{ cookiecutter.ci_tool }}".lower() != "travis":
|
||||
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)
|
||||
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ if "{{ cookiecutter.use_docker }}".lower() == "n":
|
|||
if python_major_version == 2:
|
||||
print(
|
||||
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"])
|
||||
while True:
|
||||
|
@ -59,3 +59,12 @@ if "{{ cookiecutter.use_docker }}".lower() == "n":
|
|||
)
|
||||
+ 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]
|
||||
addopts = -x --tb=short
|
||||
addopts = -v --tb=short
|
||||
python_paths = .
|
||||
norecursedirs = .tox .git */migrations/* */static/* docs venv */{{cookiecutter.project_slug}}/*
|
||||
markers =
|
||||
flake8: Run flake8 on all possible template combinations
|
||||
black: Run black on all possible template combinations
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
cookiecutter==1.6.0
|
||||
cookiecutter==1.7.0
|
||||
sh==1.12.14
|
||||
binaryornot==0.4.4
|
||||
|
||||
# Code quality
|
||||
# ------------------------------------------------------------------------------
|
||||
black==19.3b0
|
||||
flake8==3.7.8
|
||||
black==19.10b0
|
||||
flake8==3.7.9
|
||||
flake8-isort==2.9.0
|
||||
|
||||
# Testing
|
||||
# ------------------------------------------------------------------------------
|
||||
tox==3.13.2
|
||||
pytest==5.0.1
|
||||
pytest_cases==1.10.1
|
||||
pytest-cookies==0.4.0
|
||||
pytest-xdist==1.29.0
|
||||
pyyaml==5.1.1
|
||||
tox==3.14.5
|
||||
pytest==5.4.1
|
||||
pytest-cookies==0.5.1
|
||||
pytest-instafail==0.4.1.post0
|
||||
pyyaml==5.3
|
||||
|
|
2
setup.py
2
setup.py
|
@ -40,7 +40,7 @@ setup(
|
|||
"License :: OSI Approved :: BSD License",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Topic :: Software Development",
|
||||
],
|
||||
|
|
|
@ -3,7 +3,6 @@ import re
|
|||
|
||||
import pytest
|
||||
from cookiecutter.exceptions import FailedHookException
|
||||
from pytest_cases import pytest_fixture_plus
|
||||
import sh
|
||||
import yaml
|
||||
from binaryornot.check import is_binary
|
||||
|
@ -11,9 +10,6 @@ from binaryornot.check import is_binary
|
|||
PATTERN = r"{{(\s?cookiecutter)[.](.*?)}}"
|
||||
RE_OBJ = re.compile(PATTERN)
|
||||
|
||||
YN_CHOICES = ["y", "n"]
|
||||
CLOUD_CHOICES = ["AWS", "GCE", "None"]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def context():
|
||||
|
@ -29,36 +25,73 @@ def context():
|
|||
}
|
||||
|
||||
|
||||
@pytest_fixture_plus
|
||||
@pytest.mark.parametrize("windows", YN_CHOICES, ids=lambda yn: f"win:{yn}")
|
||||
@pytest.mark.parametrize("use_docker", YN_CHOICES, ids=lambda yn: f"docker:{yn}")
|
||||
@pytest.mark.parametrize("use_celery", YN_CHOICES, ids=lambda yn: f"celery:{yn}")
|
||||
@pytest.mark.parametrize("use_mailhog", YN_CHOICES, ids=lambda yn: f"mailhog:{yn}")
|
||||
@pytest.mark.parametrize("use_sentry", YN_CHOICES, ids=lambda yn: f"sentry:{yn}")
|
||||
@pytest.mark.parametrize("use_compressor", YN_CHOICES, ids=lambda yn: f"cmpr:{yn}")
|
||||
@pytest.mark.parametrize("use_whitenoise", YN_CHOICES, ids=lambda yn: f"wnoise:{yn}")
|
||||
@pytest.mark.parametrize("cloud_provider", CLOUD_CHOICES, ids=lambda yn: f"cloud:{yn}")
|
||||
def context_combination(
|
||||
windows,
|
||||
use_docker,
|
||||
use_celery,
|
||||
use_mailhog,
|
||||
use_sentry,
|
||||
use_compressor,
|
||||
use_whitenoise,
|
||||
cloud_provider,
|
||||
):
|
||||
"""Fixture that parametrize the function where it's used."""
|
||||
return {
|
||||
"windows": windows,
|
||||
"use_docker": use_docker,
|
||||
"use_compressor": use_compressor,
|
||||
"use_celery": use_celery,
|
||||
"use_mailhog": use_mailhog,
|
||||
"use_sentry": use_sentry,
|
||||
"use_whitenoise": use_whitenoise,
|
||||
"cloud_provider": cloud_provider,
|
||||
}
|
||||
SUPPORTED_COMBINATIONS = [
|
||||
{"open_source_license": "MIT"},
|
||||
{"open_source_license": "BSD"},
|
||||
{"open_source_license": "GPLv3"},
|
||||
{"open_source_license": "Apache Software License 2.0"},
|
||||
{"open_source_license": "Not open source"},
|
||||
{"windows": "y"},
|
||||
{"windows": "n"},
|
||||
{"use_pycharm": "y"},
|
||||
{"use_pycharm": "n"},
|
||||
{"use_docker": "y"},
|
||||
{"use_docker": "n"},
|
||||
{"postgresql_version": "11.3"},
|
||||
{"postgresql_version": "10.8"},
|
||||
{"postgresql_version": "9.6"},
|
||||
{"postgresql_version": "9.5"},
|
||||
{"postgresql_version": "9.4"},
|
||||
{"cloud_provider": "AWS", "use_whitenoise": "y"},
|
||||
{"cloud_provider": "AWS", "use_whitenoise": "n"},
|
||||
{"cloud_provider": "GCP", "use_whitenoise": "y"},
|
||||
{"cloud_provider": "GCP", "use_whitenoise": "n"},
|
||||
{"cloud_provider": "None", "use_whitenoise": "y"},
|
||||
# Note: cloud_provider=None AND use_whitenoise=n is not supported
|
||||
{"mail_service": "Mailgun"},
|
||||
{"mail_service": "Amazon SES"},
|
||||
{"mail_service": "Mailjet"},
|
||||
{"mail_service": "Mandrill"},
|
||||
{"mail_service": "Postmark"},
|
||||
{"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):
|
||||
|
@ -71,9 +104,7 @@ def build_files_list(root_dir):
|
|||
|
||||
|
||||
def check_paths(paths):
|
||||
"""Method to check all paths have correct substitutions,
|
||||
used by other tests cases
|
||||
"""
|
||||
"""Method to check all paths have correct substitutions."""
|
||||
# Assert that no match is found in any of the files
|
||||
for path in paths:
|
||||
if is_binary(path):
|
||||
|
@ -85,13 +116,10 @@ def check_paths(paths):
|
|||
assert match is None, msg.format(path)
|
||||
|
||||
|
||||
def test_project_generation(cookies, context, context_combination):
|
||||
"""
|
||||
Test that project is generated and fully rendered.
|
||||
|
||||
This is parametrized for each combination from ``context_combination`` fixture
|
||||
"""
|
||||
result = cookies.bake(extra_context={**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."""
|
||||
result = cookies.bake(extra_context={**context, **context_override})
|
||||
assert result.exit_code == 0
|
||||
assert result.exception is None
|
||||
assert result.project.basename == context["project_slug"]
|
||||
|
@ -102,38 +130,30 @@ def test_project_generation(cookies, context, context_combination):
|
|||
check_paths(paths)
|
||||
|
||||
|
||||
@pytest.mark.flake8
|
||||
def test_flake8_passes(cookies, context_combination):
|
||||
"""
|
||||
Generated project should pass flake8.
|
||||
|
||||
This is parametrized for each combination from ``context_combination`` fixture
|
||||
"""
|
||||
result = cookies.bake(extra_context=context_combination)
|
||||
@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id)
|
||||
def test_flake8_passes(cookies, context_override):
|
||||
"""Generated project should pass flake8."""
|
||||
result = cookies.bake(extra_context=context_override)
|
||||
|
||||
try:
|
||||
sh.flake8(str(result.project))
|
||||
except sh.ErrorReturnCode as e:
|
||||
pytest.fail(e)
|
||||
pytest.fail(e.stdout.decode())
|
||||
|
||||
|
||||
@pytest.mark.black
|
||||
def test_black_passes(cookies, context_combination):
|
||||
"""
|
||||
Generated project should pass black.
|
||||
|
||||
This is parametrized for each combination from ``context_combination`` fixture
|
||||
"""
|
||||
result = cookies.bake(extra_context=context_combination)
|
||||
@pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id)
|
||||
def test_black_passes(cookies, context_override):
|
||||
"""Generated project should pass black."""
|
||||
result = cookies.bake(extra_context=context_override)
|
||||
|
||||
try:
|
||||
sh.black("--check", "--diff", "--exclude", "migrations", f"{result.project}/")
|
||||
except sh.ErrorReturnCode as e:
|
||||
pytest.fail(e)
|
||||
pytest.fail(e.stdout.decode())
|
||||
|
||||
|
||||
def test_travis_invokes_pytest(cookies, context):
|
||||
context.update({"use_travisci": "y"})
|
||||
context.update({"ci_tool": "Travis"})
|
||||
result = cookies.bake(extra_context=context)
|
||||
|
||||
assert result.exit_code == 0
|
||||
|
@ -148,6 +168,24 @@ def test_travis_invokes_pytest(cookies, context):
|
|||
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"])
|
||||
def test_invalid_slug(cookies, context, slug):
|
||||
"""Invalid slug should failed pre-generation hook."""
|
||||
|
@ -157,3 +195,13 @@ def test_invalid_slug(cookies, context, slug):
|
|||
|
||||
assert result.exit_code != 0
|
||||
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]
|
||||
skipsdist = true
|
||||
envlist = py36,flake8,black,black-template
|
||||
envlist = py37,black-template
|
||||
|
||||
[testenv]
|
||||
deps = -rrequirements.txt
|
||||
commands = pytest -m "not flake8" -m "not black" {posargs:./tests}
|
||||
|
||||
[testenv:flake8]
|
||||
deps = -rrequirements.txt
|
||||
commands = pytest -m flake8 {posargs:./tests}
|
||||
|
||||
[testenv:black]
|
||||
deps = -rrequirements.txt
|
||||
commands = pytest -m black {posargs:./tests}
|
||||
commands = pytest {posargs:./tests}
|
||||
|
||||
[testenv:black-template]
|
||||
deps = black
|
||||
|
|
|
@ -13,10 +13,16 @@ indent_style = space
|
|||
indent_size = 4
|
||||
|
||||
[*.py]
|
||||
line_length=120
|
||||
known_first_party={{ cookiecutter.project_slug }}
|
||||
multi_line_output=3
|
||||
default_section=THIRDPARTY
|
||||
line_length = 88
|
||||
known_first_party = {{cookiecutter.project_slug}},config
|
||||
multi_line_output = 3
|
||||
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}]
|
||||
indent_style = space
|
||||
|
|
|
@ -13,9 +13,26 @@ DJANGO_SECURE_SSL_REDIRECT=False
|
|||
|
||||
# Email
|
||||
# ------------------------------------------------------------------------------
|
||||
MAILGUN_API_KEY=
|
||||
DJANGO_SERVER_EMAIL=
|
||||
{% if cookiecutter.mail_service == 'Mailgun' %}
|
||||
MAILGUN_API_KEY=
|
||||
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' %}
|
||||
# AWS
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -31,11 +48,7 @@ DJANGO_GCP_STORAGE_BUCKET_NAME=
|
|||
# django-allauth
|
||||
# ------------------------------------------------------------------------------
|
||||
DJANGO_ACCOUNT_ALLOW_REGISTRATION=True
|
||||
{% if cookiecutter.use_compressor == 'y' %}
|
||||
# django-compressor
|
||||
# ------------------------------------------------------------------------------
|
||||
COMPRESS_ENABLED=
|
||||
{% endif %}
|
||||
|
||||
# Gunicorn
|
||||
# ------------------------------------------------------------------------------
|
||||
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"?>
|
||||
<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"
|
||||
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">
|
||||
|
|
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
|
||||
language: python
|
||||
python:
|
||||
- "3.6"
|
||||
- "3.7"
|
||||
install:
|
||||
- pip install -r requirements/local.txt
|
||||
script:
|
||||
|
|
|
@ -2,4 +2,5 @@ release: python manage.py migrate
|
|||
web: gunicorn config.wsgi:application
|
||||
{% if cookiecutter.use_celery == "y" -%}
|
||||
worker: celery worker --app=config.celery_app --loglevel=info
|
||||
beat: celery beat --app=config.celery_app --loglevel=info
|
||||
{%- endif %}
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
FROM python:3.6-alpine
|
||||
FROM python:3.7-slim-buster
|
||||
|
||||
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
|
||||
&& apk add --virtual build-deps gcc python3-dev musl-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 \
|
||||
&& apt-get install -y libpq-dev \
|
||||
# Translations dependencies
|
||||
&& apk add gettext \
|
||||
# https://docs.djangoproject.com/en/dev/ref/django-admin/#dbshell
|
||||
&& apk add postgresql-client
|
||||
&& apt-get install -y gettext \
|
||||
# cleaning up unused files
|
||||
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Requirements are installed here to ensure they will be cached.
|
||||
COPY ./requirements /requirements
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
|
|
|
@ -9,21 +9,23 @@ RUN npm run build
|
|||
|
||||
# Python build stage
|
||||
{%- endif %}
|
||||
FROM python:3.6-alpine
|
||||
FROM python:3.7-slim-buster
|
||||
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
|
||||
RUN apk update \
|
||||
RUN apt-get update \
|
||||
# dependencies for building Python packages
|
||||
&& apt-get install -y build-essential \
|
||||
# psycopg2 dependencies
|
||||
&& apk add --virtual build-deps gcc python3-dev musl-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
|
||||
&& apt-get install -y libpq-dev \
|
||||
# Translations dependencies
|
||||
&& apt-get install -y gettext \
|
||||
# cleaning up unused files
|
||||
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN addgroup -S django \
|
||||
&& adduser -S -G django django
|
||||
RUN addgroup --system django \
|
||||
&& adduser --system --ingroup django django
|
||||
|
||||
# Requirements are installed here to ensure they will be cached.
|
||||
COPY ./requirements /requirements
|
||||
|
@ -57,13 +59,11 @@ RUN chmod +x /start-flower
|
|||
{%- endif %}
|
||||
|
||||
{%- if cookiecutter.js_task_runner == 'Gulp' %}
|
||||
COPY --from=client-builder /app /app
|
||||
COPY --from=client-builder --chown=django:django /app /app
|
||||
{% else %}
|
||||
COPY . /app
|
||||
COPY --chown=django:django . /app
|
||||
{%- endif %}
|
||||
|
||||
RUN chown -R django /app
|
||||
|
||||
USER django
|
||||
|
||||
WORKDIR /app
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
|
@ -6,4 +6,25 @@ set -o nounset
|
|||
|
||||
|
||||
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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
FROM traefik:alpine
|
||||
FROM traefik:v2.0
|
||||
RUN mkdir -p /etc/traefik/acme
|
||||
RUN touch /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
|
||||
|
||||
from celery import Celery
|
||||
|
||||
# set the default Django settings module for the 'celery' program.
|
||||
|
|
|
@ -68,16 +68,20 @@ DJANGO_APPS = [
|
|||
"django.contrib.staticfiles",
|
||||
# "django.contrib.humanize", # Handy template tags
|
||||
"django.contrib.admin",
|
||||
"django.forms",
|
||||
]
|
||||
THIRD_PARTY_APPS = [
|
||||
"crispy_forms",
|
||||
"allauth",
|
||||
"allauth.account",
|
||||
"allauth.socialaccount",
|
||||
"rest_framework",
|
||||
{%- if cookiecutter.use_celery == 'y' %}
|
||||
"django_celery_beat",
|
||||
{%- endif %}
|
||||
{%- if cookiecutter.use_drf == "y" %}
|
||||
"rest_framework",
|
||||
"rest_framework.authtoken",
|
||||
{%- endif %}
|
||||
]
|
||||
|
||||
LOCAL_APPS = [
|
||||
|
@ -131,12 +135,16 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||
# https://docs.djangoproject.com/en/dev/ref/settings/#middleware
|
||||
MIDDLEWARE = [
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
{%- if cookiecutter.use_whitenoise == 'y' %}
|
||||
"whitenoise.middleware.WhiteNoiseMiddleware",
|
||||
{%- endif %}
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.locale.LocaleMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.common.BrokenLinkEmailsMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
]
|
||||
|
||||
|
@ -187,10 +195,15 @@ TEMPLATES = [
|
|||
"django.template.context_processors.static",
|
||||
"django.template.context_processors.tz",
|
||||
"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
|
||||
CRISPY_TEMPLATE_PACK = "bootstrap4"
|
||||
|
||||
|
@ -291,14 +304,24 @@ ACCOUNT_EMAIL_VERIFICATION = "mandatory"
|
|||
ACCOUNT_ADAPTER = "{{cookiecutter.project_slug}}.users.adapters.AccountAdapter"
|
||||
# https://django-allauth.readthedocs.io/en/latest/configuration.html
|
||||
SOCIALACCOUNT_ADAPTER = "{{cookiecutter.project_slug}}.users.adapters.SocialAccountAdapter"
|
||||
|
||||
{% if cookiecutter.use_compressor == 'y' -%}
|
||||
# django-compressor
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://django-compressor.readthedocs.io/en/latest/quickstart/#installation
|
||||
INSTALLED_APPS += ["compressor"]
|
||||
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 %}
|
||||
# Your stuff...
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -28,19 +28,27 @@ CACHES = {
|
|||
{% if cookiecutter.use_mailhog == 'y' and cookiecutter.use_docker == 'y' -%}
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#email-host
|
||||
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' -%}
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#email-host
|
||||
EMAIL_HOST = "localhost"
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#email-port
|
||||
EMAIL_PORT = 1025
|
||||
{%- else -%}
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
|
||||
EMAIL_BACKEND = env(
|
||||
"DJANGO_EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend"
|
||||
)
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#email-host
|
||||
EMAIL_HOST = "localhost"
|
||||
{%- 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
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""isort:skip_file"""
|
||||
{% if cookiecutter.use_sentry == 'y' -%}
|
||||
import logging
|
||||
|
||||
|
@ -92,7 +93,6 @@ AWS_DEFAULT_ACL = None
|
|||
# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings
|
||||
AWS_S3_REGION_NAME = env("DJANGO_AWS_S3_REGION_NAME", default=None)
|
||||
{% elif cookiecutter.cloud_provider == 'GCP' %}
|
||||
DEFAULT_FILE_STORAGE = "storages.backends.gcloud.GoogleCloudStorage"
|
||||
GS_BUCKET_NAME = env("DJANGO_GCP_STORAGE_BUCKET_NAME")
|
||||
GS_DEFAULT_ACL = "publicRead"
|
||||
{% endif -%}
|
||||
|
@ -105,8 +105,11 @@ GS_DEFAULT_ACL = "publicRead"
|
|||
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
||||
{% elif cookiecutter.cloud_provider == 'AWS' -%}
|
||||
STATICFILES_STORAGE = "config.settings.production.StaticRootS3Boto3Storage"
|
||||
COLLECTFAST_STRATEGY = "collectfast.strategies.boto3.Boto3Strategy"
|
||||
STATIC_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/static/"
|
||||
{% 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/"
|
||||
{% endif -%}
|
||||
|
||||
|
@ -132,14 +135,27 @@ class MediaRootS3Boto3Storage(S3Boto3Storage):
|
|||
DEFAULT_FILE_STORAGE = "config.settings.production.MediaRootS3Boto3Storage"
|
||||
MEDIA_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/media/"
|
||||
{%- elif cookiecutter.cloud_provider == '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_ROOT = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/media/"
|
||||
{%- endif %}
|
||||
|
||||
# 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",
|
||||
[
|
||||
|
@ -167,41 +183,114 @@ EMAIL_SUBJECT_PREFIX = env(
|
|||
# Django Admin URL regex.
|
||||
ADMIN_URL = env("DJANGO_ADMIN_URL")
|
||||
|
||||
# Anymail (Mailgun)
|
||||
# Anymail
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://anymail.readthedocs.io/en/stable/installation/#installing-anymail
|
||||
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
|
||||
{%- if cookiecutter.mail_service == 'Mailgun' %}
|
||||
# https://anymail.readthedocs.io/en/stable/esps/mailgun/
|
||||
EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend"
|
||||
ANYMAIL = {
|
||||
"MAILGUN_API_KEY": env("MAILGUN_API_KEY"),
|
||||
"MAILGUN_SENDER_DOMAIN": env("MAILGUN_DOMAIN"),
|
||||
"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' -%}
|
||||
# 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' -%}
|
||||
{% if cookiecutter.use_compressor == 'y' -%}
|
||||
# django-compressor
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_ENABLED
|
||||
COMPRESS_ENABLED = env.bool("COMPRESS_ENABLED", default=True)
|
||||
# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_STORAGE
|
||||
{%- if cookiecutter.cloud_provider == 'AWS' %}
|
||||
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
|
||||
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 %}
|
||||
{%- if cookiecutter.use_whitenoise == 'n' -%}
|
||||
# Collectfast
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://github.com/antonagestam/collectfast#installation
|
||||
INSTALLED_APPS = ["collectfast"] + INSTALLED_APPS # noqa F405
|
||||
AWS_PRELOAD_METADATA = True
|
||||
{% endif %}
|
||||
# LOGGING
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -7,8 +7,6 @@ from .base import env
|
|||
|
||||
# GENERAL
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#debug
|
||||
DEBUG = False
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
|
||||
SECRET_KEY = env(
|
||||
"DJANGO_SECRET_KEY",
|
||||
|
@ -34,7 +32,7 @@ PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]
|
|||
|
||||
# TEMPLATES
|
||||
# ------------------------------------------------------------------------------
|
||||
TEMPLATES[0]["OPTIONS"]["loaders"] = [ # noqa F405
|
||||
TEMPLATES[-1]["OPTIONS"]["loaders"] = [ # type: ignore[index] # noqa F405
|
||||
(
|
||||
"django.template.loaders.cached.Loader",
|
||||
[
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
from django.conf import settings
|
||||
from django.urls import include, path
|
||||
from django.conf.urls.static import static
|
||||
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.generic import TemplateView
|
||||
{%- if cookiecutter.use_drf == 'y' %}
|
||||
from rest_framework.authtoken.views import obtain_auth_token
|
||||
{%- endif %}
|
||||
|
||||
urlpatterns = [
|
||||
path("", TemplateView.as_view(template_name="pages/home.html"), name="home"),
|
||||
|
@ -17,6 +20,15 @@ urlpatterns = [
|
|||
path("accounts/", include("allauth.urls")),
|
||||
# Your stuff: custom urls includes go here
|
||||
] + 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:
|
||||
# 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.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Internal variables.
|
||||
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
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@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 " 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)"
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
.PHONY: help Makefile
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
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."
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
|
|
@ -1,255 +1,55 @@
|
|||
# {{ cookiecutter.project_name }} documentation build configuration file, created by
|
||||
# sphinx-quickstart.
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
import os
|
||||
import sys
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# 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
|
||||
# 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
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
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 = []
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
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
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ["_build"]
|
||||
|
||||
# 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 = []
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = "default"
|
||||
|
||||
# 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
|
||||
#
|
||||
html_theme = "alabaster"
|
||||
|
||||
# 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,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
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
|
||||
contain the root `toctree` directive.
|
||||
|
||||
{{ cookiecutter.project_name }} Project Documentation
|
||||
====================================================================
|
||||
|
||||
Table of Contents:
|
||||
Welcome to {{ cookiecutter.project_name }}'s documentation!
|
||||
======================================================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
pycharm/configuration
|
||||
|
||||
|
||||
Indices & Tables
|
||||
================
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
|
|
|
@ -1,190 +1,35 @@
|
|||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
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" == "help" (
|
||||
:help
|
||||
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
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||
goto end
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
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% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||
goto end
|
||||
)
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
if "%1" == "singlehtml" (
|
||||
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
||||
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
|
||||
)
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
: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**:
|
||||
|
||||
* run and debug python code
|
||||
|
||||
.. image:: images/f1.png
|
||||
|
||||
* run and debug tests
|
||||
|
||||
.. image:: images/f2.png
|
||||
.. image:: images/f3.png
|
||||
|
||||
* run and debug migrations or different django management commands
|
||||
|
||||
.. image:: images/f4.png
|
||||
|
||||
* and many others..
|
||||
|
||||
Known issues
|
||||
|
|
|
@ -42,6 +42,9 @@ services:
|
|||
ports:
|
||||
- "0.0.0.0:80:80"
|
||||
- "0.0.0.0:443:443"
|
||||
{%- if cookiecutter.use_celery == 'y' %}
|
||||
- "0.0.0.0:5555:5555"
|
||||
{%- endif %}
|
||||
|
||||
redis:
|
||||
image: redis:5.0
|
||||
|
@ -60,11 +63,11 @@ services:
|
|||
flower:
|
||||
<<: *django
|
||||
image: {{ cookiecutter.project_slug }}_production_flower
|
||||
ports:
|
||||
- "5555:5555"
|
||||
command: /start-flower
|
||||
|
||||
{%- endif %}
|
||||
|
||||
{% if cookiecutter.cloud_provider == 'AWS' %}
|
||||
awscli:
|
||||
build:
|
||||
context: .
|
||||
|
@ -73,3 +76,4 @@ services:
|
|||
- ./.envs/.production/.django
|
||||
volumes:
|
||||
- production_postgres_data_backups:/backups
|
||||
{%- endif %}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
[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' %}
|
||||
norecursedirs = node_modules
|
||||
{%- endif %}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
pytz==2019.1 # https://github.com/stub42/pytz
|
||||
python-slugify==3.0.2 # https://github.com/un33k/python-slugify
|
||||
Pillow==6.1.0 # https://github.com/python-pillow/Pillow
|
||||
pytz==2019.3 # https://github.com/stub42/pytz
|
||||
python-slugify==4.0.0 # https://github.com/un33k/python-slugify
|
||||
Pillow==7.0.0 # https://github.com/python-pillow/Pillow
|
||||
{%- 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
|
||||
{%- 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' %}
|
||||
whitenoise==4.1.2 # https://github.com/evansd/whitenoise
|
||||
whitenoise==5.0.1 # https://github.com/evansd/whitenoise
|
||||
{%- 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" %}
|
||||
celery==4.3.0 # pyup: < 5.0 # https://github.com/celery/celery
|
||||
django-celery-beat==1.5.0 # https://github.com/celery/django-celery-beat
|
||||
celery==4.4.2 # pyup: < 5.0 # https://github.com/celery/celery
|
||||
django-celery-beat==2.0.0 # https://github.com/celery/django-celery-beat
|
||||
{%- if cookiecutter.use_docker == 'y' %}
|
||||
flower==0.9.3 # https://github.com/mher/flower
|
||||
{%- endif %}
|
||||
|
@ -19,16 +19,16 @@ flower==0.9.3 # https://github.com/mher/flower
|
|||
|
||||
# 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-model-utils==3.2.0 # https://github.com/jazzband/django-model-utils
|
||||
django-allauth==0.39.1 # https://github.com/pennersr/django-allauth
|
||||
django-crispy-forms==1.7.2 # https://github.com/django-crispy-forms/django-crispy-forms
|
||||
django-model-utils==4.0.0 # https://github.com/jazzband/django-model-utils
|
||||
django-allauth==0.41.0 # https://github.com/pennersr/django-allauth
|
||||
django-crispy-forms==1.9.0 # https://github.com/django-crispy-forms/django-crispy-forms
|
||||
{%- 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 %}
|
||||
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
|
||||
djangorestframework==3.9.4 # https://github.com/encode/django-rest-framework
|
||||
coreapi==2.3.3 # https://github.com/core-api/python-client
|
||||
djangorestframework==3.11.0 # https://github.com/encode/django-rest-framework
|
||||
{%- endif %}
|
||||
|
|
|
@ -1,35 +1,38 @@
|
|||
-r ./base.txt
|
||||
|
||||
Werkzeug==0.14.1 # pyup: < 0.15 # https://github.com/pallets/werkzeug
|
||||
ipdb==0.12 # https://github.com/gotcha/ipdb
|
||||
Sphinx==2.1.2 # https://github.com/sphinx-doc/sphinx
|
||||
Werkzeug==1.0.0 # https://github.com/pallets/werkzeug
|
||||
ipdb==0.13.2 # https://github.com/gotcha/ipdb
|
||||
Sphinx==2.4.4 # https://github.com/sphinx-doc/sphinx
|
||||
{%- 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 %}
|
||||
psycopg2-binary==2.8.3 # https://github.com/psycopg/psycopg2
|
||||
psycopg2-binary==2.8.4 # https://github.com/psycopg/psycopg2
|
||||
{%- endif %}
|
||||
|
||||
# Testing
|
||||
# ------------------------------------------------------------------------------
|
||||
mypy==0.711 # https://github.com/python/mypy
|
||||
pytest==5.0.1 # https://github.com/pytest-dev/pytest
|
||||
mypy==0.770 # https://github.com/python/mypy
|
||||
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
|
||||
|
||||
# Code quality
|
||||
# ------------------------------------------------------------------------------
|
||||
flake8==3.7.8 # https://github.com/PyCQA/flake8
|
||||
coverage==4.5.3 # https://github.com/nedbat/coveragepy
|
||||
black==19.3b0 # https://github.com/ambv/black
|
||||
pylint-django==2.0.10 # https://github.com/PyCQA/pylint-django
|
||||
flake8==3.7.9 # https://github.com/PyCQA/flake8
|
||||
flake8-isort==2.9.0 # https://github.com/gforcada/flake8-isort
|
||||
coverage==5.0.4 # https://github.com/nedbat/coveragepy
|
||||
black==19.10b0 # https://github.com/ambv/black
|
||||
pylint-django==2.0.14 # https://github.com/PyCQA/pylint-django
|
||||
{%- if cookiecutter.use_celery == 'y' %}
|
||||
pylint-celery==0.3 # https://github.com/PyCQA/pylint-celery
|
||||
{%- endif %}
|
||||
pre-commit==2.2.0 # https://github.com/pre-commit/pre-commit
|
||||
|
||||
# Django
|
||||
# ------------------------------------------------------------------------------
|
||||
factory-boy==2.12.0 # https://github.com/FactoryBoy/factory_boy
|
||||
|
||||
django-debug-toolbar==2.0 # https://github.com/jazzband/django-debug-toolbar
|
||||
django-extensions==2.1.9 # https://github.com/django-extensions/django-extensions
|
||||
django-coverage-plugin==1.6.0 # https://github.com/nedbat/django_coverage_plugin
|
||||
pytest-django==3.5.1 # https://github.com/pytest-dev/pytest-django
|
||||
django-debug-toolbar==2.2 # https://github.com/jazzband/django-debug-toolbar
|
||||
django-extensions==2.2.8 # https://github.com/django-extensions/django-extensions
|
||||
django-coverage-plugin==1.8.0 # https://github.com/nedbat/django_coverage_plugin
|
||||
pytest-django==3.8.0 # https://github.com/pytest-dev/pytest-django
|
||||
|
|
|
@ -2,20 +2,38 @@
|
|||
|
||||
-r ./base.txt
|
||||
|
||||
gunicorn==19.9.0 # https://github.com/benoitc/gunicorn
|
||||
psycopg2==2.8.3 --no-binary psycopg2 # https://github.com/psycopg/psycopg2
|
||||
gunicorn==20.0.4 # https://github.com/benoitc/gunicorn
|
||||
psycopg2==2.8.4 --no-binary psycopg2 # https://github.com/psycopg/psycopg2
|
||||
{%- if cookiecutter.use_whitenoise == 'n' %}
|
||||
Collectfast==0.6.2 # https://github.com/antonagestam/collectfast
|
||||
Collectfast==2.1.0 # https://github.com/antonagestam/collectfast
|
||||
{%- endif %}
|
||||
{%- 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 %}
|
||||
|
||||
# Django
|
||||
# ------------------------------------------------------------------------------
|
||||
{%- 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' %}
|
||||
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 %}
|
||||
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
|
||||
|
||||
[mypy]
|
||||
python_version = 3.6
|
||||
python_version = 3.7
|
||||
check_untyped_defs = True
|
||||
ignore_errors = False
|
||||
ignore_missing_imports = True
|
||||
strict_optional = True
|
||||
warn_unused_ignores = True
|
||||
warn_redundant_casts = True
|
||||
warn_unused_configs = True
|
||||
plugins = mypy_django_plugin.main
|
||||
|
||||
[mypy.plugins.django-stubs]
|
||||
django_settings_module = config.settings.test
|
||||
|
||||
[mypy-*.migrations.*]
|
||||
# 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
|
||||
from django.conf import settings
|
||||
from django.test import RequestFactory
|
||||
|
||||
from {{ cookiecutter.project_slug }}.users.models import User
|
||||
from {{ cookiecutter.project_slug }}.users.tests.factories import UserFactory
|
||||
|
||||
|
||||
|
@ -11,7 +11,7 @@ def media_storage(settings, tmpdir):
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def user() -> settings.AUTH_USER_MODEL:
|
||||
def user() -> User:
|
||||
return UserFactory()
|
||||
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<form method="POST" action=".">
|
||||
{% csrf_token %}
|
||||
{{ 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>
|
||||
{% else %}
|
||||
<p>{% trans 'Your password is now changed.' %}</p>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<form method="POST" action="{% url 'account_set_password' %}" class="password_set">
|
||||
{% csrf_token %}
|
||||
{{ 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>
|
||||
{% endblock %}
|
||||
{% endraw %}
|
||||
|
|
|
@ -80,7 +80,7 @@
|
|||
|
||||
{% if 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 %}
|
||||
{% endif %}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
{{ form|crispy }}
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<button type="submit" class="btn">Update</button>
|
||||
<button type="submit" class="btn btn-primary">Update</button>
|
||||
</div>
|
||||
</div>
|
||||
</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.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
|
|
@ -12,14 +12,18 @@ class UserFactory(DjangoModelFactory):
|
|||
|
||||
@post_generation
|
||||
def password(self, create: bool, extracted: Sequence[Any], **kwargs):
|
||||
password = Faker(
|
||||
"password",
|
||||
length=42,
|
||||
special_chars=True,
|
||||
digits=True,
|
||||
upper_case=True,
|
||||
lower_case=True,
|
||||
).generate(extra_kwargs={})
|
||||
password = (
|
||||
extracted
|
||||
if extracted
|
||||
else Faker(
|
||||
"password",
|
||||
length=42,
|
||||
special_chars=True,
|
||||
digits=True,
|
||||
upper_case=True,
|
||||
lower_case=True,
|
||||
).generate(extra_kwargs={})
|
||||
)
|
||||
self.set_password(password)
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import pytest
|
||||
from django.conf import settings
|
||||
|
||||
from {{ cookiecutter.project_slug }}.users.models import User
|
||||
|
||||
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}/"
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import pytest
|
||||
from celery.result import EagerResult
|
||||
|
||||
|
||||
from {{ cookiecutter.project_slug }}.users.tasks import get_users_count
|
||||
from {{ cookiecutter.project_slug }}.users.tests.factories import UserFactory
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import pytest
|
||||
from django.conf import settings
|
||||
from django.urls import reverse, resolve
|
||||
from django.urls import resolve, reverse
|
||||
|
||||
from {{ cookiecutter.project_slug }}.users.models import User
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_detail(user: settings.AUTH_USER_MODEL):
|
||||
def test_detail(user: User):
|
||||
assert (
|
||||
reverse("users:detail", kwargs={"username": user.username})
|
||||
== f"/users/{user.username}/"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import pytest
|
||||
from django.conf import settings
|
||||
from django.test import RequestFactory
|
||||
|
||||
from {{ cookiecutter.project_slug }}.users.models import User
|
||||
from {{ cookiecutter.project_slug }}.users.views import UserRedirectView, UserUpdateView
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
@ -16,9 +16,7 @@ class TestUserUpdateView:
|
|||
https://github.com/pytest-dev/pytest-django/pull/258
|
||||
"""
|
||||
|
||||
def test_get_success_url(
|
||||
self, user: settings.AUTH_USER_MODEL, request_factory: RequestFactory
|
||||
):
|
||||
def test_get_success_url(self, user: User, request_factory: RequestFactory):
|
||||
view = UserUpdateView()
|
||||
request = request_factory.get("/fake-url/")
|
||||
request.user = user
|
||||
|
@ -27,9 +25,7 @@ class TestUserUpdateView:
|
|||
|
||||
assert view.get_success_url() == f"/users/{user.username}/"
|
||||
|
||||
def test_get_object(
|
||||
self, user: settings.AUTH_USER_MODEL, request_factory: RequestFactory
|
||||
):
|
||||
def test_get_object(self, user: User, request_factory: RequestFactory):
|
||||
view = UserUpdateView()
|
||||
request = request_factory.get("/fake-url/")
|
||||
request.user = user
|
||||
|
@ -40,9 +36,7 @@ class TestUserUpdateView:
|
|||
|
||||
|
||||
class TestUserRedirectView:
|
||||
def test_get_redirect_url(
|
||||
self, user: settings.AUTH_USER_MODEL, request_factory: RequestFactory
|
||||
):
|
||||
def test_get_redirect_url(self, user: User, request_factory: RequestFactory):
|
||||
view = UserRedirectView()
|
||||
request = request_factory.get("/fake-url")
|
||||
request.user = user
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
from django.urls import path
|
||||
|
||||
from {{ cookiecutter.project_slug }}.users.views import (
|
||||
user_detail_view,
|
||||
user_redirect_view,
|
||||
user_update_view,
|
||||
user_detail_view,
|
||||
)
|
||||
|
||||
app_name = "users"
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from django.contrib import messages
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic import DetailView, RedirectView, UpdateView
|
||||
|
||||
User = get_user_model()
|
||||
|
@ -27,6 +29,12 @@ class UserUpdateView(LoginRequiredMixin, UpdateView):
|
|||
def get_object(self):
|
||||
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()
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
from django.conf import settings
|
||||
|
||||
|
||||
def settings_context(_request):
|
||||
return {"settings": settings}
|
Loading…
Reference in New Issue
Block a user