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/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index a9a857733..96c05d042 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`_ @@ -159,6 +160,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 939ad4a9a..c5f9d06b3 100644 --- a/README.rst +++ b/README.rst @@ -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/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/requirements.txt b/requirements.txt index 085d5a147..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.4.1 + +# 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/tests/test_docker.sh b/tests/test_docker.sh index d80a091e4..384fdff2d 100755 --- a/tests/test_docker.sh +++ b/tests/test_docker.sh @@ -12,7 +12,7 @@ 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 @@ -21,4 +21,4 @@ docker-compose -f local.yml run django python manage.py test 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 --rm django python manage.py makemessages +docker-compose -f local.yml run django python manage.py makemessages diff --git a/tox.ini b/tox.ini index a4acdd159..18537c5a7 100644 --- a/tox.ini +++ b/tox.ini @@ -3,10 +3,9 @@ skipsdist = true envlist = py36 [testenv] -passenv = LC_ALL, LANG, HOME deps = binaryornot flake8==3.5.0 pytest-cookies sh -commands = py.test {posargs:tests} +commands = pytest {posargs:./tests} diff --git a/{{cookiecutter.project_slug}}/compose/production/django/gunicorn.sh b/{{cookiecutter.project_slug}}/compose/production/django/gunicorn.sh index 8846cafb2..39679f289 100644 --- a/{{cookiecutter.project_slug}}/compose/production/django/gunicorn.sh +++ b/{{cookiecutter.project_slug}}/compose/production/django/gunicorn.sh @@ -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 378f9f596..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(f'Loading : {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 c903795c7..1029f9f45 100644 --- a/{{cookiecutter.project_slug}}/config/settings/local.py +++ b/{{cookiecutter.project_slug}}/config/settings/local.py @@ -1,47 +1,20 @@ -""" -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 -# 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 +22,62 @@ CACHES = { } } +# TEMPLATES +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#templates +TEMPLATES[0]['OPTIONS']['debug'] = DEBUG + +# 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': - ip = socket.gethostbyname(socket.gethostname()) - INTERNAL_IPS += [ip[:-1] + '1'] -{% endif %} +# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#prerequisites +INSTALLED_APPS += ['debug_toolbar'] +# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#middleware +MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware'] +# 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': + ip = socket.gethostbyname(socket.gethostname()) + INTERNAL_IPS += [ip[:-1] + '1'] +{%- endif %} # django-extensions # ------------------------------------------------------------------------------ -INSTALLED_APPS += ['django_extensions', ] +# https://django-extensions.readthedocs.io/en/latest/installation_instructions.html#configuration +INSTALLED_APPS += ['django_extensions'] +{% 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 4404299fc..e6f948c37 100644 --- a/{{cookiecutter.project_slug}}/config/settings/production.py +++ b/{{cookiecutter.project_slug}}/config/settings/production.py @@ -1,199 +1,189 @@ -""" -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 -# 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 - -# 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 -# ------------------------------------------------------------------------------ -# 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 - -AWS_S3_OBJECT_PARAMETERS = { - 'CacheControl': f'max-age={AWS_EXPIRY}, s-maxage={AWS_EXPIRY}, must-revalidate', -} - -# URL that handles the media served from MEDIA_ROOT, used for managing -# stored files. -{% if cookiecutter.use_whitenoise == 'y' -%} -MEDIA_URL = f'https://s3.amazonaws.com/{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 = f'https://s3.amazonaws.com/{AWS_STORAGE_BUCKET_NAME}/media/' -{%- endif %} - -# Static Assets -# ------------------------ -{% if cookiecutter.use_whitenoise == 'y' -%} -STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' -{% else %} -STATIC_URL = f'https://s3.amazonaws.com/{AWS_STORAGE_BUCKET_NAME}/static/' -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 +DATABASES['default']['CONN_MAX_AGE'] = env.int('CONN_MAX_AGE', default=60) -# CACHING +# CACHES # ------------------------------------------------------------------------------ -REDIS_LOCATION = f'{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'] +# 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'] = [ + ( + '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'] +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'] + +{% if cookiecutter.use_whitenoise == 'y' -%} +# WhiteNoise +# ------------------------------------------------------------------------------ +# http://whitenoise.evans.io/en/latest/django.html#enable-whitenoise +MIDDLEWARE = ['whitenoise.middleware.WhiteNoiseMiddleware'] + MIDDLEWARE + +{%- 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 +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'] +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 = { @@ -201,7 +191,7 @@ LOGGING = { 'disable_existing_loggers': True, 'root': { 'level': 'WARNING', - 'handlers': ['sentry', ], + 'handlers': ['sentry'], }, 'formatters': { 'verbose': { @@ -223,38 +213,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 = { @@ -274,7 +265,7 @@ LOGGING = { 'handlers': { 'mail_admins': { 'level': 'ERROR', - 'filters': ['require_debug_false', ], + 'filters': ['require_debug_false'], 'class': 'django.utils.log.AdminEmailHandler' }, 'console': { @@ -285,20 +276,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'] +# 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..b46b8e423 100644 --- a/{{cookiecutter.project_slug}}/config/settings/test.py +++ b/{{cookiecutter.project_slug}}/config/settings/test.py @@ -1,36 +1,21 @@ """ -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 - -# 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 +23,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 +# https://docs.djangoproject.com/en/dev/ref/settings/#templates +TEMPLATES[0]['OPTIONS']['debug'] = DEBUG TEMPLATES[0]['OPTIONS']['loaders'] = [ - ['django.template.loaders.cached.Loader', [ - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', - ], ], + ( + '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}}/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 c9f784327..59ac7340f 100644 --- a/{{cookiecutter.project_slug}}/requirements/base.txt +++ b/{{cookiecutter.project_slug}}/requirements/base.txt @@ -1,10 +1,3 @@ -# 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 @@ -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 @@ -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/production.txt b/{{cookiecutter.project_slug}}/requirements/production.txt index 7577c7462..351c46cef 100644 --- a/{{cookiecutter.project_slug}}/requirements/production.txt +++ b/{{cookiecutter.project_slug}}/requirements/production.txt @@ -16,7 +16,7 @@ gunicorn==19.7.1 # Static and Media Storage # ------------------------------------------------ -boto3==1.5.36 +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}}/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' + ), ]