diff --git a/.gitignore b/.gitignore index 1f80de173..9e4496f1c 100644 --- a/.gitignore +++ b/.gitignore @@ -226,3 +226,5 @@ pip-selfcheck.json # to 'run' anything within it since any particular cookiecutter # is declarative by nature. .idea/ + +.pytest_cache/ diff --git a/.travis.yml b/.travis.yml index f6e18b521..8be8bc440 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,15 +13,6 @@ env: - TOX_ENV=py36 before_install: - - sudo sh -c 'echo "deb https://apt.dockerproject.org/repo ubuntu-precise main" > /etc/apt/sources.list.d/docker.list' - - sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D - - sudo apt-get update - - sudo apt-key update - - sudo apt-get --force-yes -qqy -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install docker-engine=1.11.1-0~precise - - sudo rm /usr/local/bin/docker-compose - - curl -L https://github.com/docker/compose/releases/download/1.7.0/docker-compose-`uname -s`-`uname -m` > docker-compose - - chmod +x docker-compose - - sudo mv docker-compose /usr/local/bin - docker-compose -v - docker -v diff --git a/CHANGELOG.md b/CHANGELOG.md index 24f8d11cc..304732b9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All enhancements and patches to Cookiecutter Django will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [2018-02-16] +### Changed +- Upgraded to Django 2.0 (@epicwhale) + ## [2018-01-15] ### Changed - Removed Elastic Beanstalk support (@pydanny) diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index a9a857733..e1c3cad4e 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -44,6 +44,7 @@ Listed in alphabetical order. Aaron Eikenberry `@aeikenberry`_ Adam BogdaƂ `@bogdal`_ Adam Dobrawy `@ad-m`_ + Adam Steele `@adammsteele` Agam Dua Alberto Sanchez `@alb3rto`_ Alex Tsai `@caffodian`_ @@ -88,6 +89,7 @@ Listed in alphabetical order. Dong Huynh `@trungdong`_ Emanuel Calso `@bloodpet`_ @bloodpet Eraldo Energy `@eraldo`_ + Eric Groom `@ericgroom`_ Eyad Al Sibai `@eyadsibai`_ Felipe Arruda `@arruda`_ Garry Cairns `@garry-cairns`_ @@ -159,6 +161,7 @@ Listed in alphabetical order. .. _@a7p: https://github.com/a7p .. _@ad-m: https://github.com/ad-m +.. _@adammsteele: https://github.com/adammsteele .. _@aeikenberry: https://github.com/aeikenberry .. _@alb3rto: https://github.com/alb3rto .. _@ameistad: https://github.com/ameistad diff --git a/README.rst b/README.rst index b1dbe10d7..c5f9d06b3 100644 --- a/README.rst +++ b/README.rst @@ -41,7 +41,7 @@ Features * For Django 2.0 * Works with Python 3.6 * Renders Django projects with 100% starting test coverage -* Twitter Bootstrap_ v4.0.0 - beta 1 (`maintained Foundation fork`_ also available) +* Twitter Bootstrap_ v4.0.0 (`maintained Foundation fork`_ also available) * 12-Factor_ based settings via django-environ_ * Secure by default. We believe in SSL. * Optimized development and production settings @@ -111,7 +111,7 @@ Two Scoops of Django 1.11 :name: Two Scoops of Django 1.11 Cover :align: center :alt: Two Scoops of Django - :target: http://twoscoopspress.org/products/two-scoops-of-django-1-11 + :target: http://twoscoopspress.com/products/two-scoops-of-django-1-11 Two Scoops of Django is the best dessert-themed Django reference in the universe @@ -171,12 +171,13 @@ Answer the prompts with your own desired options_. For example:: use_heroku [n]: y use_compressor [n]: y Select postgresql_version: - 1 - 10 - 2 - 9.6 - 3 - 9.5 - 4 - 9.4 - 5 - 9.3 - 6 - 9.2 + 1 - 10.3 + 2 - 10.2 + 3 - 10.1 + 4 - 9.6 + 5 - 9.5 + 6 - 9.4 + 7 - 9.3 Choose from 1, 2, 3, 4 [1]: 1 Select js_task_runner: 1 - Gulp diff --git a/cookiecutter.json b/cookiecutter.json index 933c3dfa0..27834a517 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -1,24 +1,43 @@ { - "project_name": "Project Name", - "project_slug": "{{ cookiecutter.project_name.lower()|replace(' ', '_')|replace('-', '_') }}", - "author_name": "Daniel Roy Greenfeld", - "email": "you@example.com", - "description": "A short description of the project.", - "domain_name": "example.com", - "version": "0.1.0", - "timezone": "UTC", - "use_whitenoise": "y", - "use_celery": "n", - "use_mailhog": "n", - "use_sentry_for_error_reporting": "y", - "use_opbeat": "n", - "use_pycharm": "n", - "windows": "n", - "use_docker": "n", - "use_heroku": "n", - "use_compressor": "n", - "postgresql_version": ["10", "9.6", "9.5", "9.4", "9.3", "9.2"], - "js_task_runner": ["Gulp", "Grunt", "None"], - "custom_bootstrap_compilation": "n", - "open_source_license": ["MIT", "BSD", "GPLv3", "Apache Software License 2.0", "Not open source"] + "project_name": "My Awesome Project", + "project_slug": "{{ cookiecutter.project_name.lower()|replace(' ', '_')|replace('-', '_') }}", + "description": "Behold My Awesome Project!", + "author_name": "Daniel Roy Greenfeld", + "email": "{{ cookiecutter.author_name.lower()|replace(' ', '-') }}@example.com", + "domain_name": "example.com", + "version": "0.1.0", + "open_source_license": [ + "MIT", + "BSD", + "GPLv3", + "Apache Software License 2.0", + "Not open source" + ], + "timezone": "UTC", + "windows": "n", + "use_pycharm": "n", + "use_docker": "n", + "postgresql_version": [ + "10.3", + "10.2", + "10.1", + "9.6", + "9.5", + "9.4", + "9.3" + ], + "js_task_runner": [ + "None", + "Gulp", + "Grunt" + ], + "custom_bootstrap_compilation": "n", + "use_compressor": "n", + "use_celery": "n", + "use_mailhog": "n", + "use_sentry_for_error_reporting": "y", + "use_opbeat": "n", + "use_whitenoise": "y", + "use_heroku": "n", + "use_travisci": "n" } diff --git a/docs/deployment-on-heroku.rst b/docs/deployment-on-heroku.rst index b84edfa46..e770219b8 100644 --- a/docs/deployment-on-heroku.rst +++ b/docs/deployment-on-heroku.rst @@ -16,7 +16,9 @@ Run these commands to deploy the project to Heroku: heroku addons:create heroku-redis:hobby-dev heroku addons:create mailgun - heroku config:set DJANGO_ADMIN_URL="$(openssl rand -base64 32)" + heroku config:set WEB_CONCURRENCY=4 + # Generating a 32 character-long random string without any of the visually similiar characters "IOl01": + heroku config:set DJANGO_ADMIN_URL="^$(openssl rand -base64 4096 | tr -dc 'A-HJ-NP-Za-km-z2-9' | head -c 32)/" heroku config:set DJANGO_SECRET_KEY="$(openssl rand -base64 64)" heroku config:set DJANGO_SETTINGS_MODULE='config.settings.production' heroku config:set DJANGO_ALLOWED_HOSTS='.herokuapp.com' @@ -25,12 +27,10 @@ Run these commands to deploy the project to Heroku: heroku config:set DJANGO_AWS_SECRET_ACCESS_KEY=YOUR_AWS_SECRET_ACCESS_KEY_HERE heroku config:set DJANGO_AWS_STORAGE_BUCKET_NAME=YOUR_AWS_S3_BUCKET_NAME_HERE - heroku config:set DJANGO_MAILGUN_SERVER_NAME=YOUR_MALGUN_SERVER - heroku config:set DJANGO_MAILGUN_API_KEY=YOUR_MAILGUN_API_KEY - heroku config:set MAILGUN_SENDER_DOMAIN=YOUR_MAILGUN_SENDER_DOMAIN + # This is to be set only if you're using Sentry: + heroku config:set DJANGO_SENTRY_DSN=YOUR_SENTRY_DSN heroku config:set PYTHONHASHSEED=random - heroku config:set DJANGO_ADMIN_URL=\^somelocation/ git push heroku master heroku run python manage.py migrate diff --git a/docs/deployment-on-pythonanywhere.rst b/docs/deployment-on-pythonanywhere.rst index d70684bd9..995da7b03 100644 --- a/docs/deployment-on-pythonanywhere.rst +++ b/docs/deployment-on-pythonanywhere.rst @@ -63,13 +63,13 @@ Add these exports .. code-block:: bash + export WEB_CONCURRENCY=4 export DJANGO_SETTINGS_MODULE='config.settings.production' export DJANGO_SECRET_KEY='' export DJANGO_ALLOWED_HOSTS='' export DJANGO_ADMIN_URL='' - export DJANGO_MAILGUN_API_KEY='' - export DJANGO_MAILGUN_SERVER_NAME='' - export MAILGUN_SENDER_DOMAIN='' + export MAILGUN_API_KEY='' + export MAILGUN_DOMAIN='' export DJANGO_AWS_ACCESS_KEY_ID= export DJANGO_AWS_SECRET_ACCESS_KEY= export DJANGO_AWS_STORAGE_BUCKET_NAME= @@ -138,9 +138,8 @@ Click through to the **WSGI configuration file** link (near the top) and edit th os.environ['DJANGO_SECRET_KEY'] = '' os.environ['DJANGO_ALLOWED_HOSTS'] = '' os.environ['DJANGO_ADMIN_URL'] = '' - os.environ['DJANGO_MAILGUN_API_KEY'] = '' - os.environ['DJANGO_MAILGUN_SERVER_NAME'] = '' - os.environ['MAILGUN_SENDER_DOMAIN'] = '' + os.environ['MAILGUN_API_KEY'] = '' + os.environ['MAILGUN_DOMAIN'] = '' os.environ['DJANGO_AWS_ACCESS_KEY_ID'] = '' os.environ['DJANGO_AWS_SECRET_ACCESS_KEY'] = '' os.environ['DJANGO_AWS_STORAGE_BUCKET_NAME'] = '' diff --git a/docs/deployment-with-docker.rst b/docs/deployment-with-docker.rst index fb383c3c5..25ff7cb9f 100644 --- a/docs/deployment-with-docker.rst +++ b/docs/deployment-with-docker.rst @@ -42,6 +42,13 @@ You will probably also need to setup the Mail backend, for example by adding a ` .. _sentry.io: https://sentry.io/welcome .. _Mailgun: https://mailgun.com +Optional: Use AWS IAM Role for EC2 instance +------------------------------------ +If you are deploying to AWS, you can use the IAM role to substitute AWS credentials, after which it's safe to remove the `AWS_ACCESS_KEY_ID` AND `AWS_SECRET_ACCESS_KEY` from the `.env`. To do it, create an `IAM role`_ and `attach`_ it to the existing EC2 instance or create a new EC2 instance with that role. The role should assume a minimum permission of `AmazonS3FullAccess`. + +.. _IAM role: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html +.. _attach: https://aws.amazon.com/blogs/security/easily-replace-or-attach-an-iam-role-to-an-existing-ec2-instance-by-using-the-ec2-console/ + HTTPS is on by default ---------------------- diff --git a/docs/live-reloading-and-sass-compilation.rst b/docs/live-reloading-and-sass-compilation.rst index 7f978ede5..a456b81af 100644 --- a/docs/live-reloading-and-sass-compilation.rst +++ b/docs/live-reloading-and-sass-compilation.rst @@ -15,7 +15,7 @@ If you don't already have it, install `compass` (doesn't hurt if you run this co Now you just need:: - $ grunt serve + $ npm run dev The base app will now run as it would with the usual ``manage.py runserver`` but with live reloading and Sass compilation enabled. diff --git a/docs/project-generation-options.rst b/docs/project-generation-options.rst index d1d36324a..3f7928c50 100644 --- a/docs/project-generation-options.rst +++ b/docs/project-generation-options.rst @@ -1,69 +1,61 @@ Project Generation Options ========================== -project_name [project_name]: - Your human-readable project name, including any capitalization or spaces. +project_name [My Awesome Project]: + Your project's human-readable name, capitals and spaces allowed. -project_slug [project_name]: - The slug of your project, without dashes or spaces. Used to name your repo +project_slug [my_awesome_project]: + Your project's slug without dashes or spaces. Used to name your repo and in other places where a Python-importable version of your project name is needed. -author_name [Your Name]: - You! This goes into places like the LICENSE file. +description [Behold My Awesome Project!] + Describes your project and gets used in places like `README.rst` and such. -email [Your email]: - Your email address. +author_name [Daniel Roy Greenfeld]: + This is you! The value goes into places like `LICENSE` and such. -description [A short description of the project.] - Used in the generated README.rst and other places. +email [daniel-roy-greenfeld@example.com]: + The email address you want to identify yourself in the project. domain_name [example.com] - Whatever domain name you plan to use for your project when it goes live. + The domain name you plan to use for your project once it goes live. + Note that it can be safely changed later on whenever you need to. version [0.1.0] - The starting version number for your project. + The version of the project at its inception. + +open_source_license [1] + A software license for the project. The choices are: + + 1. MIT_ + 2. BSD_ + 3. GPLv3_ + 4. `Apache Software License 2.0`_ + 5. Not open source timezone [UTC] - Used in the base settings file for the `TIME_ZONE` value. - -use_whitenoise [y] - Whether to use WhiteNoise_ for static file serving. - -use_celery [n] - Whether to use Celery_. This gives you the ability to use distributed task - queues in your project. - -use_mailhog [n] - Whether to use MailHog_. MailHog is a tool that simulates email receiving - 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. - -use_sentry_for_error_reporting [n] - Whether to use Sentry_ to log errors from your project. - -use_opbeat [n] - Whether to use Opbeat_ for preformance monitoring and code optimization. - -use_pycharm [n] - Adds support for developing in PyCharm_ with a preconfigured .idea directory. + The value to be used for the `TIME_ZONE` setting of the project. windows [n] - Whether you'll be developing on Windows. + Indicates whether the project should be configured for development on Windows. + +use_pycharm [n] + Indicates whether the project should be configured for development with PyCharm_. use_docker [y] - Whether to use Docker_, separating the app and database into separate - containers. + Indicates whether the project should be configured to use Docker_ and `Docker Compose`_. -use_heroku [n] - Add configuration to deploy the application to a Heroku_ instance. +postgresql_version [1] + Select a PostgreSQL_ version to use. The choices are: -use_compressor [n] - Use `Django Compressor`_ to minify and combine rendered JavaScript and CSS - into cachable static resources. + 1. 10.3 + 2. 10.2 + 3. 10.1 + 4. 9.6 + 5. 9.5 + 6. 9.4 + 7. 9.3 js_task_runner [1] Select a JavaScript task runner. The choices are: @@ -73,36 +65,63 @@ js_task_runner [1] 3. None custom_bootstrap_compilation [n] - Scaffold out recompiling Bootstrap as as task, with Gulp_ or Grunt_. - Useful for letting you change Bootstrap variables in real time. - Consult project README for more details. + Indicates whether the project should support Bootstrap recompilation + via the selected JavaScript task runner's task. This can be useful + for real-time Bootstrap variable alteration. -open_source_license [1] - Select a software license for the project. The choices are: +use_compressor [n] + Indicates whether the project should be configured to use `Django Compressor`_. - 1. MIT_ - 2. BSD_ - 3. GPLv3_ - 4. `Apache Software License 2.0`_ - 5. Not open source +use_celery [n] + Indicates whether the project should be configured to use Celery_. + +use_mailhog [n] + Indicates whether the project should be configured to use MailHog_. + +use_sentry_for_error_reporting [n] + Indicates whether the project should be configured to use Sentry_. + +use_opbeat [n] + Indicates whether the project should be configured to use Opbeat_. + +use_whitenoise [y] + Indicates whether the project should be configured to use WhiteNoise_. + +use_heroku [n] + Indicates whether the project should be configured so as to be deployable + to Heroku_. + +use_travisci [n] + Indicates whether the project should be configured to use `Travis CI`_. -**NOTE:** *If you choose to use Docker, selecting a JavaScript task runner is -not supported out of the box.* -.. _WhiteNoise: https://github.com/evansd/whitenoise -.. _Celery: https://github.com/celery/celery -.. _MailHog: https://github.com/mailhog/MailHog -.. _Sentry: https://github.com/getsentry/sentry -.. _Opbeat: https://github.com/opbeat/opbeat_python -.. _PyCharm: https://www.jetbrains.com/pycharm/ -.. _Docker: https://github.com/docker/docker -.. _Heroku: https://github.com/heroku/heroku-buildpack-python -.. _Django Compressor: https://github.com/django-compressor/django-compressor -.. _Gulp: https://github.com/gulpjs/gulp -.. _Grunt: https://github.com/gruntjs/grunt -.. _Webpack: https://github.com/webpack/webpack -.. _Let's Encrypt: https://github.com/certbot/certbot .. _MIT: https://opensource.org/licenses/MIT .. _BSD: https://opensource.org/licenses/BSD-3-Clause .. _GPLv3: https://www.gnu.org/licenses/gpl.html .. _Apache Software License 2.0: http://www.apache.org/licenses/LICENSE-2.0 + +.. _PyCharm: https://www.jetbrains.com/pycharm/ + +.. _Docker: https://github.com/docker/docker +.. _Docker Compose: https://docs.docker.com/compose/ + +.. _PostgreSQL: https://www.postgresql.org/docs/ + +.. _Gulp: https://github.com/gulpjs/gulp +.. _Grunt: https://github.com/gruntjs/grunt + +.. _Django Compressor: https://github.com/django-compressor/django-compressor + +.. _Celery: https://github.com/celery/celery + +.. _MailHog: https://github.com/mailhog/MailHog + +.. _Sentry: https://github.com/getsentry/sentry + +.. _Opbeat: https://github.com/opbeat/opbeat_python + +.. _WhiteNoise: https://github.com/evansd/whitenoise + +.. _Heroku: https://github.com/heroku/heroku-buildpack-python + +.. _Travis CI: https://travis-ci.org/ diff --git a/docs/settings.rst b/docs/settings.rst index 20bf9e5e1..828b5de4b 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -47,9 +47,8 @@ DJANGO_AWS_STORAGE_BUCKET_NAME AWS_STORAGE_BUCKET_NAME n/a DJANGO_SENTRY_DSN SENTRY_DSN n/a raises error DJANGO_SENTRY_CLIENT SENTRY_CLIENT n/a raven.contrib.django.raven_compat.DjangoClient DJANGO_SENTRY_LOG_LEVEL SENTRY_LOG_LEVEL n/a logging.INFO -DJANGO_MAILGUN_API_KEY MAILGUN_ACCESS_KEY n/a raises error -DJANGO_MAILGUN_SERVER_NAME MAILGUN_SERVER_NAME n/a raises error -MAILGUN_SENDER_DOMAIN MAILGUN_SENDER_DOMAIN n/a raises error +MAILGUN_API_KEY MAILGUN_ACCESS_KEY n/a raises error +MAILGUN_DOMAIN MAILGUN_SENDER_DOMAIN n/a raises error NEW_RELIC_APP_NAME NEW_RELIC_APP_NAME n/a raises error NEW_RELIC_LICENSE_KEY NEW_RELIC_LICENSE_KEY n/a raises error DJANGO_OPBEAT_APP_ID OPBEAT['APP_ID'] n/a raises error diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 0e16ffb70..83e41eb89 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -25,11 +25,6 @@ except NotImplementedError: PROJECT_DIR_PATH = os.path.realpath(os.path.curdir) -def remove_file(file_path): - if os.path.exists(file_path): - os.remove(file_path) - - def remove_open_source_project_only_files(): file_names = [ 'CONTRIBUTORS.txt', @@ -72,22 +67,14 @@ def remove_heroku_files(): file_names = [ 'Procfile', 'runtime.txt', + 'requirements.txt', ] for file_name in file_names: - remove_file(os.path.join(PROJECT_DIR_PATH, file_name)) + os.remove(os.path.join(PROJECT_DIR_PATH, file_name)) -def remove_paas_files(): - none_paas_files_left = True - - if '{{ cookiecutter.use_heroku }}'.lower() == 'n': - remove_heroku_files() - none_paas_files_left &= True - else: - none_paas_files_left &= False - - if none_paas_files_left: - remove_file(os.path.join(PROJECT_DIR_PATH, 'requirements.txt')) +def remove_dotenv_file(): + os.remove(os.path.join(PROJECT_DIR_PATH, '.env')) def remove_grunt_files(): @@ -118,6 +105,10 @@ def remove_celery_app(): shutil.rmtree(os.path.join(PROJECT_DIR_PATH, '{{ cookiecutter.project_slug }}', 'taskapp')) +def remove_dottravisyml_file(): + os.remove(os.path.join(PROJECT_DIR_PATH, '.travis.yml')) + + def append_to_project_gitignore(path): gitignore_file_path = os.path.join(PROJECT_DIR_PATH, '.gitignore') with open(gitignore_file_path, 'a') as gitignore_file: @@ -241,7 +232,7 @@ def main(): if '{{ cookiecutter.open_source_license }}' == 'Not open source': remove_open_source_project_only_files() - elif '{{ cookiecutter.open_source_license}}' != 'GPLv3': + if '{{ cookiecutter.open_source_license}}' != 'GPLv3': remove_gplv3_files() if '{{ cookiecutter.use_pycharm }}'.lower() == 'n': @@ -250,7 +241,11 @@ def main(): if '{{ cookiecutter.use_docker }}'.lower() == 'n': remove_docker_files() - remove_paas_files() + if '{{ cookiecutter.use_heroku }}'.lower() == 'n': + remove_heroku_files() + + if '{{ cookiecutter.use_docker }}'.lower() == 'n' and '{{ cookiecutter.use_heroku }}'.lower() == 'n': + remove_dotenv_file() if '{{ cookiecutter.js_task_runner}}'.lower() == 'gulp': remove_grunt_files() @@ -278,6 +273,9 @@ def main(): if '{{ cookiecutter.use_celery }}'.lower() == 'n': remove_celery_app() + if '{{ cookiecutter.use_travisci }}'.lower() == 'n': + remove_dottravisyml_file() + if __name__ == '__main__': main() diff --git a/hooks/pre_gen_project.py b/hooks/pre_gen_project.py index a65fedae8..c48ce0ad8 100644 --- a/hooks/pre_gen_project.py +++ b/hooks/pre_gen_project.py @@ -11,6 +11,9 @@ project_slug = '{{ cookiecutter.project_slug }}' if hasattr(project_slug, 'isidentifier'): assert project_slug.isidentifier(), "'{}' project slug is not a valid Python identifier.".format(project_slug) +assert "\\" not in "{{ cookiecutter.author_name }}", "Don't include backslashes in author name." + + using_docker = '{{ cookiecutter.use_docker }}'.lower() if using_docker == 'n': TERMINATOR = "\x1b[0m" diff --git a/requirements.txt b/requirements.txt index 1790a0216..d549c678a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,17 @@ cookiecutter==1.6.0 -flake8==3.5.0 # pyup: != 2.6.0 sh==1.12.14 binaryornot==0.4.4 -# Testing -pytest==3.3.2 + +# Code quality +# ------------------------------------------------------------------------------ +flake8==3.5.0 pycodestyle==2.3.1 pyflakes==1.6.0 + + +# Testing +# ------------------------------------------------------------------------------ tox==2.9.1 +pytest==3.4.2 pytest-cookies==0.3.0 diff --git a/requirements_to_watch.txt b/requirements_to_watch.txt deleted file mode 100644 index 91f0041bf..000000000 --- a/requirements_to_watch.txt +++ /dev/null @@ -1,2 +0,0 @@ -# These requirements prevented an upgrade to Django 1.11. -django-autoslug==1.9.3 diff --git a/setup.py b/setup.py index 56306f92d..a7efb0a64 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ except ImportError: # Our version ALWAYS matches the version of Django we support # If Django has a new release, we branch, tag, then update this setting after the tag. -version = '1.11.9' +version = '2.0.2' if sys.argv[-1] == 'tag': os.system('git tag -a %s -m "version %s"' % (version, version)) @@ -34,7 +34,7 @@ setup( classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Console', - 'Framework :: Django :: 1.11', + 'Framework :: Django :: 2.0', 'Intended Audience :: Developers', 'Natural Language :: English', 'License :: OSI Approved :: BSD License', @@ -42,7 +42,6 @@ setup( 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: Implementation :: CPython', - 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Software Development', ], keywords=( diff --git a/tests/test_docker.sh b/tests/test_docker.sh index 137694d7a..384fdff2d 100755 --- a/tests/test_docker.sh +++ b/tests/test_docker.sh @@ -12,10 +12,13 @@ cd .cache/docker # create the project using the default settings in cookiecutter.json cookiecutter ../../ --no-input --overwrite-if-exists use_docker=y js_task_runner=None -cd project_name +cd my_awesome_project # run the project's tests docker-compose -f local.yml run django python manage.py test # return non-zero status code if there are migrations that have not been created docker-compose -f local.yml run django python manage.py makemigrations --dry-run --check || { echo "ERROR: there were changes in the models, but migration listed above have not been created and are not saved in version control"; exit 1; } + +# Test support for translations +docker-compose -f local.yml run django python manage.py makemessages diff --git a/tox.ini b/tox.ini index a16b80ed6..040c8a41c 100644 --- a/tox.ini +++ b/tox.ini @@ -3,10 +3,5 @@ skipsdist = true envlist = py36 [testenv] -passenv = LC_ALL, LANG, HOME -deps = - binaryornot - flake8==2.5.5 - pytest-cookies - sh -commands = py.test {posargs:tests} +deps = -rrequirements.txt +commands = pytest {posargs:./tests} diff --git a/{{cookiecutter.project_slug}}/Gruntfile.js b/{{cookiecutter.project_slug}}/Gruntfile.js index 15384c67b..b225266c3 100644 --- a/{{cookiecutter.project_slug}}/Gruntfile.js +++ b/{{cookiecutter.project_slug}}/Gruntfile.js @@ -92,7 +92,7 @@ module.exports = function (grunt) { processors: [ require('pixrem')(), // add fallbacks for rem units - require('autoprefixer-core')({browsers: [ + require('autoprefixer')({browsers: [ 'Android 2.3', 'Android >= 4', 'Chrome >= 20', diff --git a/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile b/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile index b9ee34b76..383b15776 100644 --- a/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile @@ -1,7 +1,18 @@ -FROM python:3.6 +FROM python:3.6-alpine ENV PYTHONUNBUFFERED 1 +RUN apk update \ + # psycopg2 dependencies + && apk add --virtual build-deps gcc python3-dev musl-dev \ + && apk add postgresql-dev \ + # Pillow dependencies + && apk add jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev \ + # CFFI dependencies + && apk add libffi-dev openssl-dev py-cffi \ + # Translations dependencies + && apk add gettext + # Requirements have to be pulled and installed here, otherwise caching won't work COPY ./requirements /requirements RUN pip install -r /requirements/local.txt diff --git a/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start.sh b/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start.sh index c26318b44..0ca8bb507 100644 --- a/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start.sh +++ b/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh set -o errexit set -o pipefail diff --git a/{{cookiecutter.project_slug}}/compose/local/django/celery/worker/start.sh b/{{cookiecutter.project_slug}}/compose/local/django/celery/worker/start.sh index 8b50c8cfd..4335340de 100644 --- a/{{cookiecutter.project_slug}}/compose/local/django/celery/worker/start.sh +++ b/{{cookiecutter.project_slug}}/compose/local/django/celery/worker/start.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh set -o errexit set -o pipefail diff --git a/{{cookiecutter.project_slug}}/compose/local/django/start.sh b/{{cookiecutter.project_slug}}/compose/local/django/start.sh index cf4a41667..50227e19e 100644 --- a/{{cookiecutter.project_slug}}/compose/local/django/start.sh +++ b/{{cookiecutter.project_slug}}/compose/local/django/start.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh set -o errexit set -o pipefail diff --git a/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile b/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile index 48923c80b..2ded8253e 100644 --- a/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile @@ -1,9 +1,18 @@ -FROM python:3.6 +FROM python:3.6-alpine ENV PYTHONUNBUFFERED 1 -RUN groupadd -r django \ - && useradd -r -g django django +RUN apk update \ + # psycopg2 dependencies + && apk add --virtual build-deps gcc python3-dev musl-dev \ + && apk add postgresql-dev \ + # Pillow dependencies + && apk add jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev \ + # CFFI dependencies + && apk add libffi-dev openssl-dev py-cffi + +RUN addgroup -S django \ + && adduser -S -G django django # Requirements have to be pulled and installed here, otherwise caching won't work COPY ./requirements /requirements diff --git a/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start.sh b/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start.sh index 845db0a3d..def83076c 100644 --- a/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start.sh +++ b/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh set -o errexit set -o pipefail diff --git a/{{cookiecutter.project_slug}}/compose/production/django/celery/worker/start.sh b/{{cookiecutter.project_slug}}/compose/production/django/celery/worker/start.sh index 4529aad92..10f0d20c5 100644 --- a/{{cookiecutter.project_slug}}/compose/production/django/celery/worker/start.sh +++ b/{{cookiecutter.project_slug}}/compose/production/django/celery/worker/start.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh set -o errexit set -o pipefail diff --git a/{{cookiecutter.project_slug}}/compose/production/django/entrypoint.sh b/{{cookiecutter.project_slug}}/compose/production/django/entrypoint.sh index 3b83c7bb6..a40a2b7e4 100644 --- a/{{cookiecutter.project_slug}}/compose/production/django/entrypoint.sh +++ b/{{cookiecutter.project_slug}}/compose/production/django/entrypoint.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh set -o errexit set -o pipefail @@ -25,7 +25,7 @@ export DATABASE_URL=postgres://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres:5432/$ export CELERY_BROKER_URL=$REDIS_URL/0 {% endif %} -function postgres_ready(){ +postgres_ready() { python << END import sys import psycopg2 diff --git a/{{cookiecutter.project_slug}}/compose/production/django/gunicorn.sh b/{{cookiecutter.project_slug}}/compose/production/django/gunicorn.sh index 25da06496..39679f289 100644 --- a/{{cookiecutter.project_slug}}/compose/production/django/gunicorn.sh +++ b/{{cookiecutter.project_slug}}/compose/production/django/gunicorn.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh set -o errexit set -o pipefail @@ -6,4 +6,4 @@ set -o nounset python /app/manage.py collectstatic --noinput -/usr/local/bin/gunicorn config.wsgi -w 4 -b 0.0.0.0:5000 --chdir=/app +/usr/local/bin/gunicorn config.wsgi -b 0.0.0.0:5000 --chdir=/app diff --git a/{{cookiecutter.project_slug}}/config/settings/base.py b/{{cookiecutter.project_slug}}/config/settings/base.py index 2ce697677..3df643556 100644 --- a/{{cookiecutter.project_slug}}/config/settings/base.py +++ b/{{cookiecutter.project_slug}}/config/settings/base.py @@ -1,232 +1,119 @@ """ -Base settings for {{cookiecutter.project_name}} project. - -For more information on this file, see -https://docs.djangoproject.com/en/dev/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/dev/ref/settings/ +Base settings to build other settings files upon. """ + import environ ROOT_DIR = environ.Path(__file__) - 3 # ({{ cookiecutter.project_slug }}/config/settings/base.py - 3 = {{ cookiecutter.project_slug }}/) APPS_DIR = ROOT_DIR.path('{{ cookiecutter.project_slug }}') -# Load operating system environment variables and then prepare to use them env = environ.Env() -# .env file, should load only in development environment READ_DOT_ENV_FILE = env.bool('DJANGO_READ_DOT_ENV_FILE', default=False) - if READ_DOT_ENV_FILE: - # Operating System Environment variables have precedence over variables defined in the .env file, - # that is to say variables from the .env files will only be used if not defined - # as environment variables. - env_file = str(ROOT_DIR.path('.env')) - print('Loading : {}'.format(env_file)) - env.read_env(env_file) - print('The .env file has been loaded. See base.py for more information') + # OS environment variables take precedence over variables from .env + env.read_env(str(ROOT_DIR.path('.env'))) -# APP CONFIGURATION +# GENERAL +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#debug +DEBUG = env.bool('DJANGO_DEBUG', False) +# Local time zone. Choices are +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# though not all of them may be available with every OS. +# In Windows, this must be set to your system time zone. +TIME_ZONE = '{{ cookiecutter.timezone }}' +# https://docs.djangoproject.com/en/dev/ref/settings/#language-code +LANGUAGE_CODE = 'en-us' +# https://docs.djangoproject.com/en/dev/ref/settings/#site-id +SITE_ID = 1 +# https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n +USE_I18N = True +# https://docs.djangoproject.com/en/dev/ref/settings/#use-l10n +USE_L10N = True +# https://docs.djangoproject.com/en/dev/ref/settings/#use-tz +USE_TZ = True + +# DATABASES +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#databases +{% if cookiecutter.use_docker == 'y' -%} +DATABASES = { + 'default': env.db('DATABASE_URL'), +} +{%- else %} +DATABASES = { + 'default': env.db('DATABASE_URL', default='postgres://{% if cookiecutter.windows == 'y' %}localhost{% endif %}/{{cookiecutter.project_slug}}'), +} +{%- endif %} +DATABASES['default']['ATOMIC_REQUESTS'] = True + +# URLS +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf +ROOT_URLCONF = 'config.urls' +# https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application +WSGI_APPLICATION = 'config.wsgi.application' + +# APPS # ------------------------------------------------------------------------------ DJANGO_APPS = [ - # Default Django apps: 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.messages', 'django.contrib.staticfiles', - - # Useful template tags: - # 'django.contrib.humanize', - - # Admin + # 'django.contrib.humanize', # Handy template tags 'django.contrib.admin', ] THIRD_PARTY_APPS = [ - 'crispy_forms', # Form layouts - 'allauth', # registration - 'allauth.account', # registration - 'allauth.socialaccount', # registration -] + 'crispy_forms', -# Apps specific for this project go here. + 'allauth', + 'allauth.account', + 'allauth.socialaccount', +] LOCAL_APPS = [ - # custom users app '{{ cookiecutter.project_slug }}.users.apps.UsersConfig', # Your stuff: custom apps go here ] - -# See: https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps +# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS -# MIDDLEWARE CONFIGURATION -# ------------------------------------------------------------------------------ -MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', -] - -# MIGRATIONS CONFIGURATION +# MIGRATIONS # ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#migration-modules MIGRATION_MODULES = { 'sites': '{{ cookiecutter.project_slug }}.contrib.sites.migrations' } -# DEBUG +# AUTHENTICATION # ------------------------------------------------------------------------------ -# See: https://docs.djangoproject.com/en/dev/ref/settings/#debug -DEBUG = env.bool('DJANGO_DEBUG', False) - -# FIXTURE CONFIGURATION -# ------------------------------------------------------------------------------ -# See: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FIXTURE_DIRS -FIXTURE_DIRS = ( - str(APPS_DIR.path('fixtures')), -) - -# EMAIL CONFIGURATION -# ------------------------------------------------------------------------------ -EMAIL_BACKEND = env('DJANGO_EMAIL_BACKEND', default='django.core.mail.backends.smtp.EmailBackend') - -# MANAGER CONFIGURATION -# ------------------------------------------------------------------------------ -# See: https://docs.djangoproject.com/en/dev/ref/settings/#admins -ADMINS = [ - ("""{{cookiecutter.author_name}}""", '{{cookiecutter.email}}'), +# https://docs.djangoproject.com/en/dev/ref/settings/#authentication-backends +AUTHENTICATION_BACKENDS = [ + 'django.contrib.auth.backends.ModelBackend', + 'allauth.account.auth_backends.AuthenticationBackend', ] +# https://docs.djangoproject.com/en/dev/ref/settings/#auth-user-model +AUTH_USER_MODEL = 'users.User' +# https://docs.djangoproject.com/en/dev/ref/settings/#login-redirect-url +LOGIN_REDIRECT_URL = 'users:redirect' +# https://docs.djangoproject.com/en/dev/ref/settings/#login-url +LOGIN_URL = 'account_login' -# See: https://docs.djangoproject.com/en/dev/ref/settings/#managers -MANAGERS = ADMINS - -# DATABASE CONFIGURATION +# PASSWORDS # ------------------------------------------------------------------------------ -# See: https://docs.djangoproject.com/en/dev/ref/settings/#databases -# Uses django-environ to accept uri format -# See: https://django-environ.readthedocs.io/en/latest/#supported-types -DATABASES = { - 'default': env.db('DATABASE_URL', default='postgres://{% if cookiecutter.windows == 'y' %}localhost{% endif %}/{{cookiecutter.project_slug}}'), -} -DATABASES['default']['ATOMIC_REQUESTS'] = True - - -# GENERAL CONFIGURATION -# ------------------------------------------------------------------------------ -# Local time zone for this installation. Choices can be found here: -# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name -# although not all choices may be available on all operating systems. -# In a Windows environment this must be set to your system time zone. -TIME_ZONE = '{{ cookiecutter.timezone }}' - -# See: https://docs.djangoproject.com/en/dev/ref/settings/#language-code -LANGUAGE_CODE = 'en-us' - -# See: https://docs.djangoproject.com/en/dev/ref/settings/#site-id -SITE_ID = 1 - -# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n -USE_I18N = True - -# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-l10n -USE_L10N = True - -# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-tz -USE_TZ = True - -# TEMPLATE CONFIGURATION -# ------------------------------------------------------------------------------ -# See: https://docs.djangoproject.com/en/dev/ref/settings/#templates -TEMPLATES = [ - { - # See: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-TEMPLATES-BACKEND - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-dirs - 'DIRS': [ - str(APPS_DIR.path('templates')), - ], - 'OPTIONS': { - # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-debug - 'debug': DEBUG, - # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-loaders - # https://docs.djangoproject.com/en/dev/ref/templates/api/#loader-types - 'loaders': [ - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', - ], - # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.template.context_processors.i18n', - 'django.template.context_processors.media', - 'django.template.context_processors.static', - 'django.template.context_processors.tz', - 'django.contrib.messages.context_processors.messages', - # Your stuff: custom template context processors go here - ], - }, - }, -] - -# See: http://django-crispy-forms.readthedocs.io/en/latest/install.html#template-packs -CRISPY_TEMPLATE_PACK = 'bootstrap4' - -# STATIC FILE CONFIGURATION -# ------------------------------------------------------------------------------ -# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root -STATIC_ROOT = str(ROOT_DIR('staticfiles')) - -# See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url -STATIC_URL = '/static/' - -# See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS -STATICFILES_DIRS = [ - str(APPS_DIR.path('static')), -] - -# See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders -STATICFILES_FINDERS = [ - 'django.contrib.staticfiles.finders.FileSystemFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', -] - -# MEDIA CONFIGURATION -# ------------------------------------------------------------------------------ -# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root -MEDIA_ROOT = str(APPS_DIR('media')) - -# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-url -MEDIA_URL = '/media/' - -# URL Configuration -# ------------------------------------------------------------------------------ -ROOT_URLCONF = 'config.urls' - -# See: https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application -WSGI_APPLICATION = 'config.wsgi.application' - -# PASSWORD STORAGE SETTINGS -# ------------------------------------------------------------------------------ -# See https://docs.djangoproject.com/en/dev/topics/auth/passwords/#using-argon2-with-django +# https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers PASSWORD_HASHERS = [ + # https://docs.djangoproject.com/en/dev/topics/auth/passwords/#using-argon2-with-django 'django.contrib.auth.hashers.Argon2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', 'django.contrib.auth.hashers.BCryptPasswordHasher', ] - -# PASSWORD VALIDATION # https://docs.djangoproject.com/en/dev/ref/settings/#auth-password-validators -# ------------------------------------------------------------------------------ - AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', @@ -242,50 +129,142 @@ AUTH_PASSWORD_VALIDATORS = [ }, ] -# AUTHENTICATION CONFIGURATION +# MIDDLEWARE # ------------------------------------------------------------------------------ -AUTHENTICATION_BACKENDS = [ - 'django.contrib.auth.backends.ModelBackend', - 'allauth.account.auth_backends.AuthenticationBackend', +# https://docs.djangoproject.com/en/dev/ref/settings/#middleware +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] -# Some really nice defaults -ACCOUNT_AUTHENTICATION_METHOD = 'username' -ACCOUNT_EMAIL_REQUIRED = True -ACCOUNT_EMAIL_VERIFICATION = 'mandatory' +# STATIC +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#static-root +STATIC_ROOT = str(ROOT_DIR('staticfiles')) +# https://docs.djangoproject.com/en/dev/ref/settings/#static-url +STATIC_URL = '/static/' +# https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS +STATICFILES_DIRS = [ + str(APPS_DIR.path('static')), +] +# https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders +STATICFILES_FINDERS = [ + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', +] -ACCOUNT_ALLOW_REGISTRATION = env.bool('DJANGO_ACCOUNT_ALLOW_REGISTRATION', True) -ACCOUNT_ADAPTER = '{{cookiecutter.project_slug}}.users.adapters.AccountAdapter' -SOCIALACCOUNT_ADAPTER = '{{cookiecutter.project_slug}}.users.adapters.SocialAccountAdapter' +# MEDIA +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#media-root +MEDIA_ROOT = str(APPS_DIR('media')) +# https://docs.djangoproject.com/en/dev/ref/settings/#media-url +MEDIA_URL = '/media/' -# Custom user app defaults -# Select the correct user model -AUTH_USER_MODEL = 'users.User' -LOGIN_REDIRECT_URL = 'users:redirect' -LOGIN_URL = 'account_login' +# TEMPLATES +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#templates +TEMPLATES = [ + { + # https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-TEMPLATES-BACKEND + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + # https://docs.djangoproject.com/en/dev/ref/settings/#template-dirs + 'DIRS': [ + str(APPS_DIR.path('templates')), + ], + 'OPTIONS': { + # https://docs.djangoproject.com/en/dev/ref/settings/#template-debug + 'debug': DEBUG, + # https://docs.djangoproject.com/en/dev/ref/settings/#template-loaders + # https://docs.djangoproject.com/en/dev/ref/templates/api/#loader-types + 'loaders': [ + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', + ], + # https://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.template.context_processors.i18n', + 'django.template.context_processors.media', + 'django.template.context_processors.static', + 'django.template.context_processors.tz', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] +# http://django-crispy-forms.readthedocs.io/en/latest/install.html#template-packs +CRISPY_TEMPLATE_PACK = 'bootstrap4' -# SLUGLIFIER -AUTOSLUG_SLUGIFY_FUNCTION = 'slugify.slugify' -{% if cookiecutter.use_celery == 'y' %} -########## CELERY +# FIXTURES +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#fixture-dirs +FIXTURE_DIRS = ( + str(APPS_DIR.path('fixtures')), +) + +# EMAIL +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend +EMAIL_BACKEND = env('DJANGO_EMAIL_BACKEND', default='django.core.mail.backends.smtp.EmailBackend') + +# ADMIN +# ------------------------------------------------------------------------------ +# Django Admin URL regex. +ADMIN_URL = r'^admin/' +# https://docs.djangoproject.com/en/dev/ref/settings/#admins +ADMINS = [ + ("""{{cookiecutter.author_name}}""", '{{cookiecutter.email}}'), +] +# https://docs.djangoproject.com/en/dev/ref/settings/#managers +MANAGERS = ADMINS + +{% if cookiecutter.use_celery == 'y' -%} +# Celery +# ------------------------------------------------------------------------------ INSTALLED_APPS += ['{{cookiecutter.project_slug}}.taskapp.celery.CeleryConfig'] +# http://docs.celeryproject.org/en/latest/userguide/configuration.html#std:setting-broker_url CELERY_BROKER_URL = env('CELERY_BROKER_URL', default='django://') +# http://docs.celeryproject.org/en/latest/userguide/configuration.html#std:setting-result_backend if CELERY_BROKER_URL == 'django://': CELERY_RESULT_BACKEND = 'redis://' else: CELERY_RESULT_BACKEND = CELERY_BROKER_URL -########## END CELERY -{% endif %} +# http://docs.celeryproject.org/en/latest/userguide/configuration.html#std:setting-accept_content +CELERY_ACCEPT_CONTENT = ['json'] +# http://docs.celeryproject.org/en/latest/userguide/configuration.html#std:setting-task_serializer +CELERY_TASK_SERIALIZER = 'json' +# http://docs.celeryproject.org/en/latest/userguide/configuration.html#std:setting-result_serializer +CELERY_RESULT_SERIALIZER = 'json' -{%- if cookiecutter.use_compressor == 'y'-%} +{%- endif %} +# django-allauth +# ------------------------------------------------------------------------------ +ACCOUNT_ALLOW_REGISTRATION = env.bool('DJANGO_ACCOUNT_ALLOW_REGISTRATION', True) +# https://django-allauth.readthedocs.io/en/latest/configuration.html +ACCOUNT_AUTHENTICATION_METHOD = 'username' +# https://django-allauth.readthedocs.io/en/latest/configuration.html +ACCOUNT_EMAIL_REQUIRED = True +# https://django-allauth.readthedocs.io/en/latest/configuration.html +ACCOUNT_EMAIL_VERIFICATION = 'mandatory' +# https://django-allauth.readthedocs.io/en/latest/configuration.html +ACCOUNT_ADAPTER = '{{cookiecutter.project_slug}}.users.adapters.AccountAdapter' +# https://django-allauth.readthedocs.io/en/latest/configuration.html +SOCIALACCOUNT_ADAPTER = '{{cookiecutter.project_slug}}.users.adapters.SocialAccountAdapter' + +{% if cookiecutter.use_compressor == 'y' -%} # django-compressor # ------------------------------------------------------------------------------ +# https://django-compressor.readthedocs.io/en/latest/quickstart/#installation INSTALLED_APPS += ['compressor'] STATICFILES_FINDERS += ['compressor.finders.CompressorFinder'] + {%- endif %} - -# Location of root django.contrib.admin URL, use {% raw %}{% url 'admin:index' %}{% endraw %} -ADMIN_URL = r'^admin/' - -# Your common stuff: Below this line define 3rd party library settings +# Your stuff... # ------------------------------------------------------------------------------ diff --git a/{{cookiecutter.project_slug}}/config/settings/local.py b/{{cookiecutter.project_slug}}/config/settings/local.py index 6fc3526fd..6dc27de4b 100644 --- a/{{cookiecutter.project_slug}}/config/settings/local.py +++ b/{{cookiecutter.project_slug}}/config/settings/local.py @@ -1,47 +1,21 @@ -""" -Local settings for {{cookiecutter.project_name}} project. - -- Run in Debug mode -{% if cookiecutter.use_mailhog == 'y' and cookiecutter.use_docker == 'y' %} -- Use mailhog for emails via Docker -{% elif cookiecutter.use_mailhog == 'y' and cookiecutter.use_docker == 'n' %} -- Use mailhog for emails -{% else %} -- Use console backend for emails -{% endif %} -- Add Django Debug Toolbar -- Add django-extensions as app -""" - from .base import * # noqa +from .base import env -# DEBUG +# GENERAL # ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#debug DEBUG = env.bool('DJANGO_DEBUG', default=True) -TEMPLATES[0]['OPTIONS']['debug'] = DEBUG - -# SECRET CONFIGURATION -# ------------------------------------------------------------------------------ -# See: https://docs.djangoproject.com/en/dev/ref/settings/#secret-key -# Note: This key only used for development and testing. +# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key SECRET_KEY = env('DJANGO_SECRET_KEY', default='!!!SET DJANGO_SECRET_KEY!!!') +# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts +ALLOWED_HOSTS = [ + "localhost", + "0.0.0.0", +] -# Mail settings -# ------------------------------------------------------------------------------ - -EMAIL_PORT = 1025 -{% if cookiecutter.use_mailhog == 'y' and cookiecutter.use_docker == 'y' %} -EMAIL_HOST = env('EMAIL_HOST', default='mailhog') -{% elif cookiecutter.use_mailhog == 'y' and cookiecutter.use_docker == 'n' %} -EMAIL_HOST = 'localhost' -{% else %} -EMAIL_HOST = 'localhost' -EMAIL_BACKEND = env('DJANGO_EMAIL_BACKEND', - default='django.core.mail.backends.console.EmailBackend') -{% endif %} - -# CACHING +# CACHES # ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#caches CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', @@ -49,40 +23,62 @@ CACHES = { } } +# TEMPLATES +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#templates +TEMPLATES[0]['OPTIONS']['debug'] = DEBUG # noqa F405 + +# EMAIL +# ------------------------------------------------------------------------------ +{% if cookiecutter.use_mailhog == 'y' and cookiecutter.use_docker == 'y' -%} +# https://docs.djangoproject.com/en/dev/ref/settings/#email-host +EMAIL_HOST = env('EMAIL_HOST', default='mailhog') +{%- elif cookiecutter.use_mailhog == 'y' and cookiecutter.use_docker == 'n' -%} +# https://docs.djangoproject.com/en/dev/ref/settings/#email-host +EMAIL_HOST = 'localhost' +{%- else -%} +# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend +EMAIL_BACKEND = env('DJANGO_EMAIL_BACKEND', default='django.core.mail.backends.console.EmailBackend') +# https://docs.djangoproject.com/en/dev/ref/settings/#email-host +EMAIL_HOST = 'localhost' +{%- endif %} +# https://docs.djangoproject.com/en/dev/ref/settings/#email-port +EMAIL_PORT = 1025 + # django-debug-toolbar # ------------------------------------------------------------------------------ -MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware', ] -INSTALLED_APPS += ['debug_toolbar', ] - -INTERNAL_IPS = ['127.0.0.1', '10.0.2.2', ] -{% if cookiecutter.use_docker == 'y' %} -{# [cookiecutter-django] This is a workaround to flake8 "imported but unused" errors #} -import socket -import os -# tricks to have debug toolbar when developing with docker -if os.environ.get('USE_DOCKER') == 'yes': - hostname, _, ips = socket.gethostbyname_ex(socket.gethostname()) - INTERNAL_IPS += [ip[:-1] + '1' for ip in ips] -{% endif %} +# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#prerequisites +INSTALLED_APPS += ['debug_toolbar'] # noqa F405 +# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#middleware +MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware'] # noqa F405 +# https://django-debug-toolbar.readthedocs.io/en/latest/configuration.html#debug-toolbar-config DEBUG_TOOLBAR_CONFIG = { 'DISABLE_PANELS': [ 'debug_toolbar.panels.redirects.RedirectsPanel', ], 'SHOW_TEMPLATE_CONTEXT': True, } +# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#internal-ips +INTERNAL_IPS = ['127.0.0.1', '10.0.2.2'] +{% if cookiecutter.use_docker == 'y' -%} +import socket +import os +if os.environ.get('USE_DOCKER') == 'yes': + hostname, _, ips = socket.gethostbyname_ex(socket.gethostname()) + INTERNAL_IPS += [ip[:-1] + '1' for ip in ips] +{%- endif %} # django-extensions # ------------------------------------------------------------------------------ -INSTALLED_APPS += ['django_extensions', ] +# https://django-extensions.readthedocs.io/en/latest/installation_instructions.html#configuration +INSTALLED_APPS += ['django_extensions'] # noqa F405 +{% if cookiecutter.use_celery == 'y' -%} -# TESTING +# Celery # ------------------------------------------------------------------------------ -TEST_RUNNER = 'django.test.runner.DiscoverRunner' -{% if cookiecutter.use_celery == 'y' %} -########## CELERY -# In development, all tasks will be executed locally by blocking until the task returns +# http://docs.celeryproject.org/en/latest/userguide/configuration.html#std:setting-task_always_eager CELERY_ALWAYS_EAGER = True -########## END CELERY -{% endif %} -# Your local stuff: Below this line define 3rd party library settings + +{%- endif %} +# Your stuff... # ------------------------------------------------------------------------------ diff --git a/{{cookiecutter.project_slug}}/config/settings/production.py b/{{cookiecutter.project_slug}}/config/settings/production.py index f6f857876..b29a22b06 100644 --- a/{{cookiecutter.project_slug}}/config/settings/production.py +++ b/{{cookiecutter.project_slug}}/config/settings/production.py @@ -1,203 +1,190 @@ -""" -Production settings for {{cookiecutter.project_name}} project. - -{% if cookiecutter.use_whitenoise == 'y' -%} -- Use WhiteNoise for serving static files{% endif %} -- Use Amazon's S3 for storing {% if cookiecutter.use_whitenoise == 'n' -%}static files and {% endif %}uploaded media -- Use mailgun to send emails -- Use Redis for cache -{% if cookiecutter.use_sentry_for_error_reporting == 'y' %} -- Use sentry for error logging -{% endif %} -{% if cookiecutter.use_opbeat == 'y' %} -- Use opbeat for error reporting -{% endif %} -""" - -{% if cookiecutter.use_sentry_for_error_reporting == 'y' %} import logging -{% endif %} from .base import * # noqa +from .base import env -# SECRET CONFIGURATION +# GENERAL # ------------------------------------------------------------------------------ -# See: https://docs.djangoproject.com/en/dev/ref/settings/#secret-key -# Raises ImproperlyConfigured exception if DJANGO_SECRET_KEY not in os.environ +# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key SECRET_KEY = env('DJANGO_SECRET_KEY') +# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts +ALLOWED_HOSTS = env.list('DJANGO_ALLOWED_HOSTS', default=['{{ cookiecutter.domain_name }}']) - -# This ensures that Django will be able to detect a secure connection -# properly on Heroku. -SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') - -{%- if cookiecutter.use_sentry_for_error_reporting == 'y' %} -# raven sentry client -# See https://docs.sentry.io/clients/python/integrations/django/ -INSTALLED_APPS += ['raven.contrib.django.raven_compat', ] -{% endif %} -{%- if cookiecutter.use_whitenoise == 'y' %} -# Use Whitenoise to serve static files -# See: https://whitenoise.readthedocs.io/ -WHITENOISE_MIDDLEWARE = ['whitenoise.middleware.WhiteNoiseMiddleware', ] -MIDDLEWARE = WHITENOISE_MIDDLEWARE + MIDDLEWARE -{% endif %} -{%- if cookiecutter.use_sentry_for_error_reporting == 'y' -%} -RAVEN_MIDDLEWARE = ['raven.contrib.django.raven_compat.middleware.SentryResponseErrorIdMiddleware'] -MIDDLEWARE = RAVEN_MIDDLEWARE + MIDDLEWARE -{% endif %} -{%- if cookiecutter.use_opbeat == 'y' -%} -# opbeat integration -# See https://opbeat.com/languages/django/ -INSTALLED_APPS += ['opbeat.contrib.django', ] -OPBEAT = { - 'ORGANIZATION_ID': env('DJANGO_OPBEAT_ORGANIZATION_ID'), - 'APP_ID': env('DJANGO_OPBEAT_APP_ID'), - 'SECRET_TOKEN': env('DJANGO_OPBEAT_SECRET_TOKEN') -} -MIDDLEWARE = ['opbeat.contrib.django.middleware.OpbeatAPMMiddleware', ] + MIDDLEWARE -{% endif %} - -# SECURITY CONFIGURATION +# DATABASES # ------------------------------------------------------------------------------ -# See https://docs.djangoproject.com/en/dev/ref/middleware/#module-django.middleware.security -# and https://docs.djangoproject.com/en/dev/howto/deployment/checklist/#run-manage-py-check-deploy +DATABASES['default'] = env.db('DATABASE_URL') # noqa F405 +DATABASES['default']['ATOMIC_REQUESTS'] = True # noqa F405 +DATABASES['default']['CONN_MAX_AGE'] = env.int('CONN_MAX_AGE', default=60) # noqa F405 -# set this to 60 seconds and then to 518400 when you can prove it works -SECURE_HSTS_SECONDS = 60 -SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool( - 'DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS', default=True) -SECURE_CONTENT_TYPE_NOSNIFF = env.bool( - 'DJANGO_SECURE_CONTENT_TYPE_NOSNIFF', default=True) -SECURE_BROWSER_XSS_FILTER = True -SESSION_COOKIE_SECURE = True -SESSION_COOKIE_HTTPONLY = 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 +# CACHES # ------------------------------------------------------------------------------ -# Hosts/domain names that are valid for this site -# See https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts -ALLOWED_HOSTS = env.list('DJANGO_ALLOWED_HOSTS', default=['{{cookiecutter.domain_name}}', ]) -# END SITE CONFIGURATION - -INSTALLED_APPS += ['gunicorn', ] - - -# STORAGE CONFIGURATION -# ------------------------------------------------------------------------------ -# Uploaded Media Files -# ------------------------ -# See: http://django-storages.readthedocs.io/en/latest/index.html -INSTALLED_APPS += ['storages', ] - -AWS_ACCESS_KEY_ID = env('DJANGO_AWS_ACCESS_KEY_ID') -AWS_SECRET_ACCESS_KEY = env('DJANGO_AWS_SECRET_ACCESS_KEY') -AWS_STORAGE_BUCKET_NAME = env('DJANGO_AWS_STORAGE_BUCKET_NAME') -AWS_AUTO_CREATE_BUCKET = True -AWS_QUERYSTRING_AUTH = False - -# AWS cache settings, don't change unless you know what you're doing: -AWS_EXPIRY = 60 * 60 * 24 * 7 - -# TODO See: https://github.com/jschneier/django-storages/issues/47 -# Revert the following and use str after the above-mentioned bug is fixed in -# either django-storage-redux or boto -control = 'max-age=%d, s-maxage=%d, must-revalidate' % (AWS_EXPIRY, AWS_EXPIRY) -AWS_S3_OBJECT_PARAMETERS = { - 'CacheControl': bytes(control, encoding='latin-1'), -} - -# URL that handles the media served from MEDIA_ROOT, used for managing -# stored files. -{% if cookiecutter.use_whitenoise == 'y' -%} -MEDIA_URL = 'https://s3.amazonaws.com/%s/' % AWS_STORAGE_BUCKET_NAME -DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' -{% else %} -# See:http://stackoverflow.com/questions/10390244/ -from storages.backends.s3boto3 import S3Boto3Storage -StaticRootS3BotoStorage = lambda: S3Boto3Storage(location='static') # noqa -MediaRootS3BotoStorage = lambda: S3Boto3Storage(location='media', file_overwrite=False) # noqa -DEFAULT_FILE_STORAGE = 'config.settings.production.MediaRootS3BotoStorage' - -MEDIA_URL = 'https://s3.amazonaws.com/%s/media/' % AWS_STORAGE_BUCKET_NAME -{%- endif %} - -# Static Assets -# ------------------------ -{% if cookiecutter.use_whitenoise == 'y' -%} -STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' -{% else %} -STATIC_URL = 'https://s3.amazonaws.com/%s/static/' % AWS_STORAGE_BUCKET_NAME -STATICFILES_STORAGE = 'config.settings.production.StaticRootS3BotoStorage' -# See: https://github.com/antonagestam/collectfast -# For Django 1.7+, 'collectfast' should come before -# 'django.contrib.staticfiles' -AWS_PRELOAD_METADATA = True -INSTALLED_APPS = ['collectfast', ] + INSTALLED_APPS -{%- endif %} -{% if cookiecutter.use_compressor == 'y'-%} -# COMPRESSOR -# ------------------------------------------------------------------------------ -COMPRESS_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' -COMPRESS_URL = STATIC_URL -COMPRESS_ENABLED = env.bool('COMPRESS_ENABLED', default=True) -{%- endif %} -# EMAIL -# ------------------------------------------------------------------------------ -DEFAULT_FROM_EMAIL = env('DJANGO_DEFAULT_FROM_EMAIL', - default='{{cookiecutter.project_name}} ') -EMAIL_SUBJECT_PREFIX = env('DJANGO_EMAIL_SUBJECT_PREFIX', default='[{{cookiecutter.project_name}}]') -SERVER_EMAIL = env('DJANGO_SERVER_EMAIL', default=DEFAULT_FROM_EMAIL) - -# Anymail with Mailgun -INSTALLED_APPS += ['anymail', ] -ANYMAIL = { - 'MAILGUN_API_KEY': env('DJANGO_MAILGUN_API_KEY'), - 'MAILGUN_SENDER_DOMAIN': env('MAILGUN_SENDER_DOMAIN') -} -EMAIL_BACKEND = 'anymail.backends.mailgun.EmailBackend' - -# TEMPLATE CONFIGURATION -# ------------------------------------------------------------------------------ -# See: -# https://docs.djangoproject.com/en/dev/ref/templates/api/#django.template.loaders.cached.Loader -TEMPLATES[0]['OPTIONS']['loaders'] = [ - ('django.template.loaders.cached.Loader', [ - 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader', ]), -] -{% set _DEFAULT_CONN_MAX_AGE=60 %} -# DATABASE CONFIGURATION -# ------------------------------------------------------------------------------ - -# Use the Heroku-style specification -# Raises ImproperlyConfigured exception if DATABASE_URL not in os.environ -DATABASES['default'] = env.db('DATABASE_URL') -DATABASES['default']['CONN_MAX_AGE'] = env.int('CONN_MAX_AGE', default={{ _DEFAULT_CONN_MAX_AGE }}) -DATABASES['default']['ATOMIC_REQUESTS'] = True - -# CACHING -# ------------------------------------------------------------------------------ -REDIS_LOCATION = '{0}/{1}'.format(env('REDIS_URL', default='redis://127.0.0.1:6379'), 0) - -# Heroku URL does not pass the DB number, so we parse it in CACHES = { 'default': { 'BACKEND': 'django_redis.cache.RedisCache', - 'LOCATION': REDIS_LOCATION, + 'LOCATION': f'{env("REDIS_URL", default="redis://127.0.0.1:6379")}/{0}', 'OPTIONS': { 'CLIENT_CLASS': 'django_redis.client.DefaultClient', - 'IGNORE_EXCEPTIONS': True, # mimics memcache behavior. - # http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior + # Mimicing memcache behavior. + # http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior + 'IGNORE_EXCEPTIONS': True, } } } -{% if cookiecutter.use_sentry_for_error_reporting == 'y' %} -# Sentry Configuration +# SECURITY +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#secure-proxy-ssl-header +SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') +# https://docs.djangoproject.com/en/dev/ref/settings/#secure-ssl-redirect +SECURE_SSL_REDIRECT = env.bool('DJANGO_SECURE_SSL_REDIRECT', default=True) +# https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-secure +SESSION_COOKIE_SECURE = True +# https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-httponly +SESSION_COOKIE_HTTPONLY = True +# https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-secure +CSRF_COOKIE_SECURE = True +# https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-httponly +CSRF_COOKIE_HTTPONLY = True +# https://docs.djangoproject.com/en/dev/topics/security/#ssl-https +# https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-seconds +# TODO: set this to 60 seconds first and then to 518400 once you prove the former works +SECURE_HSTS_SECONDS = 60 +# https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-include-subdomains +SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool('DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS', default=True) +# https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-preload +SECURE_HSTS_PRELOAD = env.bool('DJANGO_SECURE_HSTS_PRELOAD', default=True) +# https://docs.djangoproject.com/en/dev/ref/middleware/#x-content-type-options-nosniff +SECURE_CONTENT_TYPE_NOSNIFF = env.bool('DJANGO_SECURE_CONTENT_TYPE_NOSNIFF', default=True) +# https://docs.djangoproject.com/en/dev/ref/settings/#secure-browser-xss-filter +SECURE_BROWSER_XSS_FILTER = True +# https://docs.djangoproject.com/en/dev/ref/settings/#x-frame-options +X_FRAME_OPTIONS = 'DENY' + +# STORAGES +# ------------------------------------------------------------------------------ +# https://django-storages.readthedocs.io/en/latest/#installation +INSTALLED_APPS += ['storages'] # noqa F405 +# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings +AWS_ACCESS_KEY_ID = env('DJANGO_AWS_ACCESS_KEY_ID') +# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings +AWS_SECRET_ACCESS_KEY = env('DJANGO_AWS_SECRET_ACCESS_KEY') +# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings +AWS_STORAGE_BUCKET_NAME = env('DJANGO_AWS_STORAGE_BUCKET_NAME') +# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings +AWS_AUTO_CREATE_BUCKET = True +# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings +AWS_QUERYSTRING_AUTH = False +# DO NOT change these unless you know what you're doing. +_AWS_EXPIRY = 60 * 60 * 24 * 7 +# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings +AWS_S3_OBJECT_PARAMETERS = { + 'CacheControl': f'max-age={_AWS_EXPIRY}, s-maxage={_AWS_EXPIRY}, must-revalidate', +} + +# STATIC +# ------------------------ +{% if cookiecutter.use_whitenoise == 'y' -%} +STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' +{%- else %} +STATICFILES_STORAGE = 'config.settings.production.StaticRootS3BotoStorage' +STATIC_URL = 'https://s3.amazonaws.com/%s/static/' % AWS_STORAGE_BUCKET_NAME +{%- endif %} + +# MEDIA +# ------------------------------------------------------------------------------ +{% if cookiecutter.use_whitenoise == 'y' -%} +DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' +MEDIA_URL = f'https://s3.amazonaws.com/{AWS_STORAGE_BUCKET_NAME}/' +{%- else %} +# region http://stackoverflow.com/questions/10390244/ +from storages.backends.s3boto3 import S3Boto3Storage +StaticRootS3BotoStorage = lambda: S3Boto3Storage(location='static') # noqa +MediaRootS3BotoStorage = lambda: S3Boto3Storage(location='media', file_overwrite=False) # noqa +# endregion +DEFAULT_FILE_STORAGE = 'config.settings.production.MediaRootS3BotoStorage' +MEDIA_URL = f'https://s3.amazonaws.com/{AWS_STORAGE_BUCKET_NAME}/media/' +{%- endif %} + +# TEMPLATES +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#templates +TEMPLATES[0]['OPTIONS']['loaders'] = [ # noqa F405 + ( + 'django.template.loaders.cached.Loader', + [ + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', + ] + ), +] + +# EMAIL +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#default-from-email +DEFAULT_FROM_EMAIL = env( + 'DJANGO_DEFAULT_FROM_EMAIL', + default='{{cookiecutter.project_name}} ' +) +# https://docs.djangoproject.com/en/dev/ref/settings/#server-email +SERVER_EMAIL = env('DJANGO_SERVER_EMAIL', default=DEFAULT_FROM_EMAIL) +# https://docs.djangoproject.com/en/dev/ref/settings/#email-subject-prefix +EMAIL_SUBJECT_PREFIX = env('DJANGO_EMAIL_SUBJECT_PREFIX', default='[{{cookiecutter.project_name}}]') + +# ADMIN +# ------------------------------------------------------------------------------ +# Django Admin URL regex. +ADMIN_URL = env('DJANGO_ADMIN_URL') + +# Anymail (Mailgun) +# ------------------------------------------------------------------------------ +# https://anymail.readthedocs.io/en/stable/installation/#installing-anymail +INSTALLED_APPS += ['anymail'] # noqa F405 +EMAIL_BACKEND = 'anymail.backends.mailgun.EmailBackend' +# https://anymail.readthedocs.io/en/stable/installation/#anymail-settings-reference +ANYMAIL = { + 'MAILGUN_API_KEY': env('MAILGUN_API_KEY'), + 'MAILGUN_SENDER_DOMAIN': env('MAILGUN_DOMAIN') +} + +# Gunicorn +# ------------------------------------------------------------------------------ +INSTALLED_APPS += ['gunicorn'] # noqa F405 + +{% if cookiecutter.use_whitenoise == 'y' -%} +# WhiteNoise +# ------------------------------------------------------------------------------ +# http://whitenoise.evans.io/en/latest/django.html#enable-whitenoise +MIDDLEWARE = ['whitenoise.middleware.WhiteNoiseMiddleware'] + MIDDLEWARE # noqa F405 + +{%- endif %} +{% if cookiecutter.use_compressor == 'y' -%} +# django-compressor +# ------------------------------------------------------------------------------ +# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_ENABLED +COMPRESS_ENABLED = env.bool('COMPRESS_ENABLED', default=True) +# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_STORAGE +COMPRESS_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' +# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_URL +COMPRESS_URL = STATIC_URL + +{%- endif %} +{% if cookiecutter.use_whitenoise == 'n' -%} +# Collectfast +# ------------------------------------------------------------------------------ +# https://github.com/antonagestam/collectfast#installation +INSTALLED_APPS = ['collectfast'] + INSTALLED_APPS # noqa F405 +AWS_PRELOAD_METADATA = True + +{%- endif %} +{% if cookiecutter.use_sentry_for_error_reporting == 'y' -%} +# raven +# ------------------------------------------------------------------------------ +# https://docs.sentry.io/clients/python/integrations/django/ +INSTALLED_APPS += ['raven.contrib.django.raven_compat'] # noqa F405 +MIDDLEWARE = ['raven.contrib.django.raven_compat.middleware.SentryResponseErrorIdMiddleware'] + MIDDLEWARE + +# Sentry +# ------------------------------------------------------------------------------ SENTRY_DSN = env('DJANGO_SENTRY_DSN') SENTRY_CLIENT = env('DJANGO_SENTRY_CLIENT', default='raven.contrib.django.raven_compat.DjangoClient') LOGGING = { @@ -205,7 +192,7 @@ LOGGING = { 'disable_existing_loggers': True, 'root': { 'level': 'WARNING', - 'handlers': ['sentry', ], + 'handlers': ['sentry'], }, 'formatters': { 'verbose': { @@ -227,38 +214,39 @@ LOGGING = { 'loggers': { 'django.db.backends': { 'level': 'ERROR', - 'handlers': ['console', ], + 'handlers': ['console'], 'propagate': False, }, 'raven': { 'level': 'DEBUG', - 'handlers': ['console', ], + 'handlers': ['console'], 'propagate': False, }, 'sentry.errors': { 'level': 'DEBUG', - 'handlers': ['console', ], + 'handlers': ['console'], 'propagate': False, }, 'django.security.DisallowedHost': { 'level': 'ERROR', - 'handlers': ['console', 'sentry', ], + 'handlers': ['console', 'sentry'], 'propagate': False, }, }, } + SENTRY_CELERY_LOGLEVEL = env.int('DJANGO_SENTRY_LOG_LEVEL', logging.INFO) RAVEN_CONFIG = { 'CELERY_LOGLEVEL': env.int('DJANGO_SENTRY_LOG_LEVEL', logging.INFO), 'DSN': SENTRY_DSN } -{% elif cookiecutter.use_sentry_for_error_reporting == 'n' %} -# LOGGING CONFIGURATION +{%- else %} +# LOGGING # ------------------------------------------------------------------------------ # See: https://docs.djangoproject.com/en/dev/ref/settings/#logging # A sample logging configuration. The only tangible logging # performed by this configuration is to send an email to -# the site admins on every HTTP 500 error when DEBUG=False. +# the site admins bon every HTTP 500 error when DEBUG=False. # See https://docs.djangoproject.com/en/dev/topics/logging for # more details on how to customize your logging configuration. LOGGING = { @@ -278,7 +266,7 @@ LOGGING = { 'handlers': { 'mail_admins': { 'level': 'ERROR', - 'filters': ['require_debug_false', ], + 'filters': ['require_debug_false'], 'class': 'django.utils.log.AdminEmailHandler' }, 'console': { @@ -289,20 +277,33 @@ LOGGING = { }, 'loggers': { 'django.request': { - 'handlers': ['mail_admins', ], + 'handlers': ['mail_admins'], 'level': 'ERROR', 'propagate': True }, 'django.security.DisallowedHost': { 'level': 'ERROR', - 'handlers': ['console', 'mail_admins', ], + 'handlers': ['console', 'mail_admins'], 'propagate': True } } } -{% endif %} -# Custom Admin URL, use {% raw %}{% url 'admin:index' %}{% endraw %} -ADMIN_URL = env('DJANGO_ADMIN_URL') -# Your production stuff: Below this line define 3rd party library settings +{%- endif %} +{% if cookiecutter.use_opbeat == 'y' -%} +# opbeat +# ------------------------------------------------------------------------------ +# https://opbeat.com/docs/articles/get-started-with-django/#setup +INSTALLED_APPS += ['opbeat.contrib.django'] # noqa F405 +# https://opbeat.com/docs/articles/get-started-with-django/#setup +OPBEAT = { + 'ORGANIZATION_ID': env('DJANGO_OPBEAT_ORGANIZATION_ID'), + 'APP_ID': env('DJANGO_OPBEAT_APP_ID'), + 'SECRET_TOKEN': env('DJANGO_OPBEAT_SECRET_TOKEN') +} +# https://opbeat.com/docs/articles/get-started-with-django/#performance-metrics +MIDDLEWARE = ['opbeat.contrib.django.middleware.OpbeatAPMMiddleware'] + MIDDLEWARE + +{%- endif %} +# Your stuff... # ------------------------------------------------------------------------------ diff --git a/{{cookiecutter.project_slug}}/config/settings/test.py b/{{cookiecutter.project_slug}}/config/settings/test.py index 01a412791..e206c2f5f 100644 --- a/{{cookiecutter.project_slug}}/config/settings/test.py +++ b/{{cookiecutter.project_slug}}/config/settings/test.py @@ -1,36 +1,22 @@ """ -Test settings for {{cookiecutter.project_name}} project. - -- Used to run tests fast on the continuous integration server and locally +With these settings, tests run faster. """ from .base import * # noqa +from .base import env - -# DEBUG +# GENERAL # ------------------------------------------------------------------------------ -# Turn debug off so tests run faster +# https://docs.djangoproject.com/en/dev/ref/settings/#debug DEBUG = False -TEMPLATES[0]['OPTIONS']['debug'] = False - -# SECRET CONFIGURATION -# ------------------------------------------------------------------------------ -# See: https://docs.djangoproject.com/en/dev/ref/settings/#secret-key -# Note: This key only used for development and testing. +# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key SECRET_KEY = env('DJANGO_SECRET_KEY', default='!!!SET DJANGO_SECRET_KEY!!!') +# https://docs.djangoproject.com/en/dev/ref/settings/#test-runner +TEST_RUNNER = 'django.test.runner.DiscoverRunner' -# Mail settings +# CACHES # ------------------------------------------------------------------------------ -EMAIL_HOST = 'localhost' -EMAIL_PORT = 1025 - -# In-memory email backend stores messages in django.core.mail.outbox -# for unit testing purposes -EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend' - -# CACHING -# ------------------------------------------------------------------------------ -# Speed advantages of in-memory caching without having to run Memcached +# https://docs.djangoproject.com/en/dev/ref/settings/#caches CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', @@ -38,24 +24,35 @@ CACHES = { } } -# TESTING +# PASSWORDS # ------------------------------------------------------------------------------ -TEST_RUNNER = 'django.test.runner.DiscoverRunner' - - -# PASSWORD HASHING -# ------------------------------------------------------------------------------ -# Use fast password hasher so tests run faster +# https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.MD5PasswordHasher', ] -# TEMPLATE LOADERS +# TEMPLATES # ------------------------------------------------------------------------------ -# Keep templates in memory so tests run faster -TEMPLATES[0]['OPTIONS']['loaders'] = [ - ['django.template.loaders.cached.Loader', [ - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', - ], ], +# https://docs.djangoproject.com/en/dev/ref/settings/#templates +TEMPLATES[0]['OPTIONS']['debug'] = DEBUG # noqa F405 +TEMPLATES[0]['OPTIONS']['loaders'] = [ # noqa F405 + ( + 'django.template.loaders.cached.Loader', + [ + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', + ], + ), ] + +# EMAIL +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend +EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend' +# https://docs.djangoproject.com/en/dev/ref/settings/#email-host +EMAIL_HOST = 'localhost' +# https://docs.djangoproject.com/en/dev/ref/settings/#email-port +EMAIL_PORT = 1025 + +# Your stuff... +# ------------------------------------------------------------------------------ diff --git a/{{cookiecutter.project_slug}}/env.example b/{{cookiecutter.project_slug}}/env.example index 1d28cb599..1ea342045 100644 --- a/{{cookiecutter.project_slug}}/env.example +++ b/{{cookiecutter.project_slug}}/env.example @@ -4,6 +4,9 @@ POSTGRES_PASSWORD=!!!SET POSTGRES_PASSWORD!!! POSTGRES_USER=!!!SET POSTGRES_USER!!! CONN_MAX_AGE= +# Gunicorn concurrency +WEB_CONCURRENCY=4 + # Domain name, used by caddy DOMAIN_NAME={{ cookiecutter.domain_name }} @@ -20,9 +23,9 @@ DJANGO_AWS_SECRET_ACCESS_KEY= DJANGO_AWS_STORAGE_BUCKET_NAME= # Used with email -DJANGO_MAILGUN_API_KEY= +MAILGUN_API_KEY= DJANGO_SERVER_EMAIL= -MAILGUN_SENDER_DOMAIN= +MAILGUN_DOMAIN= # Security! Better to use DNS for this task, but you can use redirect DJANGO_SECURE_SSL_REDIRECT=False diff --git a/{{cookiecutter.project_slug}}/locale/README.rst b/{{cookiecutter.project_slug}}/locale/README.rst new file mode 100644 index 000000000..c2f1dcd6f --- /dev/null +++ b/{{cookiecutter.project_slug}}/locale/README.rst @@ -0,0 +1,6 @@ +Translations +============ + +Translations will be placed in this folder when running:: + + python manage.py makemessages diff --git a/{{cookiecutter.project_slug}}/package.json b/{{cookiecutter.project_slug}}/package.json index a37d73bf5..acaae12ab 100644 --- a/{{cookiecutter.project_slug}}/package.json +++ b/{{cookiecutter.project_slug}}/package.json @@ -4,22 +4,22 @@ "dependencies": {}, "devDependencies": { {% if cookiecutter.js_task_runner == 'Grunt' %} - "autoprefixer-core": "~5.2.1", + "autoprefixer": "~8.1.0", {% if cookiecutter.custom_bootstrap_compilation == 'y' %} "bootstrap": "^4.0.0", {% endif %} - "connect-livereload": "~0.3.2", - "cssnano": "~2.1.0", - "grunt": "~0.4.5", + "connect-livereload": "~0.6.0", + "cssnano": "~3.10.0", + "grunt": "~1.0.2", "grunt-bg-shell": "~2.3.1", - "grunt-contrib-watch": "~0.6.1", - "grunt-postcss": "~0.5.5", - "grunt-sass": "~1.0.0", + "grunt-contrib-watch": "~1.0.0", + "grunt-postcss": "~0.9.0", + "grunt-sass": "~2.1.0", {% if cookiecutter.custom_bootstrap_compilation == 'y' %} "jquery": "^3.2.1-slim", {% endif %} "load-grunt-tasks": "~3.2.0", - "pixrem": "~1.3.1", + "pixrem": "~4.0.1", {% if cookiecutter.custom_bootstrap_compilation == 'y' %} "popper.js": "^1.12.3", {% endif %} @@ -31,26 +31,34 @@ "browser-sync": "^2.14.0", "del": "^2.2.2", "gulp": "^3.9.1", - "gulp-autoprefixer": "^3.1.1", + "gulp-autoprefixer": "^5.0.0", {% if cookiecutter.custom_bootstrap_compilation == 'y' %} "gulp-concat": "^2.6.1", {% endif %} "gulp-cssnano": "^2.1.2", - "gulp-imagemin": "^3.0.3", + "gulp-imagemin": "^4.1.0", "gulp-pixrem": "^1.0.0", "gulp-plumber": "^1.1.0", "gulp-rename": "^1.2.2", - "gulp-sass": "^2.3.2", - "gulp-uglify": "^2.0.0", + "gulp-sass": "^3.1.0", + "gulp-uglify": "^3.0.0", "gulp-util": "^3.0.7", {% if cookiecutter.custom_bootstrap_compilation == 'y' %} "jquery": "^3.2.1-slim", "popper.js": "^1.12.3", {% endif %} - "run-sequence": "^1.2.2" + "run-sequence": "^2.1.1" {% endif %} }, "engines": { "node": ">=0.8.0" + }, + "scripts": { + {% if cookiecutter.js_task_runner == 'Grunt' %} + "dev": "grunt serve" + {% elif cookiecutter.js_task_runner == 'Gulp' %} + "dev": "gulp" + {% endif %} + } } diff --git a/{{cookiecutter.project_slug}}/requirements.txt b/{{cookiecutter.project_slug}}/requirements.txt index d1197135e..c1b500c2b 100644 --- a/{{cookiecutter.project_slug}}/requirements.txt +++ b/{{cookiecutter.project_slug}}/requirements.txt @@ -1,3 +1,3 @@ -# This file is here because many Platforms as a Service look for -# requirements.txt in the root directory of a project. +# This file is expected by Heroku. + -r requirements/production.txt diff --git a/{{cookiecutter.project_slug}}/requirements/base.txt b/{{cookiecutter.project_slug}}/requirements/base.txt index 5bbeb5ec1..9cd9f30f1 100644 --- a/{{cookiecutter.project_slug}}/requirements/base.txt +++ b/{{cookiecutter.project_slug}}/requirements/base.txt @@ -1,12 +1,5 @@ -# Wheel 0.25+ needed to install certain packages on CPython 3.5+ -# like Pillow and psycopg2 -# See http://bitly.com/wheel-building-fails-CPython-35 -# Verified bug on Python 3.5.1 -wheel==0.30.0 - - # Conservative Django -django==2.0.2 # pyup: < 2.1 +django==2.0.3 # pyup: < 2.1 # Configuration django-environ==0.4.4 @@ -16,7 +9,7 @@ whitenoise==3.3.1 # Forms -django-crispy-forms==1.7.0 +django-crispy-forms==1.7.1 # Models django-model-utils==3.1.1 @@ -36,7 +29,7 @@ django-allauth==0.35.0 # from http://www.lfd.uci.edu/~gohlke/pythonlibs/#psycopg {% else %} # Python-PostgreSQL Database Adapter -psycopg2==2.7.4 +psycopg2==2.7.4 --no-binary psycopg2 {%- endif %} # Unicode slugification @@ -46,7 +39,7 @@ awesome-slugify==1.6.5 pytz==2018.3 # Redis support -django-redis==4.8.0 +django-redis==4.9.0 redis>=2.10.5 {% if cookiecutter.use_celery == "y" %} diff --git a/{{cookiecutter.project_slug}}/requirements/local.txt b/{{cookiecutter.project_slug}}/requirements/local.txt index 697a40288..93e9f741d 100644 --- a/{{cookiecutter.project_slug}}/requirements/local.txt +++ b/{{cookiecutter.project_slug}}/requirements/local.txt @@ -4,8 +4,8 @@ coverage==4.5.1 django-coverage-plugin==1.5.0 -Sphinx==1.7.0 -django-extensions==1.9.9 +Sphinx==1.7.1 +django-extensions==2.0.0 Werkzeug==0.14.1 django-test-plus==1.0.22 factory-boy==2.10.0 diff --git a/{{cookiecutter.project_slug}}/requirements/production.txt b/{{cookiecutter.project_slug}}/requirements/production.txt index 09151486d..351c46cef 100644 --- a/{{cookiecutter.project_slug}}/requirements/production.txt +++ b/{{cookiecutter.project_slug}}/requirements/production.txt @@ -6,7 +6,7 @@ # Python-PostgreSQL Database Adapter # Assuming Windows is used locally, and *nix -- in production. # ------------------------------------------------------------ -psycopg2==2.7.4 +psycopg2==2.7.4 --no-binary psycopg2 {%- endif %} # WSGI Handler @@ -16,7 +16,7 @@ gunicorn==19.7.1 # Static and Media Storage # ------------------------------------------------ -boto3==1.5.31 +boto3==1.6.2 # pyup: update minor django-storages==1.6.5 {% if cookiecutter.use_whitenoise != 'y' -%} Collectfast==0.6.0 @@ -29,7 +29,7 @@ django-anymail==1.4 {% if cookiecutter.use_sentry_for_error_reporting == "y" -%} # Raven is the Sentry client # -------------------------- -raven==6.5.0 +raven==6.6.0 {%- endif %} {% if cookiecutter.use_opbeat == "y" -%} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/taskapp/celery.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/taskapp/celery.py index 72513ed38..9827fa02a 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/taskapp/celery.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/taskapp/celery.py @@ -66,7 +66,7 @@ class CeleryConfig(AppConfig): try: opbeat_register_signal(opbeat_client) except Exception as e: - opbeat_logger.exception('Failed installing celery hook: %s' % e) + opbeat_logger.exception(f'Failed installing celery hook: {e}') if 'opbeat.contrib.django' in settings.INSTALLED_APPS: opbeat_register_handlers() @@ -75,7 +75,7 @@ class CeleryConfig(AppConfig): @app.task(bind=True) def debug_task(self): - print('Request: {0!r}'.format(self.request)) # pragma: no cover + print(f'Request: {self.request!r}') # pragma: no cover {% else %} # Use this as a starting point for your project with celery. # If you are not using celery, you can remove this app diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/base.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/base.html index a45de763c..c767cb3b9 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/base.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/base.html @@ -22,7 +22,7 @@ {% endraw %}{% if cookiecutter.use_compressor == "y" %}{% raw %}{% compress css %}{% endraw %}{% endif %}{% raw %} - {% endraw %}{% if cookiecutter.js_task_runner == "Gulp" %}{% raw %} + {% endraw %}{% if cookiecutter.js_task_runner == "Gulp" and cookiecutter.use_compressor == "n" %}{% raw %} {% endraw %}{% else %}{% raw %} diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/apps.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/apps.py index 8f5bb1d44..24c319df3 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/apps.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/apps.py @@ -10,4 +10,7 @@ class UsersConfig(AppConfig): Users system checks Users signal registration """ - pass + try: + import users.signals # noqa F401 + except ImportError: + pass diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/models.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/models.py index 9a831b48c..4b1a10d1e 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/models.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/models.py @@ -1,11 +1,9 @@ from django.contrib.auth.models import AbstractUser from django.db import models from django.urls import reverse -from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ -@python_2_unicode_compatible class User(AbstractUser): # First Name and Last Name do not cover name patterns diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/factories.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/factories.py index e2c967de7..a777ae9e5 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/factories.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/tests/factories.py @@ -2,8 +2,8 @@ import factory class UserFactory(factory.django.DjangoModelFactory): - username = factory.Sequence(lambda n: 'user-{0}'.format(n)) - email = factory.Sequence(lambda n: 'user-{0}@example.com'.format(n)) + username = factory.Sequence(lambda n: f'user-{n}') + email = factory.Sequence(lambda n: f'user-{n}@example.com') password = factory.PostGenerationMethodCall('set_password', 'password') class Meta: diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/urls.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/urls.py index 7c83fab98..1e1618363 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/urls.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/users/urls.py @@ -14,14 +14,14 @@ urlpatterns = [ view=views.UserRedirectView.as_view(), name='redirect' ), - url( - regex=r'^(?P[\w.@+-]+)/$', - view=views.UserDetailView.as_view(), - name='detail' - ), url( regex=r'^~update/$', view=views.UserUpdateView.as_view(), name='update' ), + url( + regex=r'^(?P[\w.@+-]+)/$', + view=views.UserDetailView.as_view(), + name='detail' + ), ]