Merge with master

This commit is contained in:
andresgz 2016-06-16 18:34:52 -04:00
commit 7a147848bc
47 changed files with 955 additions and 339 deletions

View File

@ -2,13 +2,32 @@
All enhancements and patches to Cookiecutter Django will be documented in this file. All enhancements and patches to Cookiecutter Django will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). This project adheres to [Semantic Versioning](http://semver.org/).
##[2016-06-11]
### Changed
- Enhancements to the developing locally docs (@antoniablair)
##[2016-06-06]
### Changed
- Pin Bootstrap CSS and JS to v4.0.0-alpha.2, use minified versions
##[2016-06-05]
### Added
- Configurable admin for users (@pydanny, @jayfk, @dezoito)
##[2016-06-04]
### Added
- Let's Encrypt automation and instruction (@mjsisley and @chrisdev)
##[2016-06-03] ##[2016-06-03]
### Added ### Added
- Documentation for debugging with Docker (@mjsisley) - Documentation for debugging with Docker (@mjsisley)
- Apache 2 License option in `cookiecutter.json` (@dot2dotseurat) - Apache 2 License option in `cookiecutter.json` (@dot2dotseurat)
- Removed unnecessary version check from `pre_gen_project.py` (@suledev)
- Add gulp alternative as a js task runner and fix navbar style issue (@viviangb and @xpostudio4)
### Deleted ### Deleted
- AngularJS (@pydanny) - AngularJS (@pydanny)
- django-secure (@xpostudio4)
##[2016-06-02] ##[2016-06-02]
### Added ### Added

View File

@ -48,6 +48,7 @@ Listed in alphabetical order.
Andrew Mikhnevich `@zcho`_ Andrew Mikhnevich `@zcho`_
Andy Rose Andy Rose
Anna Callahan `@jazztpt`_ Anna Callahan `@jazztpt`_
Antonia Blair `@antoniablair`_ @antoniablairart
Areski Belaid `@areski`_ Areski Belaid `@areski`_
Ashley Camba Ashley Camba
Barclay Gauld `@yunti`_ Barclay Gauld `@yunti`_
@ -61,6 +62,7 @@ Listed in alphabetical order.
Chris Franklin Chris Franklin
Chris Franklin `@hairychris`_ Chris Franklin `@hairychris`_
Chris Pappalardo `@ChrisPappalardo`_ Chris Pappalardo `@ChrisPappalardo`_
Christopher Clarke `@chrisdev`_
Collederas `@Collederas`_ Collederas `@Collederas`_
Cristian Vargas `@cdvv7788`_ Cristian Vargas `@cdvv7788`_
Cullen Rhodes `@c-rhodes`_ Cullen Rhodes `@c-rhodes`_
@ -82,11 +84,13 @@ Listed in alphabetical order.
Jens Nilsson `@phiberjenz` Jens Nilsson `@phiberjenz`
Julio Castillo `@juliocc`_ Julio Castillo `@juliocc`_
Kaido Kert `@kaidokert`_ Kaido Kert `@kaidokert`_
kappataumu `@kappataumu`_ @kappataumu
Kaveh `@ka7eh`_ Kaveh `@ka7eh`_
Kevin A. Stone Kevin A. Stone
Kevin Ndung'u `@kevgathuku`_ Kevin Ndung'u `@kevgathuku`_
Krzysztof Szumny `@noisy`_ Krzysztof Szumny `@noisy`_
Krzysztof Żuraw `@krzysztofzuraw`_ Krzysztof Żuraw `@krzysztofzuraw`_
Leonardo Jimenez `@xpostudio4`_
Lin Xianyi `@iynaix`_ Lin Xianyi `@iynaix`_
Luis Nell `@originell`_ Luis Nell `@originell`_
Lukas Klein Lukas Klein
@ -96,8 +100,8 @@ Listed in alphabetical order.
Matt Linares Matt Linares
Matt Menzenski `@menzenski`_ Matt Menzenski `@menzenski`_
Matt Warren `@mfwarren`_ Matt Warren `@mfwarren`_
Matthew Sisley `@mjsisley`_
Meghan Heintz `@dot2dotseurat`_ Meghan Heintz `@dot2dotseurat`_
mjsisley `@mjsisley`_
mozillazg `@mozillazg`_ mozillazg `@mozillazg`_
Pablo `@oubiga`_ Pablo `@oubiga`_
Raphael Pierzina `@hackebrot`_ Raphael Pierzina `@hackebrot`_
@ -108,25 +112,28 @@ Listed in alphabetical order.
stepmr `@stepmr`_ stepmr `@stepmr`_
Sławek Ehlert `@slafs`_ Sławek Ehlert `@slafs`_
Srinivas Nyayapati `@shireenrao`_ Srinivas Nyayapati `@shireenrao`_
Sule Marshall `@suledev`_
Taylor Baldwin Taylor Baldwin
Théo Segonds `@show0k`_ Théo Segonds `@show0k`_
Tom Atkins `@knitatoms`_ Tom Atkins `@knitatoms`_
Tom Offermann Tom Offermann
Travis McNeill `@Travistock`_ @tavistock_esq Travis McNeill `@Travistock`_ @tavistock_esq
Vitaly Babiy Vitaly Babiy
Vivian Guillen `@viviangb`_
Yaroslav Halchenko Yaroslav Halchenko
========================== ============================ ============== ========================== ============================ ==============
.. _@dezoito: https://github.com/dezoito
.. _@a7p: https://github.com/a7p .. _@a7p: https://github.com/a7p
.. _@ad-m: https://github.com/ad-m .. _@ad-m: https://github.com/ad-m
.. _@aeikenberry: https://github.com/aeikenberry .. _@aeikenberry: https://github.com/aeikenberry
.. _@alb3rto: https://github.com/alb3rto .. _@alb3rto: https://github.com/alb3rto
.. _@amjith: https://github.com/amjith .. _@amjith: https://github.com/amjith
.. _@andor-pierdelacabeza: https://github.com/andor-pierdelacabeza .. _@andor-pierdelacabeza: https://github.com/andor-pierdelacabeza
.. _@antoniablair: https://github.com/antoniablair
.. _@areski: https://github.com/areski .. _@areski: https://github.com/areski
.. _@arruda: https://github.com/arruda .. _@arruda: https://github.com/arruda
.. _@bloodpet: https://github.com/bloodpet .. _@bloodpet: https://github.com/bloodpet
.. _@blopker: https://github.com/blopker
.. _@bogdal: https://github.com/bogdal .. _@bogdal: https://github.com/bogdal
.. _@burhan: https://github.com/burhan .. _@burhan: https://github.com/burhan
.. _@c-rhodes: https://github.com/c-rhodes .. _@c-rhodes: https://github.com/c-rhodes
@ -134,9 +141,11 @@ Listed in alphabetical order.
.. _@catherinedevlin: https://github.com/catherinedevlin .. _@catherinedevlin: https://github.com/catherinedevlin
.. _@ccurvey: https://github.com/ccurvey .. _@ccurvey: https://github.com/ccurvey
.. _@cdvv7788: https://github.com/cdvv7788 .. _@cdvv7788: https://github.com/cdvv7788
.. _@chrisdev: https://github.com/chrisdev
.. _@ChrisPappalardo: https://github.com/ChrisPappalardo .. _@ChrisPappalardo: https://github.com/ChrisPappalardo
.. _@Collederas: https://github.com/Collederas .. _@Collederas: https://github.com/Collederas
.. _@ddiazpinto: https://github.com/ddiazpinto .. _@ddiazpinto: https://github.com/ddiazpinto
.. _@dezoito: https://github.com/dezoito
.. _@dot2dotseurat: https://github.com/dot2dotseurat .. _@dot2dotseurat: https://github.com/dot2dotseurat
.. _@dsclementsen: https://github.com/dsclementsen .. _@dsclementsen: https://github.com/dsclementsen
.. _@epileptic-fish: https://gihub.com/epileptic-fish .. _@epileptic-fish: https://gihub.com/epileptic-fish
@ -156,27 +165,33 @@ Listed in alphabetical order.
.. _@jvanbrug: https://github.com/jvanbrug .. _@jvanbrug: https://github.com/jvanbrug
.. _@ka7eh: https://github.com/ka7eh .. _@ka7eh: https://github.com/ka7eh
.. _@kaidokert: https://github.com/kaidokert .. _@kaidokert: https://github.com/kaidokert
.. _@kappataumu: https://github.com/kappataumu
.. _@kevgathuku: https://github.com/kevgathuku .. _@kevgathuku: https://github.com/kevgathuku
.. _@knitatoms: https://github.com/knitatoms .. _@knitatoms: https://github.com/knitatoms
.. _@krzysztofzuraw: https://github.com/krzysztofzuraw
.. _@MathijsHoogland: https://github.com/MathijsHoogland .. _@MathijsHoogland: https://github.com/MathijsHoogland
.. _@menzenski: https://github.com/menzenski .. _@menzenski: https://github.com/menzenski
.. _@mfwarren: https://github.com/mfwarren .. _@mfwarren: https://github.com/mfwarren
.. _@mjsisley: https://github.com/mjsisley .. _@mjsisley: https://github.com/mjsisley
.. _@mozillazg: https://github.com/mozillazg .. _@mozillazg: https://github.com/mozillazg
.. _@noisy: https://github.com/noisy
.. _@originell: https://github.com/originell .. _@originell: https://github.com/originell
.. _@oubiga: https://github.com/oubiga .. _@oubiga: https://github.com/oubiga
.. _@romanosipenko: https://github.com/romanosipenko
.. _@raonyguimaraes: https://github.com/raonyguimaraes .. _@raonyguimaraes: https://github.com/raonyguimaraes
.. _@romanosipenko: https://github.com/romanosipenko
.. _@shireenrao: https://github.com/shireenrao
.. _@show0k: https://github.com/show0k .. _@show0k: https://github.com/show0k
.. _@siauPatrick: https://github.com/siauPatrick .. _@siauPatrick: https://github.com/siauPatrick
.. _@shireenrao: https://github.com/shireenrao
.. _@slafs: https://github.com/slafs .. _@slafs: https://github.com/slafs
.. _@stepmr: https://github.com/stepmr .. _@stepmr: https://github.com/stepmr
.. _@suledev: https://github.com/suledev
.. _@Travistock: https://github.com/Tavistock .. _@Travistock: https://github.com/Tavistock
.. _@trungdong: https://github.com/trungdong .. _@trungdong: https://github.com/trungdong
.. _@viviangb: httpsL//github.com/viviangb
.. _@xpostudio4: https://github.com/xpostudio4
.. _@yunti: https://github.com/yunti .. _@yunti: https://github.com/yunti
.. _@zcho: https://github.com/zcho .. _@zcho: https://github.com/zcho
.. _@noisy: https://github.com/noisy
Special Thanks Special Thanks
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~

43
LICENSE
View File

@ -1,4 +1,17 @@
Copyright (c) 2013-2016, Daniel Greenfeld {% if cookiecutter.open_source_license == 'MIT' %}
MIT License
Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.full_name }}
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
{% elif cookiecutter.open_source_license == 'BSD' %}
BSD License
Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.full_name }}
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, Redistribution and use in source and binary forms, with or without modification,
@ -11,9 +24,9 @@ are permitted provided that the following conditions are met:
list of conditions and the following disclaimer in the documentation and/or list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution. other materials provided with the distribution.
* Neither the name of Cookiecutter Django nor the names of its contributors may * Neither the name of {{ cookiecutter.project_name }} nor the names of its
be used to endorse or promote products derived from this software without contributors may be used to endorse or promote products derived from this
specific prior written permission. software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -21,7 +34,25 @@ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIME
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE. OF THE POSSIBILITY OF SUCH DAMAGE.
{% elif cookiecutter.open_source_license == 'Apache Software License 2.0' %}
Apache Software License 2.0
Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.full_name }}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
{% endif %}

View File

@ -1,9 +1,9 @@
Cookiecutter Django Cookiecutter Django
======================= =======================
.. image:: https://requires.io/github/pydanny/cookiecutter-django/requirements.svg?branch=master .. image:: https://pyup.io/repos/github/pydanny/cookiecutter-django/shield.svg
:target: https://requires.io/github/pydanny/cookiecutter-django/requirements/?branch=master :target: https://pyup.io/repos/github/pydanny/cookiecutter-django/
:alt: Requirements Status :alt: Updates
.. image:: https://travis-ci.org/pydanny/cookiecutter-django.svg?branch=master .. image:: https://travis-ci.org/pydanny/cookiecutter-django.svg?branch=master
:target: https://travis-ci.org/pydanny/cookiecutter-django?branch=master :target: https://travis-ci.org/pydanny/cookiecutter-django?branch=master
@ -14,7 +14,8 @@ Cookiecutter Django
Powered by Cookiecutter_, Cookiecutter Django is a framework for jumpstarting production-ready Django projects quickly. Powered by Cookiecutter_, Cookiecutter Django is a framework for jumpstarting production-ready Django projects quickly.
See Troubleshooting_ for common errors and obstacles. * Documentation: https://cookiecutter-django.readthedocs.io
* See Troubleshooting_ for common errors and obstacles
.. _cookiecutter: https://github.com/audreyr/cookiecutter .. _cookiecutter: https://github.com/audreyr/cookiecutter
@ -115,13 +116,10 @@ Answer the prompts with your own desired options_. For example::
domain_name [example.com]: myreddit.com domain_name [example.com]: myreddit.com
version [0.1.0]: 0.0.1 version [0.1.0]: 0.0.1
timezone [UTC]: America/Los_Angeles timezone [UTC]: America/Los_Angeles
now [2016/03/01]: 2016/03/05
year [2016]:
use_whitenoise [y]: n use_whitenoise [y]: n
use_celery [n]: y use_celery [n]: y
use_mailhog [n]: n use_mailhog [n]: n
use_sentry [n]: y use_sentry_for_error_reporting [y]: y
use_newrelic [n]: y
use_opbeat [n]: y use_opbeat [n]: y
use_pycharm [n]: y use_pycharm [n]: y
windows [n]: n windows [n]: n
@ -229,6 +227,12 @@ Got a blog or online publication? Write about your cookiecutter-django tips and
.. _`Introduction to Cookiecutter-Django`: http://krzysztofzuraw.com/blog/2016/django-cookiecutter.html .. _`Introduction to Cookiecutter-Django`: http://krzysztofzuraw.com/blog/2016/django-cookiecutter.html
.. _`Django and GitLab - Running Continuous Integration and tests with your FREE account`: http://dezoito.github.io/2016/05/11/django-gitlab-continuous-integration-phantomjs.html .. _`Django and GitLab - Running Continuous Integration and tests with your FREE account`: http://dezoito.github.io/2016/05/11/django-gitlab-continuous-integration-phantomjs.html
Code of Conduct
---------------
Everyone interacting in the Cookiecutter project's codebases, issue trackers, chat
rooms, and mailing lists is expected to follow the `PyPA Code of Conduct`_.
Support This Project Support This Project
--------------------------- ---------------------------
@ -239,3 +243,5 @@ This project is maintained by volunteers. Support their efforts by spreading the
:align: center :align: center
:alt: Two Scoops Academy :alt: Two Scoops Academy
:target: http://www.twoscoops.academy/ :target: http://www.twoscoops.academy/
.. _`PyPA Code of Conduct`: https://www.pypa.io/en/latest/code-of-conduct/

View File

@ -7,20 +7,18 @@
"domain_name": "example.com", "domain_name": "example.com",
"version": "0.1.0", "version": "0.1.0",
"timezone": "UTC", "timezone": "UTC",
"now": "{% now 'local' %}",
"year": "{{ cookiecutter.now[:4] }}",
"use_whitenoise": "y", "use_whitenoise": "y",
"use_celery": "n", "use_celery": "n",
"use_mailhog": "n", "use_mailhog": "n",
"use_sentry": "n", "use_sentry_for_error_reporting": "y",
"use_newrelic": "n",
"use_opbeat": "n", "use_opbeat": "n",
"use_pycharm": "n", "use_pycharm": "n",
"windows": "n", "windows": "n",
"use_python2": "n", "use_python2": "n",
"use_docker": "y", "use_docker": "y",
"use_heroku": "n", "use_heroku": "n",
"use_grunt": "n",
"use_compressor": "n", "use_compressor": "n",
"js_task_runner": ["Gulp", "Grunt", "None"],
"use_lets_encrypt": "n",
"open_source_license": ["MIT", "BSD", "Apache Software License 2.0", "Not open source"] "open_source_license": ["MIT", "BSD", "Apache Software License 2.0", "Not open source"]
} }

View File

@ -46,7 +46,7 @@ master_doc = 'index'
# General information about the project. # General information about the project.
project = 'Cookiecutter Django' project = 'Cookiecutter Django'
copyright = '2013-{}, Daniel Roy Greenfeld'.format(now.year) copyright = "2013-2016, Daniel Roy Greenfeld".format(now.year)
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the

View File

@ -1,49 +1,104 @@
Deployment with Docker Deployment with Docker
================================================= =======================
.. index:: Docker, deployment .. index:: Docker, deployment
TODO: Review and revise Prerequisites
-------------
**Warning** * Docker (at least 1.10)
* Docker Compose (at least 1.6)
Docker is evolving extremely fast, but it has still some rough edges here and there. Compose is currently (as of version 1.4) Understand the Compose Setup
not considered production ready. That means you won't be able to scale to multiple servers and you won't be able to run --------------------------------
zero downtime deployments out of the box. Consider all this as experimental until you understand all the implications
to run docker (with compose) on production.
**Run your app with docker-compose**
Prerequisites:
* docker (at least 1.10)
* docker-compose (at least 1.6)
Before you start, check out the `docker-compose.yml` file in the root of this project. This is where each component Before you start, check out the `docker-compose.yml` file in the root of this project. This is where each component
of this application gets its configuration from. It consists of a `postgres` service that runs the database, `redis` of this application gets its configuration from. Notice how it provides configuration for these services:
for caching, `nginx` as reverse proxy and last but not least the `django` application run by gunicorn.
{% if cookiecutter.use_celery == 'y' -%}
Since this application also runs Celery, there are two more services with a service called `celeryworker` that runs the
celery worker process and `celerybeat` that runs the celery beat process.
{% endif %}
* `postgres` service that runs the database
* `redis` for caching
* `nginx` as reverse proxy
* `django` is the Django project run by gunicorn
All of these services except `redis` rely on environment variables set by you. There is an `env.example` file in the If you chose the `use_celery` option, there are two more services:
* `celeryworker` which runs the celery worker process
* `celerybeat` which runs the celery beat process
If you chose the `use_letsencrypt` option, you also have:
* `certbot` which keeps your certs from letsencrypt up-to-date
Populate .env With Your Environment Variables
---------------------------------------------
Some of these services rely on environment variables set by you. There is an `env.example` file in the
root directory of this project as a starting point. Add your own variables to the file and rename it to `.env`. This root directory of this project as a starting point. Add your own variables to the file and rename it to `.env`. This
file won't be tracked by git by default so you'll have to make sure to use some other mechanism to copy your secret if file won't be tracked by git by default so you'll have to make sure to use some other mechanism to copy your secret if
you are relying solely on git. you are relying solely on git.
Optional: nginx-proxy Setup
---------------------------
By default, the application is configured to listen on all interfaces on port 80. If you want to change that, open the By default, the application is configured to listen on all interfaces on port 80. If you want to change that, open the
`docker-compose.yml` file and replace `0.0.0.0` with your own ip. If you are using `nginx-proxy`_ to run multiple `docker-compose.yml` file and replace `0.0.0.0` with your own ip.
application stacks on one host, remove the port setting entirely and add `VIRTUAL_HOST={{cookiecutter.domain_name}}` to your env file.
If you are using `nginx-proxy`_ to run multiple application stacks on one host, remove the port setting entirely and add `VIRTUAL_HOST=example.com` to your env file. Here, replace example.com with the value you entered for `domain_name`.
This pass all incoming requests on `nginx-proxy`_ to the nginx service your application is using. This pass all incoming requests on `nginx-proxy`_ to the nginx service your application is using.
.. _nginx-proxy: https://github.com/jwilder/nginx-proxy .. _nginx-proxy: https://github.com/jwilder/nginx-proxy
Optional: Postgres Data Volume Modifications
---------------------------------------------
Postgres is saving its database files to the `postgres_data` volume by default. Change that if you wan't Postgres is saving its database files to the `postgres_data` volume by default. Change that if you wan't
something else and make sure to make backups since this is not done automatically. something else and make sure to make backups since this is not done automatically.
Optional: Certbot and Let's Encrypt Setup
------------------------------------------
If you chose `use_letsencrypt` and will be using certbot for https, you must do the following before running anything with docker-compose:
Replace dhparam.pem.example with a generated dhparams.pem file before running anything with docker-compose. You can generate this on ubuntu or OS X by running the following in the project root:
::
$ openssl dhparam -out /path/to/project/compose/nginx/dhparams.pem 2048
If you would like to add additional subdomains to your certificate, you must add additional parameters to the certbot command in the `docker-compose.yml` file:
Replace:
::
command: bash -c "sleep 6 && certbot certonly -n --standalone -d {{ cookiecutter.domain_name }} --text --agree-tos --email mjsisley@relawgo.com --server https://acme-v01.api.letsencrypt.org/directory --rsa-key-size 4096 --verbose --keep-until-expiring --standalone-supported-challenges http-01"
With:
::
command: bash -c "sleep 6 && certbot certonly -n --standalone -d {{ cookiecutter.domain_name }} -d www.{{ cookiecutter.domain_name }} -d etc.{{ cookiecutter.domain_name }} --text --agree-tos --email {{ cookiecutter.email }} --server https://acme-v01.api.letsencrypt.org/directory --rsa-key-size 4096 --verbose --keep-until-expiring --standalone-supported-challenges http-01"
Please be cognizant of Certbot/Letsencrypt certificate requests limits when getting this set up. The provide a test server that does not count against the limit while you are getting set up.
The certbot certificates expire after 3 months.
If you would like to set up autorenewal of your certificates, the following commands can be put into a bash script:
::
#!/bin/bash
cd <project directory>
docker-compose run certbot bash -c "sleep 6 && certbot certonly --standalone -d {{ cookiecutter.domain_name }} --text --agree-tos --email {{ cookiecutter.email }} --server https://acme-v01.api.letsencrypt.org/directory --rsa-key-size 4096 --verbose --keep-until-expiring --standalone-supported-challenges http-01"
docker exec pearl_nginx_1 nginx -s reload
And then set a cronjob by running `crontab -e` and placing in it (period can be adjusted as desired)::
0 4 * * 1 /path/to/bashscript/renew_certbot.sh
Run your app with docker-compose
--------------------------------
To get started, pull your code from source control (don't forget the `.env` file) and change to your projects root To get started, pull your code from source control (don't forget the `.env` file) and change to your projects root
directory. directory.
@ -55,7 +110,6 @@ Once this is ready, you can run it with::
docker-compose up docker-compose up
To run a migration, open up a second terminal and run:: To run a migration, open up a second terminal and run::
docker-compose run django python manage.py migrate docker-compose run django python manage.py migrate
@ -64,7 +118,6 @@ To create a superuser, run::
docker-compose run django python manage.py createsuperuser docker-compose run django python manage.py createsuperuser
If you need a shell, run:: If you need a shell, run::
docker-compose run django python manage.py shell docker-compose run django python manage.py shell
@ -80,8 +133,15 @@ If you want to scale your application, run::
docker-compose scale django=4 docker-compose scale django=4
docker-compose scale celeryworker=2 docker-compose scale celeryworker=2
.. warning:: Don't run the scale command on postgres, celerybeat, certbot, or nginx.
**Don't run the scale command on postgres or celerybeat** If you have errors, you can always check your stack with `docker-compose`. Switch to your projects root directory and run::
docker-compose ps
Supervisor Example
-------------------
Once you are ready with your initial setup, you wan't to make sure that your application is run by a process manager to Once you are ready with your initial setup, you wan't to make sure that your application is run by a process manager to
survive reboots and auto restarts in case of an error. You can use the process manager you are most familiar with. All survive reboots and auto restarts in case of an error. You can use the process manager you are most familiar with. All
@ -97,7 +157,6 @@ If you are using `supervisor`, you can use this file as a starting point::
autorestart=true autorestart=true
priority=10 priority=10
Place it in `/etc/supervisor/conf.d/{{cookiecutter.project_slug}}.conf` and run:: Place it in `/etc/supervisor/conf.d/{{cookiecutter.project_slug}}.conf` and run::
supervisorctl reread supervisorctl reread
@ -106,7 +165,3 @@ Place it in `/etc/supervisor/conf.d/{{cookiecutter.project_slug}}.conf` and run:
To get the status, run:: To get the status, run::
supervisorctl status supervisorctl status
If you have errors, you can always check your stack with `docker-compose`. Switch to your projects root directory and run::
docker-compose ps

View File

@ -1,5 +1,5 @@
Getting Up and Running with Docker Getting Up and Running Locally With Docker
================================== ==========================================
.. index:: Docker .. index:: Docker
@ -9,7 +9,7 @@ All of these commands assume you are in the root of your generated project.
Prerequisites Prerequisites
------------- -------------
You'll need at least docker 1.10. You'll need at least Docker 1.10.
If you don't already have it installed, follow the instructions for your OS: If you don't already have it installed, follow the instructions for your OS:
@ -91,35 +91,6 @@ Production Mode
Instead of using `dev.yml`, you would use `docker-compose.yml`. Instead of using `dev.yml`, you would use `docker-compose.yml`.
Database Backups
~~~~~~~~~~~~~~~~
The database has to be running to create/restore a backup.
First, run the app with `docker-compose -f dev.yml up`.
To create a backup, run::
docker-compose -f dev.yml run postgres backup
To list backups, run::
docker-compose -f dev.yml run postgres list-backups
To restore a backup, run::
docker-compose -f dev.yml run postgres restore filename.sql
To copy the files from the running Postgres container to the host system::
docker cp <containerId>:/backups /host/path/target
Where <containerId> is the ID of the Postgres container. To get it, run::
docker ps
Other Useful Tips Other Useful Tips
----------------- -----------------
@ -160,6 +131,7 @@ Then you may need to run the following for it to work as desired:
$ docker-compose run -f dev.yml --service-ports django $ docker-compose run -f dev.yml --service-ports django
django-debug-toolbar django-debug-toolbar
"""""""""""""""""""" """"""""""""""""""""
@ -182,4 +154,12 @@ You may need to add the following to your css in order for the django-debug-tool
} }
Using the Mailhog Docker Container
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In development you can (optionally) use MailHog_ for email testing. If you selected `use_docker`, MailHog is added as a Docker container. To use MailHog:
1. Make sure, that ``mailhog`` docker container is up and running
2. Open your browser and go to ``http://127.0.0.1:8025``
.. _Mailhog: https://github.com/mailhog/MailHog/

View File

@ -9,47 +9,50 @@ The steps below will get you up and running with a local development environment
* virtualenv * virtualenv
* PostgreSQL * PostgreSQL
First make sure to create and activate a virtualenv_, then open a terminal at the project root and install the os dependencies:: First make sure to create and activate a virtualenv_.
$ sudo ./install_os_dependencies.sh install .. _virtualenv: http://docs.python-guide.org/en/latest/dev/virtualenvs/
Then install the requirements for your local development:: Then install the requirements for your local development::
$ pip install -r requirements/local.txt $ pip install -r requirements/local.txt
.. _virtualenv: http://docs.python-guide.org/en/latest/dev/virtualenvs/
Then, create a PostgreSQL database with the following command, where `[project_slug]` is what value you entered for your project's `project_slug`:: Then, create a PostgreSQL database with the following command, where `[project_slug]` is what value you entered for your project's `project_slug`::
$ createdb [project_slug] $ createdb [project_slug]
`Cookiecutter Django` uses the excellent `django-environ`_ package with its ``DATABASE_URL`` environment variable to simplify database configuration in your Django settings. Now all you have to do is rename env.example to .env and then compose a definition for ``DATABASE_URL`` as shown below and add it to the .env file:
.. parsed-literal::
$ export DATABASE_URL="postgres://*<pg_user_name>*:*<pg_user_password>*\ @127.0.0.1:\ *<pg_port>*/*<pg_database_name>*"
.. _django-environ: http://django-environ.readthedocs.io
You can now run the usual Django ``migrate`` and ``runserver`` commands:: You can now run the usual Django ``migrate`` and ``runserver`` commands::
$ python manage.py migrate $ python manage.py migrate
$ python manage.py runserver $ python manage.py runserver
**Setup your email backend** At this point you can take a break from setup and start getting to know the files in the project.
But if you want to go further with setup, read on.
(Note: the following sections still need to be revised)
Setting Up Env Vars for Production
-----------------------------------
`Cookiecutter Django` uses the excellent `django-environ`_ package, which includes a ``DATABASE_URL`` environment variable to simplify database configuration in your Django settings.
Rename env.example to .env to begin updating the file with your own environment variables. To add your database, define ``DATABASE_URL`` and add it to the .env file, as shown below:
.. parsed-literal::
DATABASE_URL="postgres://*<pg_user_name>*:*<pg_user_password>*\ @127.0.0.1:\ *<pg_port>*/*<pg_database_name>*"
.. _django-environ: http://django-environ.readthedocs.io
Setup your email backend
-------------------------
django-allauth sends an email to verify users (and superusers) after signup and login (if they are still not verified). To send email you need to `configure your email backend`_ django-allauth sends an email to verify users (and superusers) after signup and login (if they are still not verified). To send email you need to `configure your email backend`_
.. _configure your email backend: http://docs.djangoproject.com/en/1.9/topics/email/#smtp-backend .. _configure your email backend: http://docs.djangoproject.com/en/1.9/topics/email/#smtp-backend
{% if cookiecutter.use_docker == 'y' %}
In development you can (optionally) use MailHog_ for email testing. MailHog is added as docker-container. To use MailHog::
1. Make sure, that ``mailhog`` docker container is up and running In development you can (optionally) use MailHog_ for email testing. MailHog is built with Go so there are no dependencies. To use MailHog:
2. Open your browser and go to ``http://127.0.0.1:8025``
.. _Mailhog: https://github.com/mailhog/MailHog/
{% else %}
In development you can (optionally) use MailHog_ for email testing. MailHog is built with Go so there are no dependencies. To use MailHog::
1. `Download the latest release`_ for your operating system 1. `Download the latest release`_ for your operating system
2. Rename the executable to ``mailhog`` and copy it to the root of your project directory 2. Rename the executable to ``mailhog`` and copy it to the root of your project directory
@ -59,7 +62,7 @@ In development you can (optionally) use MailHog_ for email testing. MailHog is b
.. _Mailhog: https://github.com/mailhog/MailHog/ .. _Mailhog: https://github.com/mailhog/MailHog/
.. _Download the latest release: https://github.com/mailhog/MailHog/releases .. _Download the latest release: https://github.com/mailhog/MailHog/releases
{% endif %}
Alternatively simply output emails to the console via: ``EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'`` Alternatively simply output emails to the console via: ``EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'``
In production basic email configuration is setup to send emails with Mailgun_ In production basic email configuration is setup to send emails with Mailgun_
@ -68,22 +71,6 @@ In production basic email configuration is setup to send emails with Mailgun_
**Live reloading and Sass CSS compilation** **Live reloading and Sass CSS compilation**
If you'd like to take advantage of live reloading and Sass / Compass CSS compilation you can do so with the included Grunt task. If youd like to take advantage of live reloading and Sass / Compass CSS compilation you can do so with a little bit of `prep work`_.
Make sure that nodejs_ is installed. Then in the project root run:: .. _prep work: https://cookiecutter-django.readthedocs.io/en/latest/live-reloading-and-sass-compilation.html
$ npm install
.. _nodejs: http://nodejs.org/download/
Now you just need::
$ grunt serve
The base app will now run as it would with the usual ``manage.py runserver`` but with live reloading and Sass compilation enabled.
To get live reloading to work you'll probably need to install an `appropriate browser extension`_
.. _appropriate browser extension: http://feedback.livereload.com/knowledgebase/articles/86242-how-do-i-install-and-use-the-browser-extensions-
It's time to write the code!!!

View File

@ -0,0 +1,40 @@
============================
Database Backups with Docker
============================
The database has to be running to create/restore a backup. These examples show local examples. If you want to use it on a remote server, remove ``-f dev.yml`` from each example.
Running Backups
================
Run the app with `docker-compose -f dev.yml up`.
To create a backup, run::
docker-compose -f dev.yml run postgres backup
To list backups, run::
docker-compose -f dev.yml run postgres list-backups
To restore a backup, run::
docker-compose -f dev.yml run postgres restore filename.sql
Where <containerId> is the ID of the Postgres container. To get it, run::
docker ps
To copy the files from the running Postgres container to the host system::
docker cp <containerId>:/backups /host/path/target
Restoring From Backups
======================
To restore the production database to a local PostgreSQL database::
createdb NAME_OF_DATABASE
psql NAME_OF_DATABASE < NAME_OF_BACKUP_FILE

View File

@ -7,14 +7,11 @@ A Cookiecutter_ template for Django.
.. _cookiecutter: https://github.com/audreyr/cookiecutter .. _cookiecutter: https://github.com/audreyr/cookiecutter
.. note:: This is an in-progress documentation reorganization. Locations of files may change dramatically over the course of the next few days. See https://github.com/pydanny/cookiecutter-django/issues/335
Contents: Contents:
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
project-generation-options project-generation-options
developing-locally developing-locally
developing-locally-docker developing-locally-docker
@ -24,8 +21,10 @@ Contents:
deployment-on-pythonanywhere deployment-on-pythonanywhere
deployment-on-heroku deployment-on-heroku
deployment-with-docker deployment-with-docker
docker-postgres-backups
faq faq
troubleshooting troubleshooting
my-favorite-cookie
Indices and tables Indices and tables
================== ==================

100
docs/my-favorite-cookie.rst Normal file
View File

@ -0,0 +1,100 @@
************************************************
Creating your first app with Cookiecutter-Django
************************************************
This tutorial will show you how to build a simple app using the `Cookiecutter Django <https://github.com/pydanny/cookiecutter-django>`_ templating system. We'll be building a cookie polling app to determine the most popular flavor of cookie.
Developers who have never used Django will learn the basics of creating a Django app; developers who are experienced with Django will learn how to set up a project within the Cookiecutter system. While many Django tutorials use the default SQLite database, Cookiecutter Django uses PostGres only, so we'll have you install and use that.
Dependencies
============
This tutorial was written on Windows 10 using `git bash <https://git-for-windows.github.io/>`_; alternate instructions for Mac OS and Linux will be provided when needed. Any Linux-style shell should work for the following commands.
You should have your preferred versions of `Python <https://www.python.org/downloads/>`_
and `Django <https://www.djangoproject.com/download/>`_ installed. Use the latest stable versions if you have no preference.
You should have `Virtualenv <https://virtualenv.pypa.io/en/stable/>`_ and `Cookiecutter <https://github.com/pydanny/cookiecutter-django/>`_ installed:
.. code-block:: python
$ pip install virtualenv
$ pip install cookiecutter
You should also have `PostgreSQL <https://www.postgresql.org/download/>`_ installed on your machine--just download and run the installer for your OS. The install menu will prompt you for a password, which you'll use when creating the project's database.
Instructions
============
1. **Setup** -- how to set up a virtual environment
2. **Cookiecutter** -- use Cookiecutter to initialize a project with your own customized information.
3. **Building the App** -- creating the My Favorite Cookie application.
============
1. Setup
============
Virtual Environment
"""""""""""""""""""
Create a virtual environment for your project. Cookiecutter will install a bunch of dependencies for you automatically; using a virtualenv will prevent this from interfering with your other work.
.. code-block:: python
$ virtualenv c:/.virtualenvs/cookie_polls
Replace ``c:/.virtualenvs`` with the path to your own ``.virtualenvs`` folder.
Activate the virtual environment by calling ``source`` on the ``activate`` shell script . On Windows you'll call this from the virtualenv's ``scripts`` folder:
.. code-block:: python
$ source /path/to/.virtualenvs/cookie_polls/scripts/activate
On other operating systems, it'll be found in the ``bin`` folder.
.. code-block:: python
$ source /path/to/.virtualenvs/cookie_polls/bin/activate
You'll know the virtual environment is active because its name will appear in parentheses before the command prompt. When you're done with this project, you can leave the virtual environment with the ``deactivate`` command.
.. code-block:: python
(cookie_polls)
$ deactivate
Now you're ready to create your project using Cookiecutter.
===============
2. Cookiecutter
===============
Django developers may be familiar with the ``startproject`` command, which initializes the directory structure and required files for a bare-bones Django project. While this is fine when you're just learning Django for the first time, it's not great for a real production app. Cookiecutter takes care of a lot of standard tasks for you, including installing software dependencies, setting up testing files, and including and organizing common libraries like Bootstrap and AngularJS. It also generates a software license and a README.
Change directories into the folder where you want your project to live, and run ``cookiecutter`` followed by the URL of Cookiecutter's Github repo.
.. code-block:: python
$ cd /my/project/folder
(cookie_polls)
my/project/folder
$ cookiecutter https://github.com/pydanny/cookiecutter-django
This will prompt you for a bunch of values specific to your project. Press "enter" without typing anything to use the default values, which are shown in [brackets] after the question. You can learn about all the different options `here, <http://cookiecutter-django.readthedocs.io/en/latest/project-generation-options.html>`_ but for now we'll use the defaults for everything but your name, your email, the project's name, and the project's description.
.. code-block:: python
project_name [project_name]: My Favorite Cookie
project_slug [My_Favorite_Cookie]:
author_name [Your Name]: Emily Cain
email [Your email]: contact@emcain.net
description [A short description of the project.]: Poll your friends to determine the most popular cookie.
Then hit "enter" to use the default values for everything else.

View File

@ -39,7 +39,7 @@ use_mailhog [n]
for development purposes. It runs a simple SMTP server which catches for development purposes. It runs a simple SMTP server which catches
any message sent to it. Messages are displayed in a web interface which runs at ``http://localhost:8025/`` You need to download the MailHog executable for your operating system, see the 'Developing Locally' docs for instructions. any message sent to it. Messages are displayed in a web interface which runs at ``http://localhost:8025/`` You need to download the MailHog executable for your operating system, see the 'Developing Locally' docs for instructions.
use_sentry [n] use_sentry_for_error_reporting [n]
Whether to use Sentry_ to log errors from your project. Whether to use Sentry_ to log errors from your project.
windows [n] windows [n]

View File

@ -82,6 +82,11 @@ def make_secret_key(project_directory):
set_secret_key(env_file) set_secret_key(env_file)
def remove_file(file_name):
if os.path.exists(file_name):
os.remove(file_name)
def remove_task_app(project_directory): def remove_task_app(project_directory):
"""Removes the taskapp if celery isn't going to be used""" """Removes the taskapp if celery isn't going to be used"""
# Determine the local_setting_file_location # Determine the local_setting_file_location
@ -111,9 +116,8 @@ def remove_heroku_files():
Removes files needed for heroku if it isn't going to be used Removes files needed for heroku if it isn't going to be used
""" """
for filename in ["app.json", "Procfile", "requirements.txt", "runtime.txt"]: for filename in ["app.json", "Procfile", "requirements.txt", "runtime.txt"]:
os.remove(os.path.join( file_name = os.path.join(PROJECT_DIRECTORY, filename)
PROJECT_DIRECTORY, filename remove_file(file_name)
))
def remove_docker_files(): def remove_docker_files():
@ -134,11 +138,38 @@ def remove_grunt_files():
""" """
Removes files needed for grunt if it isn't going to be used Removes files needed for grunt if it isn't going to be used
""" """
for filename in ["Gruntfile.js", "package.json"]: for filename in ["Gruntfile.js"]:
os.remove(os.path.join( os.remove(os.path.join(
PROJECT_DIRECTORY, filename PROJECT_DIRECTORY, filename
)) ))
def remove_gulp_files():
"""
Removes files needed for grunt if it isn't going to be used
"""
for filename in ["gulpfile.js"]:
os.remove(os.path.join(
PROJECT_DIRECTORY, filename
))
def remove_packageJSON_file():
"""
Removes files needed for grunt if it isn't going to be used
"""
for filename in ["package.json"]:
os.remove(os.path.join(
PROJECT_DIRECTORY, filename
))
def remove_certbot_files():
"""
Removes files needed for certbot if it isn't going to be used
"""
nginx_dir_location = os.path.join(PROJECT_DIRECTORY, 'compose/nginx')
for filename in ["nginx-secure.conf", "start.sh", "dhparams.example.pem"]:
file_name = os.path.join(nginx_dir_location, filename)
remove_file(file_name)
# IN PROGRESS # IN PROGRESS
# def copy_doc_files(project_directory): # def copy_doc_files(project_directory):
# cookiecutters_dir = DEFAULT_CONFIG['cookiecutters_dir'] # cookiecutters_dir = DEFAULT_CONFIG['cookiecutters_dir']
@ -176,27 +207,42 @@ if '{{ cookiecutter.use_heroku }}'.lower() != 'y':
if '{{ cookiecutter.use_docker }}'.lower() != 'y': if '{{ cookiecutter.use_docker }}'.lower() != 'y':
remove_docker_files() remove_docker_files()
# 6. Removes all grunt files if it isn't going to be used # 6. Removes all JS task manager files if it isn't going to be used
if '{{ cookiecutter.use_grunt }}'.lower() != 'y': if '{{ cookiecutter.js_task_runner}}'.lower() == 'gulp':
remove_grunt_files() remove_grunt_files()
elif '{{ cookiecutter.js_task_runner}}'.lower() == 'grunt':
remove_gulp_files()
else:
remove_gulp_files()
remove_grunt_files()
remove_packageJSON_file()
# 7. Removes all certbot/letsencrypt files if it isn't going to be used
if '{{ cookiecutter.use_lets_encrypt }}'.lower() != 'y':
remove_certbot_files()
# 7. Display a warning if use_docker and use_grunt are selected. Grunt isn't supported by our # 8. Display a warning if use_docker and use_grunt are selected. Grunt isn't
# docker config atm. # supported by our docker config atm.
if '{{ cookiecutter.use_grunt }}'.lower() == 'y' and '{{ cookiecutter.use_docker }}'.lower() == 'y': if '{{ cookiecutter.js_task_runner }}'.lower() in ['grunt', 'gulp'] and '{{ cookiecutter.use_docker }}'.lower() == 'y':
print( print(
"You selected to use docker and grunt. This is NOT supported out of the box for now. You " "You selected to use docker and a JS task runner. This is NOT supported out of the box for now. You "
"can continue to use the project like you normally would, but you will need to add a " "can continue to use the project like you normally would, but you will need to add a "
" grunt service to your docker configuration manually." "js task runner service to your docker configuration manually."
) )
# 7. Display a warning if use_docker and use_mailhog are selected. Mailhog isn't supported by our # 9. Removes the certbot/letsencrypt files and display a warning if use_lets_encrypt is selected and use_docker isn't.
# docker config atm. if '{{ cookiecutter.use_lets_encrypt }}'.lower() == 'y' and '{{ cookiecutter.use_docker }}'.lower() != 'y':
if '{{ cookiecutter.use_mailhog }}'.lower() == 'y' and '{{ cookiecutter.use_docker }}'.lower() == 'y': remove_certbot_files()
print( print(
"You selected to use docker and mailhog. This is NOT supported out of the box for now. You" "You selected to use Let's Encrypt and didn't select to use docker. This is NOT supported out of the box for now. You "
" can continue to use the project like you normally would, but you will need to add a " "can continue to use the project like you normally would, but Let's Encrypt files have been included."
" mailhog service to your docker configuration manually." )
# 10. Directs the user to the documentation if certbot and docker are selected.
if '{{ cookiecutter.use_lets_encrypt }}'.lower() == 'y' and '{{ cookiecutter.use_docker }}'.lower() == 'y':
print(
"You selected to use Let's Encrypt, please see the documentation for instructions on how to use this in production. "
"You must generate a dhparams.pem file before running docker-compose in a production environment."
) )
# 4. Copy files from /docs/ to {{ cookiecutter.project_slug }}/docs/ # 4. Copy files from /docs/ to {{ cookiecutter.project_slug }}/docs/

View File

@ -1,8 +1,6 @@
import cookiecutter
project_slug = '{{ cookiecutter.project_slug }}' project_slug = '{{ cookiecutter.project_slug }}'
if hasattr(project_slug, 'isidentifier'): if hasattr(project_slug, 'isidentifier'):
assert project_slug.isidentifier(), 'Project slug should be valid Python identifier!' assert project_slug.isidentifier(), 'Project slug should be valid Python identifier!'
assert cookiecutter.__version__ > '1.3.0', 'Please upgrade your Cookiecutter installation'

View File

@ -1,10 +1,10 @@
cookiecutter==1.4.0 cookiecutter==1.4.0
flake8==2.5.4 flake8==2.6.0
sh==1.11 sh==1.11
binaryornot==0.4.0 binaryornot==0.4.0
# Testing # Testing
pytest==2.9.1 pytest==2.9.2
pep8==1.7.0 pep8==1.7.0
pyflakes==1.2.3 pyflakes==1.2.3
tox==2.3.1 tox==2.3.1

View File

@ -22,8 +22,6 @@ def context():
'domain_name': 'example.com', 'domain_name': 'example.com',
'version': '0.1.0', 'version': '0.1.0',
'timezone': 'UTC', 'timezone': 'UTC',
'now': '2015/01/13',
'year': '2015'
} }

View File

@ -1,6 +1,6 @@
{% if cookiecutter.open_source_license == 'MIT' %} {% if cookiecutter.open_source_license == 'MIT' %}
The MIT License (MIT) The MIT License (MIT)
Copyright (c) {{ cookiecutter.year }}, {{ cookiecutter.author_name }} Copyright (c) {% now 'utc', '%Y' %}, {{ cookiecutter.author_name }}
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
@ -8,7 +8,7 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
{% elif cookiecutter.open_source_license == 'BSD' %} {% elif cookiecutter.open_source_license == 'BSD' %}
Copyright (c) {{ cookiecutter.year }}, {{ cookiecutter.author_name }} Copyright (c) {% now 'utc', '%Y' %}, {{ cookiecutter.author_name }}
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, Redistribution and use in source and binary forms, with or without modification,

View File

@ -1,4 +1,4 @@
web: gunicorn config.wsgi:application web: gunicorn config.wsgi:application
{% if cookiecutter.use_celery == "y" -%} {% if cookiecutter.use_celery == "y" -%}
worker: {% if cookiecutter.use_newrelic == "y" %}newrelic-admin run-program {% endif %}celery worker --app={{cookiecutter.project_slug}}.taskapp --loglevel=info worker: celery worker --app={{cookiecutter.project_slug}}.taskapp --loglevel=info
{%- endif %} {%- endif %}

View File

@ -104,12 +104,12 @@ The email server will exit when you exit the Grunt task on the CLI with Ctrl+C.
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if cookiecutter.use_sentry == "y" %} {% if cookiecutter.use_sentry_for_error_reporting == "y" %}
Sentry Sentry
^^^^^^ ^^^^^^
Sentry is an error logging aggregator service. You can sign up for a free account at http://getsentry.com or download and host it yourself. Sentry is an error logging aggregator service. You can sign up for a free account at https://getsentry.com/signup/?code=cookiecutter or download and host it yourself.
The system is setup with reasonable defaults, including 404 logging and integration with the WSGI application. The system is setup with reasonable defaults, including 404 logging and integration with the WSGI application.
You must set the DSN url in production. You must set the DSN url in production.

View File

@ -20,12 +20,8 @@
"DJANGO_AWS_SECRET_ACCESS_KEY": "", "DJANGO_AWS_SECRET_ACCESS_KEY": "",
"DJANGO_AWS_STORAGE_BUCKET_NAME": "", "DJANGO_AWS_STORAGE_BUCKET_NAME": "",
"DJANGO_MAILGUN_SERVER_NAME": "", "DJANGO_MAILGUN_SERVER_NAME": "",
{% if cookiecutter.use_newrelic == "y" -%} "DJANGO_MAILGUN_API_KEY": ""{% if cookiecutter.use_sentry_for_error_reporting == "y" -%},
"NEW_RELIC_LICENSE_KEY": "", "DJANGO_SENTRY_DSN": ""{%- endif %}
"NEW_RELIC_APP_NAME": "",
{%- endif %}
"DJANGO_MAILGUN_API_KEY": ""{% if cookiecutter.use_sentry == "y" -%},
"DJANGO_SENTRY_DSN": ""{%- endif %}
}, },
"scripts": { "scripts": {
"postdeploy": "python manage.py migrate" "postdeploy": "python manage.py migrate"

View File

@ -8,18 +8,21 @@ ENV PYTHONUNBUFFERED 1
# Requirements have to be pulled and installed here, otherwise caching won't work # Requirements have to be pulled and installed here, otherwise caching won't work
COPY ./requirements /requirements COPY ./requirements /requirements
RUN pip install -r /requirements/production.txt RUN pip install -r /requirements/production.txt \
&& groupadd -r django \
&& useradd -r -g django django
RUN groupadd -r django && useradd -r -g django django
COPY . /app COPY . /app
RUN chown -R django /app RUN chown -R django /app
COPY ./compose/django/gunicorn.sh /gunicorn.sh COPY ./compose/django/gunicorn.sh /gunicorn.sh
COPY ./compose/django/entrypoint.sh /entrypoint.sh COPY ./compose/django/entrypoint.sh /entrypoint.sh
RUN sed -i 's/\r//' /entrypoint.sh RUN sed -i 's/\r//' /entrypoint.sh \
RUN sed -i 's/\r//' /gunicorn.sh && sed -i 's/\r//' /gunicorn.sh \
RUN chmod +x /entrypoint.sh && chown django /entrypoint.sh && chmod +x /entrypoint.sh \
RUN chmod +x /gunicorn.sh && chown django /gunicorn.sh && chown django /entrypoint.sh \
&& chmod +x /gunicorn.sh \
&& chown django /gunicorn.sh
WORKDIR /app WORKDIR /app

View File

@ -1,2 +1,8 @@
FROM nginx:latest FROM nginx:latest
ADD nginx.conf /etc/nginx/nginx.conf ADD nginx.conf /etc/nginx/nginx.conf
{% if cookiecutter.use_lets_encrypt == 'y' and cookiecutter.use_docker == 'y' %}
ADD start.sh /start.sh
ADD nginx-secure.conf /etc/nginx/nginx-secure.conf
ADD dhparams.pem /etc/ssl/private/dhparams.pem
{% endif %}

View File

@ -0,0 +1,3 @@
-----BEGIN DH PARAMETERS-----
EXAMPLE_FILE
-----END DH PARAMETERS-----

View File

@ -0,0 +1,92 @@
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
proxy_headers_hash_bucket_size 52;
gzip on;
upstream app {
server django:5000;
}
server {
listen 80;
server_name ___my.example.com___ www.___my.example.com___;
location /.well-known/acme-challenge {
proxy_pass http://___LETSENCRYPT_IP___:___LETSENCRYPT_PORT___;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
return 301 https://$server_name$request_uri;
}
}
server {
listen 443;
server_name ___my.example.com___ www.___my.example.com___;
ssl on;
ssl_certificate /etc/letsencrypt/live/___my.example.com___/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/___my.example.com___/privkey.pem;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_dhparam /etc/ssl/private/dhparams.pem;
location /.well-known/acme-challenge {
proxy_pass http://___LETSENCRYPT_HTTPS_IP___:___LETSENCRYPT_HTTPS_PORT___;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto https;
}
location / {
# checks for static file, if not found proxy to app
try_files $uri @proxy_to_app;
}
# cookiecutter-django app
location @proxy_to_app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app;
}
}
}

View File

@ -9,9 +9,9 @@ events {
worker_connections 1024; worker_connections 1024;
} }
http { http {
include /etc/nginx/mime.types;
include /etc/nginx/mime.types;
default_type application/octet-stream; default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' log_format main '$remote_addr - $remote_user [$time_local] "$request" '
@ -31,23 +31,40 @@ http {
server django:5000; server django:5000;
} }
server { server {
listen 80; listen 80;
charset utf-8; charset utf-8;
{% if cookiecutter.use_lets_encrypt == 'y' and cookiecutter.use_docker == 'y' %}
server_name ___my.example.com___ ;
location / { location /.well-known/acme-challenge {
proxy_pass http://___LETSENCRYPT_IP___:___LETSENCRYPT_PORT___;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto https;
}
{% endif %}
location / {
# checks for static file, if not found proxy to app # checks for static file, if not found proxy to app
try_files $uri @proxy_to_app; try_files $uri @proxy_to_app;
} }
location @proxy_to_app {
# cookiecutter-django app
location @proxy_to_app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_redirect off; proxy_redirect off;
proxy_pass http://app; proxy_pass http://app;
}
} }
} }
}

View File

@ -0,0 +1,81 @@
echo sleep 5
sleep 5
echo build starting nginx config
echo replacing ___my.example.com___/$MY_DOMAIN_NAME
echo replacing ___LETSENCRYPT_IP___/$LETSENCRYPT_PORT_80_TCP_ADDR
echo replacing ___LETSENCRYPT_PORT___/$LETSENCRYPT_PORT_80_TCP_PORT
echo replacing ___APPLICATION_IP___/$APP_PORT_80_TCP_ADDR
echo replacing ___APPLICATION_PORT___/$APP_PORT_80_TCP_PORT
# Put your domain name into the nginx reverse proxy config.
sed -i "s/___my.example.com___/$MY_DOMAIN_NAME/g" /etc/nginx/nginx.conf
# Add your app's container IP and port into config
sed -i "s/___APPLICATION_IP___/$APP_PORT_80_TCP_ADDR/g" /etc/nginx/nginx.conf
sed -i "s/___APPLICATION_PORT___/$APP_PORT_80_TCP_PORT/g" /etc/nginx/nginx.conf
sed -i "s/___LETSENCRYPT_IP___/$LETSENCRYPT_PORT_80_TCP_ADDR/g" /etc/nginx/nginx.conf
sed -i "s/___LETSENCRYPT_PORT___/$LETSENCRYPT_PORT_80_TCP_PORT/g" /etc/nginx/nginx.conf
cat /etc/nginx/nginx.conf
echo .
echo Firing up nginx in the background.
nginx
# # Check user has specified domain name
if [ -z "$MY_DOMAIN_NAME" ]; then
echo "Need to set MY_DOMAIN_NAME (to a letsencrypt-registered name)."
exit 1
fi
# This bit waits until the letsencrypt container has done its thing.
# We see the changes here bceause there's a docker volume mapped.
echo Waiting for folder /etc/letsencrypt/live/$MY_DOMAIN_NAME to exist
while [ ! -d /etc/letsencrypt/live/$MY_DOMAIN_NAME ] ;
do
sleep 2
done
while [ ! -f /etc/letsencrypt/live/$MY_DOMAIN_NAME/fullchain.pem ] ;
do
echo Waiting for file fullchain.pem to exist
sleep 2
done
while [ ! -f /etc/letsencrypt/live/$MY_DOMAIN_NAME/privkey.pem ] ;
do
echo Waiting for file privkey.pem to exist
sleep 2
done
# This is added so that when the certificate is being renewed or is already in place, nginx waits for everything to be good.
sleep 15
echo replacing ___my.example.com___/$MY_DOMAIN_NAME
echo replacing ___LETSENCRYPT_IP___/$LETSENCRYPT_PORT_80_TCP_ADDR
echo replacing ___LETSENCRYPT_PORT___/$LETSENCRYPT_PORT_80_TCP_PORT
echo replacing ___LETSENCRYPT_HTTPS_IP___/$LETSENCRYPT_PORT_443_TCP_ADDR
echo replacing ___LETSENCRYPT_HTTPS_PORT___/$LETSENCRYPT_PORT_443_TCP_PORT
echo replacing ___APPLICATION_IP___/$APP_PORT_80_TCP_ADDR
echo replacing ___APPLICATION_PORT___/$APP_PORT_80_TCP_PORT
# Put your domain name into the nginx reverse proxy config.
sed -i "s/___my.example.com___/$MY_DOMAIN_NAME/g" /etc/nginx/nginx-secure.conf
# Add LE container IP and port into config
sed -i "s/___LETSENCRYPT_IP___/$LETSENCRYPT_PORT_80_TCP_ADDR/g" /etc/nginx/nginx-secure.conf
sed -i "s/___LETSENCRYPT_PORT___/$LETSENCRYPT_PORT_80_TCP_PORT/g" /etc/nginx/nginx-secure.conf
sed -i "s/___LETSENCRYPT_HTTPS_IP___/$LETSENCRYPT_PORT_443_TCP_ADDR/g" /etc/nginx/nginx-secure.conf
sed -i "s/___LETSENCRYPT_HTTPS_PORT___/$LETSENCRYPT_PORT_443_TCP_PORT/g" /etc/nginx/nginx-secure.conf
# Add your app's container IP and port into config
sed -i "s/___APPLICATION_IP___/$APP_PORT_80_TCP_ADDR/g" /etc/nginx/nginx-secure.conf
sed -i "s/___APPLICATION_PORT___/$APP_PORT_80_TCP_PORT/g" /etc/nginx/nginx-secure.conf
#go!
kill $(ps aux | grep 'nginx' | awk '{print $2}')
cp /etc/nginx/nginx-secure.conf /etc/nginx/nginx.conf
nginx -g 'daemon off;'

View File

@ -53,7 +53,7 @@ INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
# MIDDLEWARE CONFIGURATION # MIDDLEWARE CONFIGURATION
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
MIDDLEWARE_CLASSES = ( MIDDLEWARE_CLASSES = (
# Make sure djangosecure.middleware.SecurityMiddleware is listed first 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',

View File

@ -6,7 +6,7 @@ Production Configurations
- Use Amazon's S3 for storing static files and uploaded media - Use Amazon's S3 for storing static files and uploaded media
- Use mailgun to send emails - Use mailgun to send emails
- Use Redis on Heroku - Use Redis on Heroku
{% if cookiecutter.use_sentry == 'y' %} {% if cookiecutter.use_sentry_for_error_reporting == 'y' %}
- Use sentry for error logging - Use sentry for error logging
{% endif %} {% endif %}
{% if cookiecutter.use_opbeat == 'y' %} {% if cookiecutter.use_opbeat == 'y' %}
@ -17,7 +17,7 @@ from __future__ import absolute_import, unicode_literals
from boto.s3.connection import OrdinaryCallingFormat from boto.s3.connection import OrdinaryCallingFormat
from django.utils import six from django.utils import six
{% if cookiecutter.use_sentry == 'y' %} {% if cookiecutter.use_sentry_for_error_reporting == 'y' %}
import logging import logging
{% endif %} {% endif %}
@ -29,40 +29,27 @@ from .common import * # noqa
# Raises ImproperlyConfigured exception if DJANGO_SECRET_KEY not in os.environ # Raises ImproperlyConfigured exception if DJANGO_SECRET_KEY not in os.environ
SECRET_KEY = env('DJANGO_SECRET_KEY') SECRET_KEY = env('DJANGO_SECRET_KEY')
# This ensures that Django will be able to detect a secure connection # This ensures that Django will be able to detect a secure connection
# properly on Heroku. # properly on Heroku.
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# django-secure {%- if cookiecutter.use_sentry_for_error_reporting == 'y' %}
# ------------------------------------------------------------------------------
INSTALLED_APPS += ('djangosecure', )
{% if cookiecutter.use_sentry == 'y' -%}
# raven sentry client # raven sentry client
# See https://docs.getsentry.com/hosted/clients/python/integrations/django/ # See https://docs.getsentry.com/hosted/clients/python/integrations/django/
INSTALLED_APPS += ('raven.contrib.django.raven_compat', ) INSTALLED_APPS += ('raven.contrib.django.raven_compat', )
{%- endif %} {% endif %}
SECURITY_MIDDLEWARE = ( {%- if cookiecutter.use_whitenoise == 'y' %}
'djangosecure.middleware.SecurityMiddleware',
)
{% if cookiecutter.use_whitenoise == 'y' -%}
# Use Whitenoise to serve static files # Use Whitenoise to serve static files
# See: https://whitenoise.readthedocs.io/ # See: https://whitenoise.readthedocs.io/
WHITENOISE_MIDDLEWARE = ( WHITENOISE_MIDDLEWARE = ('whitenoise.middleware.WhiteNoiseMiddleware', )
'whitenoise.middleware.WhiteNoiseMiddleware',
)
MIDDLEWARE_CLASSES = WHITENOISE_MIDDLEWARE + MIDDLEWARE_CLASSES MIDDLEWARE_CLASSES = WHITENOISE_MIDDLEWARE + MIDDLEWARE_CLASSES
{%- endif %} {% endif %}
{% if cookiecutter.use_sentry == 'y' -%} {%- if cookiecutter.use_sentry_for_error_reporting == 'y' -%}
RAVEN_MIDDLEWARE = ( RAVEN_MIDDLEWARE = ('raven.contrib.django.raven_compat.middleware.SentryResponseErrorIdMiddleware', )
'raven.contrib.django.raven_compat.middleware.SentryResponseErrorIdMiddleware',
)
MIDDLEWARE_CLASSES = RAVEN_MIDDLEWARE + MIDDLEWARE_CLASSES MIDDLEWARE_CLASSES = RAVEN_MIDDLEWARE + MIDDLEWARE_CLASSES
{%- endif %} {% endif %}
{%- if cookiecutter.use_opbeat == 'y' -%}
# Make sure djangosecure.middleware.SecurityMiddleware is listed first
MIDDLEWARE_CLASSES = SECURITY_MIDDLEWARE + MIDDLEWARE_CLASSES
{% if cookiecutter.use_opbeat == 'y' -%}
# opbeat integration # opbeat integration
# See https://opbeat.com/languages/django/ # See https://opbeat.com/languages/django/
INSTALLED_APPS += ('opbeat.contrib.django',) INSTALLED_APPS += ('opbeat.contrib.django',)
@ -74,7 +61,13 @@ OPBEAT = {
MIDDLEWARE_CLASSES = ( MIDDLEWARE_CLASSES = (
'opbeat.contrib.django.middleware.OpbeatAPMMiddleware', 'opbeat.contrib.django.middleware.OpbeatAPMMiddleware',
) + MIDDLEWARE_CLASSES ) + MIDDLEWARE_CLASSES
{%- endif %} {% endif %}
# SECURITY CONFIGURATION
# ------------------------------------------------------------------------------
# See https://docs.djangoproject.com/en/1.9/ref/middleware/#module-django.middleware.security
# and https://docs.djangoproject.com/ja/1.9/howto/deployment/checklist/#run-manage-py-check-deploy
# set this to 60 seconds and then to 518400 when you can prove it works # set this to 60 seconds and then to 518400 when you can prove it works
SECURE_HSTS_SECONDS = 60 SECURE_HSTS_SECONDS = 60
SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool( SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool(
@ -82,9 +75,12 @@ SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool(
SECURE_CONTENT_TYPE_NOSNIFF = env.bool( SECURE_CONTENT_TYPE_NOSNIFF = env.bool(
'DJANGO_SECURE_CONTENT_TYPE_NOSNIFF', default=True) 'DJANGO_SECURE_CONTENT_TYPE_NOSNIFF', default=True)
SECURE_BROWSER_XSS_FILTER = True SECURE_BROWSER_XSS_FILTER = True
SESSION_COOKIE_SECURE = False SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_HTTPONLY = True
SECURE_SSL_REDIRECT = env.bool('DJANGO_SECURE_SSL_REDIRECT', default=True) SECURE_SSL_REDIRECT = env.bool('DJANGO_SECURE_SSL_REDIRECT', default=True)
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True
X_FRAME_OPTIONS = 'DENY'
# SITE CONFIGURATION # SITE CONFIGURATION
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -95,6 +91,7 @@ ALLOWED_HOSTS = env.list('DJANGO_ALLOWED_HOSTS', default=['{{cookiecutter.domain
INSTALLED_APPS += ('gunicorn', ) INSTALLED_APPS += ('gunicorn', )
# STORAGE CONFIGURATION # STORAGE CONFIGURATION
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Uploaded Media Files # Uploaded Media Files
@ -169,11 +166,6 @@ ANYMAIL = {
"MAILGUN_API_KEY": env('DJANGO_MAILGUN_API_KEY'), "MAILGUN_API_KEY": env('DJANGO_MAILGUN_API_KEY'),
} }
EMAIL_BACKEND = "anymail.backends.mailgun.MailgunBackend" EMAIL_BACKEND = "anymail.backends.mailgun.MailgunBackend"
{% if cookiecutter.use_newrelic == 'y'-%}# NEW RELIC
# ------------------------------------------------------------------------------
NEW_RELIC_LICENSE_KEY = env('NEW_RELIC_LICENSE_KEY')
NEW_RELIC_APP_NAME = env('NEW_RELIC_APP_NAME')
{%- endif %}
# TEMPLATE CONFIGURATION # TEMPLATE CONFIGURATION
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -204,7 +196,7 @@ CACHES = {
} }
} }
{% if cookiecutter.use_sentry == 'y' %} {% if cookiecutter.use_sentry_for_error_reporting == 'y' %}
# Sentry Configuration # Sentry Configuration
SENTRY_DSN = env('DJANGO_SENTRY_DSN') SENTRY_DSN = env('DJANGO_SENTRY_DSN')
SENTRY_CLIENT = env('DJANGO_SENTRY_CLIENT', default='raven.contrib.django.raven_compat.DjangoClient') SENTRY_CLIENT = env('DJANGO_SENTRY_CLIENT', default='raven.contrib.django.raven_compat.DjangoClient')
@ -260,7 +252,7 @@ RAVEN_CONFIG = {
'CELERY_LOGLEVEL': env.int('DJANGO_SENTRY_LOG_LEVEL', logging.INFO), 'CELERY_LOGLEVEL': env.int('DJANGO_SENTRY_LOG_LEVEL', logging.INFO),
'DSN': SENTRY_DSN 'DSN': SENTRY_DSN
} }
{% elif cookiecutter.use_sentry == 'n' %} {% elif cookiecutter.use_sentry_for_error_reporting == 'n' %}
# LOGGING CONFIGURATION # LOGGING CONFIGURATION
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#logging # See: https://docs.djangoproject.com/en/dev/ref/settings/#logging

View File

@ -15,13 +15,8 @@ framework.
""" """
import os import os
{% if cookiecutter.use_newrelic == 'y' -%}
if os.environ.get('DJANGO_SETTINGS_MODULE') == 'config.settings.production':
import newrelic.agent
newrelic.agent.initialize()
{%- endif %}
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
{% if cookiecutter.use_sentry == 'y' -%} {% if cookiecutter.use_sentry_for_error_reporting == 'y' -%}
if os.environ.get('DJANGO_SETTINGS_MODULE') == 'config.settings.production': if os.environ.get('DJANGO_SETTINGS_MODULE') == 'config.settings.production':
from raven.contrib.django.raven_compat.middleware.wsgi import Sentry from raven.contrib.django.raven_compat.middleware.wsgi import Sentry
{%- endif %} {%- endif %}
@ -36,14 +31,10 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
# file. This includes Django's development server, if the WSGI_APPLICATION # file. This includes Django's development server, if the WSGI_APPLICATION
# setting points here. # setting points here.
application = get_wsgi_application() application = get_wsgi_application()
{% if cookiecutter.use_sentry == 'y' -%} {% if cookiecutter.use_sentry_for_error_reporting == 'y' -%}
if os.environ.get('DJANGO_SETTINGS_MODULE') == 'config.settings.production': if os.environ.get('DJANGO_SETTINGS_MODULE') == 'config.settings.production':
application = Sentry(application) application = Sentry(application)
{%- endif %} {%- endif %}
{% if cookiecutter.use_newrelic == 'y' -%}
if os.environ.get('DJANGO_SETTINGS_MODULE') == 'config.settings.production':
application = newrelic.agent.WSGIApplicationWrapper(application)
{%- endif %}
# Apply WSGI middleware here. # Apply WSGI middleware here.
# from helloworld.wsgi import HelloWorldApplication # from helloworld.wsgi import HelloWorldApplication
# application = HelloWorldApplication(application) # application = HelloWorldApplication(application)

View File

@ -27,12 +27,38 @@ services:
build: ./compose/nginx build: ./compose/nginx
depends_on: depends_on:
- django - django
{% if cookiecutter.use_lets_encrypt == 'y' %}
- certbot
{% endif %}
ports: ports:
- "0.0.0.0:80:80" - "0.0.0.0:80:80"
{% if cookiecutter.use_lets_encrypt == 'y' %}
environment:
- MY_DOMAIN_NAME={{ cookiecutter.domain_name }}
ports:
- "0.0.0.0:80:80"
- "0.0.0.0:443:443"
volumes:
- /etc/letsencrypt:/etc/letsencrypt
- /var/lib/letsencrypt:/var/lib/letsencrypt
certbot:
image: quay.io/letsencrypt/letsencrypt
command: bash -c "sleep 6 && certbot certonly -n --standalone -d {{ cookiecutter.domain_name }} --text --agree-tos --email {{ cookiecutter.email }} --server https://acme-v01.api.letsencrypt.org/directory --rsa-key-size 4096 --verbose --keep-until-expiring --standalone-supported-challenges http-01"
entrypoint: ""
volumes:
- /etc/letsencrypt:/etc/letsencrypt
- /var/lib/letsencrypt:/var/lib/letsencrypt
ports:
- "80"
- "443"
environment:
- TERM=xterm
{% endif %}
redis: redis:
image: redis:3.0 image: redis:latest
{% if cookiecutter.use_celery == 'y' %} {% if cookiecutter.use_celery == 'y' %}
celeryworker: celeryworker:
build: build:
context: . context: .
@ -54,4 +80,4 @@ services:
- postgres - postgres
- redis - redis
command: celery -A {{cookiecutter.project_slug}}.taskapp beat -l INFO command: celery -A {{cookiecutter.project_slug}}.taskapp beat -l INFO
{% endif %} {% endif %}

View File

@ -44,7 +44,7 @@ master_doc = 'index'
# General information about the project. # General information about the project.
project = '{{ cookiecutter.project_name }}' project = '{{ cookiecutter.project_name }}'
copyright = """{{ cookiecutter.year }}, {{ cookiecutter.author_name }}""" copyright = """{% now 'utc', '%Y' %}, {{ cookiecutter.author_name }}"""
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the

View File

@ -1,24 +1,31 @@
# PostgreSQL
POSTGRES_PASSWORD=mysecretpass POSTGRES_PASSWORD=mysecretpass
POSTGRES_USER=postgresuser POSTGRES_USER=postgresuser
# General settings
DJANGO_ADMIN_URL= DJANGO_ADMIN_URL=
DJANGO_SETTINGS_MODULE=config.settings.production DJANGO_SETTINGS_MODULE=config.settings.production
DJANGO_SECRET_KEY=CHANGEME!!! DJANGO_SECRET_KEY=CHANGEME!!!
DJANGO_ALLOWED_HOSTS=.{{ cookiecutter.domain_name }} DJANGO_ALLOWED_HOSTS=.{{ cookiecutter.domain_name }}
# AWS Settings
DJANGO_AWS_ACCESS_KEY_ID= DJANGO_AWS_ACCESS_KEY_ID=
DJANGO_AWS_SECRET_ACCESS_KEY= DJANGO_AWS_SECRET_ACCESS_KEY=
DJANGO_AWS_STORAGE_BUCKET_NAME= DJANGO_AWS_STORAGE_BUCKET_NAME=
# Used with email
DJANGO_MAILGUN_API_KEY= DJANGO_MAILGUN_API_KEY=
DJANGO_SERVER_EMAIL= DJANGO_SERVER_EMAIL=
# Security! Better to use DNS for this task, but you can use redirect
DJANGO_SECURE_SSL_REDIRECT=False DJANGO_SECURE_SSL_REDIRECT=False
# django-allauth
DJANGO_ACCOUNT_ALLOW_REGISTRATION=True DJANGO_ACCOUNT_ALLOW_REGISTRATION=True
{% if cookiecutter.use_sentry == 'y' -%} {% if cookiecutter.use_sentry_for_error_reporting == 'y' -%}
# Sentry
DJANGO_SENTRY_DSN= DJANGO_SENTRY_DSN=
{% endif %} {% endif %}
{% if cookiecutter.use_newrelic == 'y' -%}
NEW_RELIC_LICENSE_KEY=
NEW_RELIC_APP_NAME={{cookiecutter.project_slug}}
{% endif %}
{% if cookiecutter.use_opbeat == 'y' -%} {% if cookiecutter.use_opbeat == 'y' -%}
DJANGO_OPBEAT_ORGANIZATION_ID DJANGO_OPBEAT_ORGANIZATION_ID
DJANGO_OPBEAT_APP_ID DJANGO_OPBEAT_APP_ID

View File

@ -0,0 +1,107 @@
////////////////////////////////
//Setup//
////////////////////////////////
// Plugins
var gulp = require('gulp'),
pjson = require('./package.json'),
gutil = require('gulp-util'),
sass = require('gulp-sass'),
autoprefixer = require('gulp-autoprefixer'),
cssnano = require('gulp-cssnano'),
rename = require('gulp-rename'),
del = require('del'),
plumber = require('gulp-plumber'),
pixrem = require('gulp-pixrem'),
uglify = require('gulp-uglify'),
imagemin = require('gulp-imagemin'),
exec = require('gulp-exec'),
runSequence = require('run-sequence'),
browserSync = require('browser-sync');
// Relative paths function
var pathsConfig = function (appName) {
this.app = "./" + (appName || pjson.name);
return {
app: this.app,
templates: this.app + '/templates',
css: this.app + '/static/css',
sass: this.app + '/static/sass',
fonts: this.app + '/static/fonts',
images: this.app + '/static/images',
js: this.app + '/static/js',
}
};
var paths = pathsConfig();
////////////////////////////////
//Tasks//
////////////////////////////////
// Styles autoprefixing and minification
gulp.task('styles', function() {
return gulp.src(paths.sass + '/project.scss')
.pipe(sass().on('error', sass.logError))
.pipe(plumber()) // Checks for errors
.pipe(autoprefixer({browsers: ['last 2 version']})) // Adds vendor prefixes
.pipe(pixrem()) // add fallbacks for rem units
.pipe(gulp.dest(paths.css))
.pipe(rename({ suffix: '.min' }))
.pipe(cssnano()) // Minifies the result
.pipe(gulp.dest(paths.css));
});
// Javascript minification
gulp.task('scripts', function() {
return gulp.src(paths.js + '/project.js')
.pipe(plumber()) // Checks for errors
.pipe(uglify()) // Minifies the js
.pipe(rename({ suffix: '.min' }))
.pipe(gulp.dest(paths.js));
});
// Image compression
gulp.task('imgCompression', function(){
return gulp.src(paths.images + '/*')
.pipe(imagemin()) // Compresses PNG, JPEG, GIF and SVG images
.pipe(gulp.dest(paths.images))
});
// Run django server
gulp.task('runServer', function() {
exec('python manage.py runserver', function (err, stdout, stderr) {
console.log(stdout);
console.log(stderr);
});
});
// Browser sync server for live reload
gulp.task('browserSync', function() {
browserSync.init(
[paths.css + "/*.css", paths.js + "*.js", paths.templates + '*.html'], {
proxy: "localhost:8000"
});
});
// Default task
gulp.task('default', function() {
runSequence(['styles', 'scripts', 'imgCompression'], 'runServer', 'browserSync');
});
////////////////////////////////
//Watch//
////////////////////////////////
// Watch
gulp.task('watch', ['default'], function() {
gulp.watch(paths.sass + '/*.scss', ['styles']);
gulp.watch(paths.js + '/*.js', ['scripts']);
gulp.watch(paths.images + '/*', ['imgCompression']);
gulp.watch('templates/*.html');
});

View File

@ -3,17 +3,34 @@
"version": "{{ cookiecutter.version }}", "version": "{{ cookiecutter.version }}",
"dependencies": {}, "dependencies": {},
"devDependencies": { "devDependencies": {
"grunt": "~0.4.5", {% if cookiecutter.js_task_runner == 'Grunt' %}
"grunt-contrib-watch": "~0.6.1",
"grunt-bg-shell": "~2.3.1",
"connect-livereload": "~0.3.2",
"time-grunt": "~1.2.1",
"load-grunt-tasks": "~3.2.0",
"grunt-sass": "~1.0.0",
"grunt-postcss": "~0.5.5",
"cssnano": "~2.1.0",
"autoprefixer-core": "~5.2.1", "autoprefixer-core": "~5.2.1",
"pixrem": "~1.3.1" "connect-livereload": "~0.3.2",
"cssnano": "~2.1.0",
"grunt": "~0.4.5",
"grunt-bg-shell": "~2.3.1",
"grunt-contrib-watch": "~0.6.1",
"grunt-postcss": "~0.5.5",
"grunt-sass": "~1.0.0",
"load-grunt-tasks": "~3.2.0",
"pixrem": "~1.3.1",
"time-grunt": "~1.2.1"
{% elif cookiecutter.js_task_runner == 'Gulp' %}
"browser-sync": "^2.12.10",
"del": "^2.2.0",
"gulp": "^3.9.1",
"gulp-autoprefixer": "^3.1.0",
"gulp-cssnano": "^2.1.2",
"gulp-exec": "^2.1.2",
"gulp-imagemin": "^3.0.1",
"gulp-pixrem": "^1.0.0",
"gulp-plumber": "^1.1.0",
"gulp-rename": "^1.2.2",
"gulp-sass": "^2.3.1",
"gulp-uglify": "^1.5.3",
"gulp-util": "^3.0.7",
"run-sequence": "^1.2.1"
{% endif %}
}, },
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"

View File

@ -7,18 +7,17 @@ wheel==0.29.0
{%- endif %} {%- endif %}
# Bleeding edge Django # Bleeding edge Django
django==1.9.6 django==1.9.7
# Configuration # Configuration
django-environ==0.4.0 django-environ==0.4.0
django-secure==1.0.1
{% if cookiecutter.use_whitenoise == 'y' -%} {% if cookiecutter.use_whitenoise == 'y' -%}
whitenoise==3.0 whitenoise==3.2
{%- endif %} {%- endif %}
# Forms # Forms
django-braces==1.8.1 django-braces==1.9.0
django-crispy-forms==1.6.0 django-crispy-forms==1.6.0
django-floppyforms==1.6.2 django-floppyforms==1.6.2
@ -48,7 +47,7 @@ django-autoslug==1.9.3
pytz==2016.4 pytz==2016.4
# Redis support # Redis support
django-redis==4.4.2 django-redis==4.4.3
redis>=2.10.0 redis>=2.10.0
{% if cookiecutter.use_celery == "y" %} {% if cookiecutter.use_celery == "y" %}

View File

@ -1,16 +1,16 @@
# Local development dependencies go here # Local development dependencies go here
-r base.txt -r base.txt
coverage==4.0.3 coverage==4.1
django_coverage_plugin==1.3 django-coverage-plugin==1.3.1
Sphinx Sphinx==1.4.4
django-extensions==1.6.7 django-extensions==1.6.7
Werkzeug==0.11.9 Werkzeug==0.11.10
django-test-plus==1.0.12 django-test-plus==1.0.13
factory_boy==2.7.0 factory_boy==2.7.0
django-debug-toolbar==1.4 django-debug-toolbar==1.4
# improved REPL # improved REPL
ipdb==0.10.0 ipdb==0.10.1
pytest-django==2.9.1 pytest-django==2.9.1
pytest-sugar==0.7.1 pytest-sugar==0.7.1

View File

@ -12,7 +12,7 @@ psycopg2==2.6.1
# WSGI Handler # WSGI Handler
# ------------------------------------------------ # ------------------------------------------------
gevent==1.1.1 gevent==1.1.1
gunicorn==19.5.0 gunicorn==19.6.0
# Static and Media Storage # Static and Media Storage
# ------------------------------------------------ # ------------------------------------------------
@ -26,20 +26,14 @@ Collectfast==0.2.3
# ------------------------------------------------------- # -------------------------------------------------------
django-anymail==0.3.1 django-anymail==0.3.1
{% if cookiecutter.use_sentry == "y" -%} {% if cookiecutter.use_sentry_for_error_reporting == "y" -%}
# Raven is the Sentry client # Raven is the Sentry client
# -------------------------- # --------------------------
raven raven==5.20.0
{%- endif %}
{% if cookiecutter.use_newrelic == "y" -%}
# Newrelic agent for performance monitoring
# -----------------------------------------
newrelic
{%- endif %} {%- endif %}
{% if cookiecutter.use_opbeat == "y" -%} {% if cookiecutter.use_opbeat == "y" -%}
# Opbeat agent for performance monitoring # Opbeat agent for performance monitoring
# ----------------------------------------- # -----------------------------------------
opbeat opbeat==3.3.3
{%- endif %} {%- endif %}

View File

@ -7,10 +7,10 @@
psycopg2==2.6.1 psycopg2==2.6.1
{%- endif %} {%- endif %}
coverage==4.0.3 coverage==4.1
django_coverage_plugin==1.3 django-coverage-plugin==1.3.1
flake8==2.5.4 flake8==2.6.0
django-test-plus==1.0.12 django-test-plus==1.0.13
factory_boy==2.7.0 factory_boy==2.7.0
# pytest # pytest

View File

@ -1,14 +1,17 @@
#!/bin/bash #!/bin/bash
WORK_DIR="$(dirname "$0")" WORK_DIR="$(dirname "$0")"
OS_REQUIREMENTS_FILENAME="$WORK_DIR/requirements.apt" DISTRO_NAME=$(lsb_release -sc)
VER=$(lsb_release -sr) OS_REQUIREMENTS_FILENAME="$WORK_DIR/requirements-$DISTRO_NAME.apt"
if [ "$VER" == "16.04" ]; then
OS_REQUIREMENTS_FILENAME="requirements.apt.xenial"
else if [ "$DISTRO_NAME" != "xenial" ] && [ "$DISTRO_NAME" != "trusty" ]; then
OS_REQUIREMENTS_FILENAME="requirements.apt" echo "Only the Ubuntu 14.04 (Trusty) and 16.04 (Xenial) is supported by this script";
echo "You can see requirements-trusty.apt or requirements-xenial.apt file to help search the equivalent package in your system";
exit 1;
fi fi
# Handle call with wrong command # Handle call with wrong command
function wrong_command() function wrong_command()
{ {
@ -31,15 +34,15 @@ function usage_message()
# Read the requirements.apt file, and remove comments and blank lines # Read the requirements.apt file, and remove comments and blank lines
function list_packages(){ function list_packages(){
grep -v "#" ${OS_REQUIREMENTS_FILENAME} | grep -v "^$"; grep -v "#" "${OS_REQUIREMENTS_FILENAME}" | grep -v "^$";
} }
function install() function install_packages()
{ {
list_packages | xargs apt-get --no-upgrade install -y; list_packages | xargs apt-get --no-upgrade install -y;
} }
function upgrade() function upgrade_packages()
{ {
list_packages | xargs apt-get install -y; list_packages | xargs apt-get install -y;
} }
@ -53,7 +56,7 @@ function install_or_upgrade()
if [[ $EUID -ne 0 ]]; then if [[ $EUID -ne 0 ]]; then
echo -e "\nYou must run this with root privilege" 2>&1 echo -e "\nYou must run this with root privilege" 2>&1
echo -e "Please do:\n" 2>&1 echo -e "Please do:\n" 2>&1
echo "sudo ./${0##*/} $PARAN" 2>&1 echo "sudo ./$WORK_DIR/${0##*/} $PARAN" 2>&1
echo -e "\n" 2>&1 echo -e "\n" 2>&1
exit 1 exit 1
@ -63,9 +66,9 @@ function install_or_upgrade()
# Install the basic compilation dependencies and other required libraries of this project # Install the basic compilation dependencies and other required libraries of this project
if [ "$PARAN" == "install" ]; then if [ "$PARAN" == "install" ]; then
install; install_packages;
else else
upgrade; upgrade_packages;
fi fi
# cleaning downloaded packages from apt-get cache # cleaning downloaded packages from apt-get cache
@ -84,5 +87,5 @@ case "$1" in
upgrade) install_or_upgrade "upgrade";; upgrade) install_or_upgrade "upgrade";;
list) list_packages;; list) list_packages;;
help) usage_message;; help) usage_message;;
*) wrong_command $1;; *) wrong_command "$1";;
esac esac

View File

@ -1,4 +1,4 @@
##basic build dependencies of various Django apps for Ubuntu 14.04 ##basic build dependencies of various Django apps for Ubuntu Trusty 14.04
#build-essential metapackage install: make, gcc, g++, #build-essential metapackage install: make, gcc, g++,
build-essential build-essential
#required to translate #required to translate
@ -25,3 +25,4 @@ libwebp-dev
##django-extensions ##django-extensions
graphviz-dev graphviz-dev

View File

@ -0,0 +1,28 @@
##basic build dependencies of various Django apps for Ubuntu Xenial 16.04
#build-essential metapackage install: make, gcc, g++,
build-essential
#required to translate
gettext
{% if cookiecutter.use_python2 == 'n' -%}
python3-dev
{% else %}
python-dev
{%- endif %}
##shared dependencies of:
##Pillow, pylibmc
zlib1g-dev
##Postgresql and psycopg2 dependencies
libpq-dev
##Pillow dependencies
libtiff5-dev
libjpeg8-dev
libfreetype6-dev
liblcms2-dev
libwebp-dev
##django-extensions
graphviz-dev

View File

@ -1,44 +0,0 @@
##basic build dependencies of various Django apps for Ubuntu 14.04
#build-essential metapackage install: make, gcc, g++,
build-essential
#required to translate
gettext
python-dev
##shared dependencies of:
##Pillow, pylibmc
zlib1g-dev
##Postgresql and psycopg2 dependencies
libpq-dev
##Pillow dependencies
libtiff5-dev
libjpeg8-dev
libfreetype6-dev
liblcms2-dev
libwebp-dev
##django-extensions
graphviz-dev
##hitch
python-setuptools
python3-dev
python-virtualenv
python-pip
firefox
automake
libtool
libreadline6
libreadline6-dev
libreadline-dev
libsqlite3-dev
libxml2
libxml2-dev
libssl-dev
libbz2-dev
wget
curl
llvm

View File

@ -1,5 +1,10 @@
// project specific CSS goes here // project specific CSS goes here
////////////////////////////////
//Variables//
////////////////////////////////
// Alert colors // Alert colors
$white: #fff; $white: #fff;
@ -9,6 +14,10 @@ $pink: #f2dede;
$dark-pink: #eed3d7; $dark-pink: #eed3d7;
$red: #b94a48; $red: #b94a48;
////////////////////////////////
//Alerts//
////////////////////////////////
// bootstrap alert CSS, translated to the django-standard levels of // bootstrap alert CSS, translated to the django-standard levels of
// debug, info, success, warning, error // debug, info, success, warning, error
@ -24,8 +33,16 @@ $red: #b94a48;
color: $red; color: $red;
} }
////////////////////////////////
//Navbar//
////////////////////////////////
// This is a fix for the bootstrap4 alpha release // This is a fix for the bootstrap4 alpha release
.navbar {
border-radius: 0px;
}
@media (max-width: 47.9em) { @media (max-width: 47.9em) {
.navbar-nav .nav-item { .navbar-nav .nav-item {
display: inline-block; display: inline-block;
@ -42,6 +59,10 @@ $red: #b94a48;
} }
} }
////////////////////////////////
//Django Toolbar//
////////////////////////////////
// Display django-debug-toolbar. // Display django-debug-toolbar.
// See https://github.com/django-debug-toolbar/django-debug-toolbar/issues/742 // See https://github.com/django-debug-toolbar/django-debug-toolbar/issues/742
// and https://github.com/pydanny/cookiecutter-django/issues/317 // and https://github.com/pydanny/cookiecutter-django/issues/317

View File

@ -24,7 +24,7 @@ class CeleryConfig(AppConfig):
app.config_from_object('django.conf:settings') app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS, force=True) app.autodiscover_tasks(lambda: settings.INSTALLED_APPS, force=True)
{% if cookiecutter.use_sentry == 'y' -%} {% if cookiecutter.use_sentry_for_error_reporting == 'y' -%}
if hasattr(settings, 'RAVEN_CONFIG'): if hasattr(settings, 'RAVEN_CONFIG'):
# Celery signal registration # Celery signal registration
from raven import Client as RavenClient from raven import Client as RavenClient

View File

@ -15,7 +15,7 @@
{% block css %} {% block css %}
<!-- Latest compiled and minified CSS --> <!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://cdn.rawgit.com/twbs/bootstrap/v4-dev/dist/css/bootstrap.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.2/css/bootstrap.min.css" integrity="sha384-y3tfxAZXuh4HwSYylfB+J125MxIs6mR5FOHamPBG064zB+AFeWH94NdvaCBm8qnd" crossorigin="anonymous">
<!-- Your stuff: Third-party css libraries go here --> <!-- Your stuff: Third-party css libraries go here -->
{% endraw %}{% if cookiecutter.use_compressor == "y" %}{% raw %}{% compress css %}{% endraw %}{% endif %}{% raw %} {% endraw %}{% if cookiecutter.use_compressor == "y" %}{% raw %}{% compress css %}{% endraw %}{% endif %}{% raw %}
@ -96,7 +96,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.2.0/js/tether.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.2.0/js/tether.min.js"></script>
<!-- Latest compiled and minified JavaScript --> <!-- Latest compiled and minified JavaScript -->
<script src="https://cdn.rawgit.com/twbs/bootstrap/v4-dev/dist/js/bootstrap.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.2/js/bootstrap.min.js" integrity="sha384-vZ2WRJMwsjRMW/8U7i6PWi6AlO1L79snBrmgiDpgIWJ82z8eA5lenwvxbMV1PAh7" crossorigin="anonymous"></script>
<!-- Your stuff: Third-party javascript libraries go here --> <!-- Your stuff: Third-party javascript libraries go here -->

View File

@ -5,7 +5,6 @@ from django import forms
from django.contrib import admin from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as AuthUserAdmin from django.contrib.auth.admin import UserAdmin as AuthUserAdmin
from django.contrib.auth.forms import UserChangeForm, UserCreationForm from django.contrib.auth.forms import UserChangeForm, UserCreationForm
from .models import User from .models import User
@ -24,7 +23,7 @@ class MyUserCreationForm(UserCreationForm):
model = User model = User
def clean_username(self): def clean_username(self):
username = self.cleaned_data['username'] username = self.cleaned_data["username"]
try: try:
User.objects.get(username=username) User.objects.get(username=username)
except User.DoesNotExist: except User.DoesNotExist:
@ -33,6 +32,11 @@ class MyUserCreationForm(UserCreationForm):
@admin.register(User) @admin.register(User)
class UserAdmin(AuthUserAdmin): class MyUserAdmin(AuthUserAdmin):
form = MyUserChangeForm form = MyUserChangeForm
add_form = MyUserCreationForm add_form = MyUserCreationForm
fieldsets = (
('User Profile', {'fields': ('name',)}),
) + AuthUserAdmin.fieldsets
list_display = ('username', 'name', 'is_superuser')
search_fields = ['name']