mirror of
https://github.com/cookiecutter/cookiecutter-django.git
synced 2025-08-09 14:34:53 +03:00
Merge branch 'master' into traefik
This commit is contained in:
commit
2f748a507f
17
.pyup.yml
Normal file
17
.pyup.yml
Normal file
|
@ -0,0 +1,17 @@
|
|||
# configure updates globally
|
||||
# default: all
|
||||
# allowed: all, insecure, False
|
||||
update: all
|
||||
|
||||
# configure dependency pinning globally
|
||||
# default: True
|
||||
# allowed: True, False
|
||||
pin: True
|
||||
|
||||
# Specify requirement files by hand, pyup seems to struggle to
|
||||
# find the ones in the project_slug folder
|
||||
requirements:
|
||||
- "requirements.txt"
|
||||
- "{{cookiecutter.project_slug}}/requirements/base.txt"
|
||||
- "{{cookiecutter.project_slug}}/requirements/local.txt"
|
||||
- "{{cookiecutter.project_slug}}/requirements/production.txt"
|
16
.travis.yml
16
.travis.yml
|
@ -7,16 +7,20 @@ language: python
|
|||
|
||||
python: 3.6
|
||||
|
||||
env:
|
||||
- TOX_ENV=py36
|
||||
|
||||
before_install:
|
||||
- docker-compose -v
|
||||
- docker -v
|
||||
|
||||
script:
|
||||
- tox -e $TOX_ENV
|
||||
- sh tests/test_docker.sh
|
||||
matrix:
|
||||
include:
|
||||
- name: Test
|
||||
script: tox -e py36
|
||||
- name: Black
|
||||
script: tox -e black
|
||||
- name: Basic Docker
|
||||
script: sh tests/test_docker.sh
|
||||
- name: Docker with Celery
|
||||
script: sh tests/test_docker.sh use_celery=y
|
||||
|
||||
install:
|
||||
- pip install tox
|
||||
|
|
|
@ -279,9 +279,9 @@ experience better.
|
|||
Articles
|
||||
---------
|
||||
|
||||
* `Using cookiecutter-django with Google Cloud Storage`_ - Mar. 12, 2019
|
||||
* `cookiecutter-django with Nginx, Route 53 and ELB`_ - Feb. 12, 2018
|
||||
* `cookiecutter-django and Amazon RDS`_ - Feb. 7, 2018
|
||||
* `Deploying Cookiecutter-Django with Docker-Compose`_ - Oct. 19, 2017
|
||||
* `Using Cookiecutter to Jumpstart a Django Project on Windows with PyCharm`_ - May 19, 2017
|
||||
* `Exploring with Cookiecutter`_ - Dec. 3, 2016
|
||||
* `Introduction to Cookiecutter-Django`_ - Feb. 19, 2016
|
||||
|
@ -292,9 +292,9 @@ Articles
|
|||
|
||||
Have a blog or online publication? Write about your cookiecutter-django tips and tricks, then send us a pull request with the link.
|
||||
|
||||
.. _`Using cookiecutter-django with Google Cloud Storage`: https://ahhda.github.io/cloud/gce/django/2019/03/12/using-django-cookiecutter-cloud-storage.html
|
||||
.. _`cookiecutter-django with Nginx, Route 53 and ELB`: https://msaizar.com/blog/cookiecutter-django-nginx-route-53-and-elb/
|
||||
.. _`cookiecutter-django and Amazon RDS`: https://msaizar.com/blog/cookiecutter-django-and-amazon-rds/
|
||||
.. _`Deploying Cookiecutter-Django with Docker-Compose`: http://adamantine.me/2017/10/19/deploying-cookiecutter-django-with-docker-compose/
|
||||
.. _`Exploring with Cookiecutter`: http://www.snowboardingcoder.com/django/2016/12/03/exploring-with-cookiecutter/
|
||||
.. _`Using Cookiecutter to Jumpstart a Django Project on Windows with PyCharm`: https://joshuahunter.com/posts/using-cookiecutter-to-jumpstart-a-django-project-on-windows-with-pycharm/
|
||||
|
||||
|
|
|
@ -48,7 +48,6 @@ Run these commands to deploy the project to Heroku:
|
|||
git push heroku master
|
||||
|
||||
heroku run python manage.py createsuperuser
|
||||
heroku run python manage.py collectstatic --no-input
|
||||
|
||||
heroku run python manage.py check --deploy
|
||||
|
||||
|
|
|
@ -32,10 +32,7 @@ DEBUG_VALUE = "debug"
|
|||
|
||||
|
||||
def remove_open_source_files():
|
||||
file_names = [
|
||||
"CONTRIBUTORS.txt",
|
||||
"LICENSE",
|
||||
]
|
||||
file_names = ["CONTRIBUTORS.txt", "LICENSE"]
|
||||
for file_name in file_names:
|
||||
os.remove(file_name)
|
||||
|
||||
|
@ -71,7 +68,10 @@ def remove_utility_files():
|
|||
def remove_heroku_files():
|
||||
file_names = ["Procfile", "runtime.txt", "requirements.txt"]
|
||||
for file_name in file_names:
|
||||
if file_name == "requirements.txt" and "{{ cookiecutter.use_travisci }}".lower() == "y":
|
||||
if (
|
||||
file_name == "requirements.txt"
|
||||
and "{{ cookiecutter.use_travisci }}".lower() == "y"
|
||||
):
|
||||
# don't remove the file if we are using travisci but not using heroku
|
||||
continue
|
||||
os.remove(file_name)
|
||||
|
@ -183,11 +183,7 @@ def generate_postgres_user(debug=False):
|
|||
|
||||
|
||||
def set_postgres_user(file_path, value):
|
||||
postgres_user = set_flag(
|
||||
file_path,
|
||||
"!!!SET POSTGRES_USER!!!",
|
||||
value=value,
|
||||
)
|
||||
postgres_user = set_flag(file_path, "!!!SET POSTGRES_USER!!!", value=value)
|
||||
return postgres_user
|
||||
|
||||
|
||||
|
@ -205,9 +201,7 @@ def set_postgres_password(file_path, value=None):
|
|||
|
||||
def set_celery_flower_user(file_path, value):
|
||||
celery_flower_user = set_flag(
|
||||
file_path,
|
||||
"!!!SET CELERY_FLOWER_USER!!!",
|
||||
value=value,
|
||||
file_path, "!!!SET CELERY_FLOWER_USER!!!", value=value
|
||||
)
|
||||
return celery_flower_user
|
||||
|
||||
|
@ -230,11 +224,7 @@ def append_to_gitignore_file(s):
|
|||
gitignore_file.write(os.linesep)
|
||||
|
||||
|
||||
def set_flags_in_envs(
|
||||
postgres_user,
|
||||
celery_flower_user,
|
||||
debug=False,
|
||||
):
|
||||
def set_flags_in_envs(postgres_user, celery_flower_user, debug=False):
|
||||
local_django_envs_path = os.path.join(".envs", ".local", ".django")
|
||||
production_django_envs_path = os.path.join(".envs", ".production", ".django")
|
||||
local_postgres_envs_path = os.path.join(".envs", ".local", ".postgres")
|
||||
|
@ -244,14 +234,22 @@ def set_flags_in_envs(
|
|||
set_django_admin_url(production_django_envs_path)
|
||||
|
||||
set_postgres_user(local_postgres_envs_path, value=postgres_user)
|
||||
set_postgres_password(local_postgres_envs_path, value=DEBUG_VALUE if debug else None)
|
||||
set_postgres_password(
|
||||
local_postgres_envs_path, value=DEBUG_VALUE if debug else None
|
||||
)
|
||||
set_postgres_user(production_postgres_envs_path, value=postgres_user)
|
||||
set_postgres_password(production_postgres_envs_path, value=DEBUG_VALUE if debug else None)
|
||||
set_postgres_password(
|
||||
production_postgres_envs_path, value=DEBUG_VALUE if debug else None
|
||||
)
|
||||
|
||||
set_celery_flower_user(local_django_envs_path, value=celery_flower_user)
|
||||
set_celery_flower_password(local_django_envs_path, value=DEBUG_VALUE if debug else None)
|
||||
set_celery_flower_password(
|
||||
local_django_envs_path, value=DEBUG_VALUE if debug else None
|
||||
)
|
||||
set_celery_flower_user(production_django_envs_path, value=celery_flower_user)
|
||||
set_celery_flower_password(production_django_envs_path, value=DEBUG_VALUE if debug else None)
|
||||
set_celery_flower_password(
|
||||
production_django_envs_path, value=DEBUG_VALUE if debug else None
|
||||
)
|
||||
|
||||
|
||||
def set_flags_in_settings_files():
|
||||
|
@ -302,8 +300,8 @@ def main():
|
|||
if "{{ cookiecutter.keep_local_envs_in_vcs }}".lower() == "y":
|
||||
print(
|
||||
INFO + ".env(s) are only utilized when Docker Compose and/or "
|
||||
"Heroku support is enabled so keeping them does not "
|
||||
"make sense given your current setup." + TERMINATOR
|
||||
"Heroku support is enabled so keeping them does not "
|
||||
"make sense given your current setup." + TERMINATOR
|
||||
)
|
||||
remove_envs_and_associated_files()
|
||||
else:
|
||||
|
@ -325,10 +323,10 @@ def main():
|
|||
"{{ cookiecutter.js_task_runner }}".lower().capitalize()
|
||||
)
|
||||
+ "working together not supported yet. "
|
||||
"You can continue using the generated project like you "
|
||||
"normally would, however you would need to add a JS "
|
||||
"task runner service to your Docker Compose configuration "
|
||||
"manually." + TERMINATOR
|
||||
"You can continue using the generated project like you "
|
||||
"normally would, however you would need to add a JS "
|
||||
"task runner service to your Docker Compose configuration "
|
||||
"manually." + TERMINATOR
|
||||
)
|
||||
|
||||
if "{{ cookiecutter.use_celery }}".lower() == "n":
|
||||
|
|
|
@ -18,11 +18,13 @@ SUCCESS = "\x1b[1;32m [SUCCESS]: "
|
|||
|
||||
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 (
|
||||
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."
|
||||
assert (
|
||||
"\\" not in "{{ cookiecutter.author_name }}"
|
||||
), "Don't include backslashes in author name."
|
||||
|
||||
if "{{ cookiecutter.use_docker }}".lower() == "n":
|
||||
python_major_version = sys.version_info[0]
|
||||
|
|
|
@ -9,6 +9,6 @@ flake8==3.7.6
|
|||
# Testing
|
||||
# ------------------------------------------------------------------------------
|
||||
tox==3.6.1
|
||||
pytest==4.3.0
|
||||
pytest==4.3.1
|
||||
pytest-cookies==0.3.0
|
||||
pyyaml==3.13
|
||||
pyyaml==5.1
|
||||
|
|
|
@ -97,8 +97,8 @@ def test_travis_invokes_pytest(cookies, context):
|
|||
assert result.project.basename == context["project_slug"]
|
||||
assert result.project.isdir()
|
||||
|
||||
with open(f'{result.project}/.travis.yml', 'r') as travis_yml:
|
||||
with open(f"{result.project}/.travis.yml", "r") as travis_yml:
|
||||
try:
|
||||
assert yaml.load(travis_yml)['script'] == ['pytest']
|
||||
assert yaml.load(travis_yml)["script"] == ["pytest"]
|
||||
except yaml.YAMLError as e:
|
||||
pytest.fail(e)
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
# it is meant to be run from the root directory of the repository, eg:
|
||||
# sh tests/test_docker.sh
|
||||
|
||||
set -o errexit
|
||||
|
||||
# install test requirements
|
||||
pip install -r requirements.txt
|
||||
|
||||
|
@ -11,12 +13,15 @@ mkdir -p .cache/docker
|
|||
cd .cache/docker
|
||||
|
||||
# create the project using the default settings in cookiecutter.json
|
||||
cookiecutter ../../ --no-input --overwrite-if-exists use_docker=y
|
||||
cookiecutter ../../ --no-input --overwrite-if-exists use_docker=y $@
|
||||
cd my_awesome_project
|
||||
|
||||
# run the project's type checks
|
||||
docker-compose -f local.yml run django mypy my_awesome_project
|
||||
|
||||
# Run black with --check option
|
||||
docker-compose -f local.yml run django black --check --diff --exclude 'migrations' ./
|
||||
|
||||
# run the project's tests
|
||||
docker-compose -f local.yml run django pytest
|
||||
|
||||
|
|
6
tox.ini
6
tox.ini
|
@ -1,7 +1,11 @@
|
|||
[tox]
|
||||
skipsdist = true
|
||||
envlist = py36
|
||||
envlist = py36,black
|
||||
|
||||
[testenv]
|
||||
deps = -rrequirements.txt
|
||||
commands = pytest {posargs:./tests}
|
||||
|
||||
[testenv:black]
|
||||
deps = black
|
||||
commands = black --check hooks tests setup.py docs
|
||||
|
|
|
@ -4,27 +4,29 @@ 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 }}')
|
||||
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 }}")
|
||||
|
||||
env = environ.Env()
|
||||
|
||||
READ_DOT_ENV_FILE = env.bool('DJANGO_READ_DOT_ENV_FILE', default=False)
|
||||
READ_DOT_ENV_FILE = env.bool("DJANGO_READ_DOT_ENV_FILE", default=False)
|
||||
if READ_DOT_ENV_FILE:
|
||||
# OS environment variables take precedence over variables from .env
|
||||
env.read_env(str(ROOT_DIR.path('.env')))
|
||||
env.read_env(str(ROOT_DIR.path(".env")))
|
||||
|
||||
# GENERAL
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#debug
|
||||
DEBUG = env.bool('DJANGO_DEBUG', False)
|
||||
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 }}'
|
||||
TIME_ZONE = "{{ cookiecutter.timezone }}"
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#language-code
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
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
|
||||
|
@ -37,45 +39,45 @@ USE_TZ = True
|
|||
# DATABASES
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#databases
|
||||
{% if cookiecutter.use_docker == 'y' -%}
|
||||
DATABASES = {
|
||||
'default': env.db('DATABASE_URL'),
|
||||
}
|
||||
{% 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}}'),
|
||||
"default": env.db(
|
||||
"DATABASE_URL", default="postgres://{% if cookiecutter.windows == 'y' %}localhost{% endif %}/{{cookiecutter.project_slug}}"
|
||||
),
|
||||
}
|
||||
{%- endif %}
|
||||
DATABASES['default']['ATOMIC_REQUESTS'] = True
|
||||
DATABASES["default"]["ATOMIC_REQUESTS"] = True
|
||||
|
||||
# URLS
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf
|
||||
ROOT_URLCONF = 'config.urls'
|
||||
ROOT_URLCONF = "config.urls"
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application
|
||||
WSGI_APPLICATION = 'config.wsgi.application'
|
||||
WSGI_APPLICATION = "config.wsgi.application"
|
||||
|
||||
# APPS
|
||||
# ------------------------------------------------------------------------------
|
||||
DJANGO_APPS = [
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.sites',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
# 'django.contrib.humanize', # Handy template tags
|
||||
'django.contrib.admin',
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.sites",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
# "django.contrib.humanize", # Handy template tags
|
||||
"django.contrib.admin",
|
||||
]
|
||||
THIRD_PARTY_APPS = [
|
||||
'crispy_forms',
|
||||
'allauth',
|
||||
'allauth.account',
|
||||
'allauth.socialaccount',
|
||||
'rest_framework',
|
||||
"crispy_forms",
|
||||
"allauth",
|
||||
"allauth.account",
|
||||
"allauth.socialaccount",
|
||||
"rest_framework",
|
||||
]
|
||||
LOCAL_APPS = [
|
||||
'{{ cookiecutter.project_slug }}.users.apps.UsersAppConfig',
|
||||
"{{ cookiecutter.project_slug }}.users.apps.UsersAppConfig",
|
||||
# Your stuff: custom apps go here
|
||||
]
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
|
||||
|
@ -84,86 +86,76 @@ INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
|
|||
# MIGRATIONS
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#migration-modules
|
||||
MIGRATION_MODULES = {
|
||||
'sites': '{{ cookiecutter.project_slug }}.contrib.sites.migrations'
|
||||
}
|
||||
MIGRATION_MODULES = {"sites": "{{ cookiecutter.project_slug }}.contrib.sites.migrations"}
|
||||
|
||||
# AUTHENTICATION
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#authentication-backends
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
'allauth.account.auth_backends.AuthenticationBackend',
|
||||
"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'
|
||||
AUTH_USER_MODEL = "users.User"
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#login-redirect-url
|
||||
LOGIN_REDIRECT_URL = 'users:redirect'
|
||||
LOGIN_REDIRECT_URL = "users:redirect"
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#login-url
|
||||
LOGIN_URL = 'account_login'
|
||||
LOGIN_URL = "account_login"
|
||||
|
||||
# PASSWORDS
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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',
|
||||
"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",
|
||||
]
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#auth-password-validators
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"
|
||||
},
|
||||
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
|
||||
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
|
||||
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
|
||||
]
|
||||
|
||||
# MIDDLEWARE
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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',
|
||||
"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",
|
||||
]
|
||||
|
||||
# STATIC
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#static-root
|
||||
STATIC_ROOT = str(ROOT_DIR('staticfiles'))
|
||||
STATIC_ROOT = str(ROOT_DIR("staticfiles"))
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#static-url
|
||||
STATIC_URL = '/static/'
|
||||
STATIC_URL = "/static/"
|
||||
# https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS
|
||||
STATICFILES_DIRS = [
|
||||
str(APPS_DIR.path('static')),
|
||||
]
|
||||
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',
|
||||
"django.contrib.staticfiles.finders.FileSystemFinder",
|
||||
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
||||
]
|
||||
|
||||
# MEDIA
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#media-root
|
||||
MEDIA_ROOT = str(APPS_DIR('media'))
|
||||
MEDIA_ROOT = str(APPS_DIR("media"))
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#media-url
|
||||
MEDIA_URL = '/media/'
|
||||
MEDIA_URL = "/media/"
|
||||
|
||||
# TEMPLATES
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -171,43 +163,39 @@ MEDIA_URL = '/media/'
|
|||
TEMPLATES = [
|
||||
{
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-TEMPLATES-BACKEND
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#template-dirs
|
||||
'DIRS': [
|
||||
str(APPS_DIR.path('templates')),
|
||||
],
|
||||
'OPTIONS': {
|
||||
"DIRS": [str(APPS_DIR.path("templates"))],
|
||||
"OPTIONS": {
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#template-debug
|
||||
'debug': 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',
|
||||
"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',
|
||||
"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'
|
||||
CRISPY_TEMPLATE_PACK = "bootstrap4"
|
||||
|
||||
# FIXTURES
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#fixture-dirs
|
||||
FIXTURE_DIRS = (
|
||||
str(APPS_DIR.path('fixtures')),
|
||||
)
|
||||
FIXTURE_DIRS = (str(APPS_DIR.path("fixtures")),)
|
||||
|
||||
# SECURITY
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -218,41 +206,41 @@ CSRF_COOKIE_HTTPONLY = 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'
|
||||
X_FRAME_OPTIONS = "DENY"
|
||||
|
||||
# EMAIL
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
|
||||
EMAIL_BACKEND = env('DJANGO_EMAIL_BACKEND', default='django.core.mail.backends.smtp.EmailBackend')
|
||||
EMAIL_BACKEND = env(
|
||||
"DJANGO_EMAIL_BACKEND", default="django.core.mail.backends.smtp.EmailBackend"
|
||||
)
|
||||
|
||||
# ADMIN
|
||||
# ------------------------------------------------------------------------------
|
||||
# Django Admin URL.
|
||||
ADMIN_URL = 'admin/'
|
||||
ADMIN_URL = "admin/"
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#admins
|
||||
ADMINS = [
|
||||
("""{{cookiecutter.author_name}}""", '{{cookiecutter.email}}'),
|
||||
]
|
||||
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.CeleryAppConfig']
|
||||
INSTALLED_APPS += ["{{cookiecutter.project_slug}}.taskapp.celery.CeleryAppConfig"]
|
||||
if USE_TZ:
|
||||
# http://docs.celeryproject.org/en/latest/userguide/configuration.html#std:setting-timezone
|
||||
CELERY_TIMEZONE = TIME_ZONE
|
||||
# http://docs.celeryproject.org/en/latest/userguide/configuration.html#std:setting-broker_url
|
||||
CELERY_BROKER_URL = env('CELERY_BROKER_URL')
|
||||
CELERY_BROKER_URL = env("CELERY_BROKER_URL")
|
||||
# http://docs.celeryproject.org/en/latest/userguide/configuration.html#std:setting-result_backend
|
||||
CELERY_RESULT_BACKEND = CELERY_BROKER_URL
|
||||
# http://docs.celeryproject.org/en/latest/userguide/configuration.html#std:setting-accept_content
|
||||
CELERY_ACCEPT_CONTENT = ['json']
|
||||
CELERY_ACCEPT_CONTENT = ["json"]
|
||||
# http://docs.celeryproject.org/en/latest/userguide/configuration.html#std:setting-task_serializer
|
||||
CELERY_TASK_SERIALIZER = 'json'
|
||||
CELERY_TASK_SERIALIZER = "json"
|
||||
# http://docs.celeryproject.org/en/latest/userguide/configuration.html#std:setting-result_serializer
|
||||
CELERY_RESULT_SERIALIZER = 'json'
|
||||
CELERY_RESULT_SERIALIZER = "json"
|
||||
# http://docs.celeryproject.org/en/latest/userguide/configuration.html#task-time-limit
|
||||
# TODO: set to whatever value is adequate in your circumstances
|
||||
CELERYD_TASK_TIME_LIMIT = 5 * 60
|
||||
|
@ -263,24 +251,24 @@ CELERYD_TASK_SOFT_TIME_LIMIT = 60
|
|||
{%- endif %}
|
||||
# django-allauth
|
||||
# ------------------------------------------------------------------------------
|
||||
ACCOUNT_ALLOW_REGISTRATION = env.bool('DJANGO_ACCOUNT_ALLOW_REGISTRATION', True)
|
||||
ACCOUNT_ALLOW_REGISTRATION = env.bool("DJANGO_ACCOUNT_ALLOW_REGISTRATION", True)
|
||||
# https://django-allauth.readthedocs.io/en/latest/configuration.html
|
||||
ACCOUNT_AUTHENTICATION_METHOD = 'username'
|
||||
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'
|
||||
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
|
||||
# https://django-allauth.readthedocs.io/en/latest/configuration.html
|
||||
ACCOUNT_ADAPTER = '{{cookiecutter.project_slug}}.users.adapters.AccountAdapter'
|
||||
ACCOUNT_ADAPTER = "{{cookiecutter.project_slug}}.users.adapters.AccountAdapter"
|
||||
# https://django-allauth.readthedocs.io/en/latest/configuration.html
|
||||
SOCIALACCOUNT_ADAPTER = '{{cookiecutter.project_slug}}.users.adapters.SocialAccountAdapter'
|
||||
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']
|
||||
INSTALLED_APPS += ["compressor"]
|
||||
STATICFILES_FINDERS += ["compressor.finders.CompressorFinder"]
|
||||
|
||||
{%- endif %}
|
||||
# Your stuff...
|
||||
|
|
|
@ -6,42 +6,43 @@ from .base import env
|
|||
# https://docs.djangoproject.com/en/dev/ref/settings/#debug
|
||||
DEBUG = True
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
|
||||
SECRET_KEY = env('DJANGO_SECRET_KEY', default='!!!SET DJANGO_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",
|
||||
"127.0.0.1",
|
||||
]
|
||||
ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1"]
|
||||
|
||||
# CACHES
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#caches
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
'LOCATION': ''
|
||||
"default": {
|
||||
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
||||
"LOCATION": "",
|
||||
}
|
||||
}
|
||||
|
||||
# TEMPLATES
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#templates
|
||||
TEMPLATES[0]['OPTIONS']['debug'] = DEBUG # noqa F405
|
||||
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')
|
||||
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'
|
||||
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')
|
||||
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'
|
||||
EMAIL_HOST = "localhost"
|
||||
{%- endif %}
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#email-port
|
||||
EMAIL_PORT = 1025
|
||||
|
@ -49,29 +50,28 @@ EMAIL_PORT = 1025
|
|||
# django-debug-toolbar
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#prerequisites
|
||||
INSTALLED_APPS += ['debug_toolbar'] # noqa F405
|
||||
INSTALLED_APPS += ["debug_toolbar"] # noqa F405
|
||||
# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#middleware
|
||||
MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware'] # noqa F405
|
||||
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,
|
||||
"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']
|
||||
INTERNAL_IPS = ["127.0.0.1", "10.0.2.2"]
|
||||
{% if cookiecutter.use_docker == 'y' -%}
|
||||
if env('USE_DOCKER') == 'yes':
|
||||
if env("USE_DOCKER") == "yes":
|
||||
import socket
|
||||
|
||||
hostname, _, ips = socket.gethostbyname_ex(socket.gethostname())
|
||||
INTERNAL_IPS += [ip[:-1] + '1' for ip in ips]
|
||||
INTERNAL_IPS += [ip[:-1] + "1" for ip in ips]
|
||||
{%- endif %}
|
||||
|
||||
# django-extensions
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://django-extensions.readthedocs.io/en/latest/installation_instructions.html#configuration
|
||||
INSTALLED_APPS += ['django_extensions'] # noqa F405
|
||||
INSTALLED_APPS += ["django_extensions"] # noqa F405
|
||||
{% if cookiecutter.use_celery == 'y' -%}
|
||||
|
||||
# Celery
|
||||
|
|
|
@ -8,37 +8,37 @@ from .base import env
|
|||
# GENERAL
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
|
||||
SECRET_KEY = env('DJANGO_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 }}'])
|
||||
ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=["{{ cookiecutter.domain_name }}"])
|
||||
|
||||
# DATABASES
|
||||
# ------------------------------------------------------------------------------
|
||||
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
|
||||
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
|
||||
|
||||
# CACHES
|
||||
# ------------------------------------------------------------------------------
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django_redis.cache.RedisCache',
|
||||
'LOCATION': env('REDIS_URL'),
|
||||
'OPTIONS': {
|
||||
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
|
||||
"default": {
|
||||
"BACKEND": "django_redis.cache.RedisCache",
|
||||
"LOCATION": env("REDIS_URL"),
|
||||
"OPTIONS": {
|
||||
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
||||
# Mimicing memcache behavior.
|
||||
# http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior
|
||||
'IGNORE_EXCEPTIONS': True,
|
||||
}
|
||||
"IGNORE_EXCEPTIONS": True,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
# SECURITY
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#secure-proxy-ssl-header
|
||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
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)
|
||||
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/#csrf-cookie-secure
|
||||
|
@ -48,45 +48,49 @@ CSRF_COOKIE_SECURE = True
|
|||
# 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)
|
||||
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)
|
||||
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)
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = env.bool(
|
||||
"DJANGO_SECURE_CONTENT_TYPE_NOSNIFF", default=True
|
||||
)
|
||||
|
||||
# STORAGES
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://django-storages.readthedocs.io/en/latest/#installation
|
||||
INSTALLED_APPS += ['storages'] # noqa F405
|
||||
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')
|
||||
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')
|
||||
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')
|
||||
AWS_STORAGE_BUCKET_NAME = env("DJANGO_AWS_STORAGE_BUCKET_NAME")
|
||||
# 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',
|
||||
"CacheControl": f"max-age={_AWS_EXPIRY}, s-maxage={_AWS_EXPIRY}, must-revalidate"
|
||||
}
|
||||
|
||||
# STATIC
|
||||
# ------------------------
|
||||
{% if cookiecutter.use_whitenoise == 'y' -%}
|
||||
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
|
||||
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
||||
{%- else %}
|
||||
STATICFILES_STORAGE = 'config.settings.production.StaticRootS3Boto3Storage'
|
||||
STATIC_URL = f'https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/static/'
|
||||
STATICFILES_STORAGE = "config.settings.production.StaticRootS3Boto3Storage"
|
||||
STATIC_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/static/"
|
||||
{%- endif %}
|
||||
|
||||
# MEDIA
|
||||
# ------------------------------------------------------------------------------
|
||||
{% if cookiecutter.use_whitenoise == 'y' -%}
|
||||
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
|
||||
MEDIA_URL = f'https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/'
|
||||
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
|
||||
MEDIA_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/"
|
||||
{%- else %}
|
||||
# region http://stackoverflow.com/questions/10390244/
|
||||
# Full-fledge class: https://stackoverflow.com/a/18046120/104731
|
||||
|
@ -94,78 +98,79 @@ from storages.backends.s3boto3 import S3Boto3Storage # noqa E402
|
|||
|
||||
|
||||
class StaticRootS3Boto3Storage(S3Boto3Storage):
|
||||
location = 'static'
|
||||
location = "static"
|
||||
|
||||
|
||||
class MediaRootS3Boto3Storage(S3Boto3Storage):
|
||||
location = 'media'
|
||||
location = "media"
|
||||
file_overwrite = False
|
||||
|
||||
|
||||
# endregion
|
||||
DEFAULT_FILE_STORAGE = 'config.settings.production.MediaRootS3Boto3Storage'
|
||||
MEDIA_URL = f'https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/media/'
|
||||
DEFAULT_FILE_STORAGE = "config.settings.production.MediaRootS3Boto3Storage"
|
||||
MEDIA_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/media/"
|
||||
{%- endif %}
|
||||
|
||||
# TEMPLATES
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#templates
|
||||
TEMPLATES[0]['OPTIONS']['loaders'] = [ # noqa F405
|
||||
TEMPLATES[0]["OPTIONS"]["loaders"] = [ # noqa F405
|
||||
(
|
||||
'django.template.loaders.cached.Loader',
|
||||
"django.template.loaders.cached.Loader",
|
||||
[
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
'django.template.loaders.app_directories.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}} <noreply@{{cookiecutter.domain_name}}>'
|
||||
"DJANGO_DEFAULT_FROM_EMAIL", default="{{cookiecutter.project_name}} <noreply@{{cookiecutter.domain_name}}>"
|
||||
)
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#server-email
|
||||
SERVER_EMAIL = env('DJANGO_SERVER_EMAIL', default=DEFAULT_FROM_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}}]')
|
||||
EMAIL_SUBJECT_PREFIX = env(
|
||||
"DJANGO_EMAIL_SUBJECT_PREFIX", default="[{{cookiecutter.project_name}}]"
|
||||
)
|
||||
|
||||
# ADMIN
|
||||
# ------------------------------------------------------------------------------
|
||||
# Django Admin URL regex.
|
||||
ADMIN_URL = env('DJANGO_ADMIN_URL')
|
||||
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'
|
||||
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')
|
||||
"MAILGUN_API_KEY": env("MAILGUN_API_KEY"),
|
||||
"MAILGUN_SENDER_DOMAIN": env("MAILGUN_DOMAIN"),
|
||||
}
|
||||
|
||||
# Gunicorn
|
||||
# ------------------------------------------------------------------------------
|
||||
INSTALLED_APPS += ['gunicorn'] # noqa F405
|
||||
INSTALLED_APPS += ["gunicorn"] # noqa F405
|
||||
|
||||
{% if cookiecutter.use_whitenoise == 'y' -%}
|
||||
# WhiteNoise
|
||||
# ------------------------------------------------------------------------------
|
||||
# http://whitenoise.evans.io/en/latest/django.html#enable-whitenoise
|
||||
MIDDLEWARE.insert(1, 'whitenoise.middleware.WhiteNoiseMiddleware') # noqa F405
|
||||
MIDDLEWARE.insert(1, "whitenoise.middleware.WhiteNoiseMiddleware") # 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)
|
||||
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'
|
||||
COMPRESS_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
|
||||
# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_URL
|
||||
COMPRESS_URL = STATIC_URL
|
||||
|
||||
|
@ -174,7 +179,7 @@ COMPRESS_URL = STATIC_URL
|
|||
# Collectfast
|
||||
# ------------------------------------------------------------------------------
|
||||
# https://github.com/antonagestam/collectfast#installation
|
||||
INSTALLED_APPS = ['collectfast'] + INSTALLED_APPS # noqa F405
|
||||
INSTALLED_APPS = ["collectfast"] + INSTALLED_APPS # noqa F405
|
||||
AWS_PRELOAD_METADATA = True
|
||||
|
||||
{% endif %}
|
||||
|
@ -182,64 +187,64 @@ AWS_PRELOAD_METADATA = True
|
|||
# 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
|
||||
INSTALLED_APPS += ["raven.contrib.django.raven_compat"] # noqa F405
|
||||
MIDDLEWARE = ["raven.contrib.django.raven_compat.middleware.SentryResponseErrorIdMiddleware"] + MIDDLEWARE
|
||||
|
||||
# Sentry
|
||||
# ------------------------------------------------------------------------------
|
||||
SENTRY_DSN = env('SENTRY_DSN')
|
||||
SENTRY_CLIENT = env('DJANGO_SENTRY_CLIENT', default='raven.contrib.django.raven_compat.DjangoClient')
|
||||
SENTRY_DSN = env("SENTRY_DSN")
|
||||
SENTRY_CLIENT = env("DJANGO_SENTRY_CLIENT", default="raven.contrib.django.raven_compat.DjangoClient")
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': True,
|
||||
'root': {
|
||||
'level': 'WARNING',
|
||||
'handlers': ['sentry'],
|
||||
"version": 1,
|
||||
"disable_existing_loggers": True,
|
||||
"root": {
|
||||
"level": "WARNING",
|
||||
"handlers": ["sentry"],
|
||||
},
|
||||
'formatters': {
|
||||
'verbose': {
|
||||
'format': '%(levelname)s %(asctime)s %(module)s '
|
||||
'%(process)d %(thread)d %(message)s'
|
||||
},
|
||||
},
|
||||
'handlers': {
|
||||
'sentry': {
|
||||
'level': 'ERROR',
|
||||
'class': 'raven.contrib.django.raven_compat.handlers.SentryHandler',
|
||||
},
|
||||
'console': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'verbose'
|
||||
"formatters": {
|
||||
"verbose": {
|
||||
"format": "%(levelname)s %(asctime)s %(module)s "
|
||||
"%(process)d %(thread)d %(message)s"
|
||||
}
|
||||
},
|
||||
'loggers': {
|
||||
'django.db.backends': {
|
||||
'level': 'ERROR',
|
||||
'handlers': ['console'],
|
||||
'propagate': False,
|
||||
"handlers": {
|
||||
"sentry": {
|
||||
"level": "ERROR",
|
||||
"class": "raven.contrib.django.raven_compat.handlers.SentryHandler",
|
||||
},
|
||||
'raven': {
|
||||
'level': 'DEBUG',
|
||||
'handlers': ['console'],
|
||||
'propagate': False,
|
||||
"console": {
|
||||
"level": "DEBUG",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "verbose",
|
||||
},
|
||||
'sentry.errors': {
|
||||
'level': 'DEBUG',
|
||||
'handlers': ['console'],
|
||||
'propagate': False,
|
||||
},
|
||||
"loggers": {
|
||||
"django.db.backends": {
|
||||
"level": "ERROR",
|
||||
"handlers": ["console"],
|
||||
"propagate": False,
|
||||
},
|
||||
'django.security.DisallowedHost': {
|
||||
'level': 'ERROR',
|
||||
'handlers': ['console', 'sentry'],
|
||||
'propagate': False,
|
||||
"raven": {
|
||||
"level": "DEBUG",
|
||||
"handlers": ["console"],
|
||||
"propagate": False,
|
||||
},
|
||||
"sentry.errors": {
|
||||
"level": "DEBUG",
|
||||
"handlers": ["console"],
|
||||
"propagate": False,
|
||||
},
|
||||
"django.security.DisallowedHost": {
|
||||
"level": "ERROR",
|
||||
"handlers": ["console", "sentry"],
|
||||
"propagate": False,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
SENTRY_CELERY_LOGLEVEL = env.int('DJANGO_SENTRY_LOG_LEVEL', logging.INFO)
|
||||
SENTRY_CELERY_LOGLEVEL = env.int("DJANGO_SENTRY_LOG_LEVEL", logging.INFO)
|
||||
RAVEN_CONFIG = {
|
||||
'dsn': SENTRY_DSN
|
||||
"dsn": SENTRY_DSN
|
||||
}
|
||||
|
||||
{%- else %}
|
||||
|
@ -252,43 +257,39 @@ RAVEN_CONFIG = {
|
|||
# See https://docs.djangoproject.com/en/dev/topics/logging for
|
||||
# more details on how to customize your logging configuration.
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'filters': {
|
||||
'require_debug_false': {
|
||||
'()': 'django.utils.log.RequireDebugFalse'
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}},
|
||||
"formatters": {
|
||||
"verbose": {
|
||||
"format": "%(levelname)s %(asctime)s %(module)s "
|
||||
"%(process)d %(thread)d %(message)s"
|
||||
}
|
||||
},
|
||||
'formatters': {
|
||||
'verbose': {
|
||||
'format': '%(levelname)s %(asctime)s %(module)s '
|
||||
'%(process)d %(thread)d %(message)s'
|
||||
"handlers": {
|
||||
"mail_admins": {
|
||||
"level": "ERROR",
|
||||
"filters": ["require_debug_false"],
|
||||
"class": "django.utils.log.AdminEmailHandler",
|
||||
},
|
||||
"console": {
|
||||
"level": "DEBUG",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "verbose",
|
||||
},
|
||||
},
|
||||
'handlers': {
|
||||
'mail_admins': {
|
||||
'level': 'ERROR',
|
||||
'filters': ['require_debug_false'],
|
||||
'class': 'django.utils.log.AdminEmailHandler'
|
||||
"loggers": {
|
||||
"django.request": {
|
||||
"handlers": ["mail_admins"],
|
||||
"level": "ERROR",
|
||||
"propagate": True,
|
||||
},
|
||||
'console': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'verbose',
|
||||
"django.security.DisallowedHost": {
|
||||
"level": "ERROR",
|
||||
"handlers": ["console", "mail_admins"],
|
||||
"propagate": True,
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
'django.request': {
|
||||
'handlers': ['mail_admins'],
|
||||
'level': 'ERROR',
|
||||
'propagate': True
|
||||
},
|
||||
'django.security.DisallowedHost': {
|
||||
'level': 'ERROR',
|
||||
'handlers': ['console', 'mail_admins'],
|
||||
'propagate': True
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
|
|
|
@ -10,7 +10,10 @@ from .base import env
|
|||
# https://docs.djangoproject.com/en/dev/ref/settings/#debug
|
||||
DEBUG = False
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
|
||||
SECRET_KEY = env("DJANGO_SECRET_KEY", default="!!!SET DJANGO_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"
|
||||
|
||||
|
@ -19,7 +22,8 @@ TEST_RUNNER = "django.test.runner.DiscoverRunner"
|
|||
# https://docs.djangoproject.com/en/dev/ref/settings/#caches
|
||||
CACHES = {
|
||||
"default": {
|
||||
"BACKEND": "django.core.cache.backends.locmem.LocMemCache", "LOCATION": ""
|
||||
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
||||
"LOCATION": "",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,22 +8,15 @@ from django.views import defaults as default_views
|
|||
urlpatterns = [
|
||||
path("", TemplateView.as_view(template_name="pages/home.html"), name="home"),
|
||||
path(
|
||||
"about/",
|
||||
TemplateView.as_view(template_name="pages/about.html"),
|
||||
name="about",
|
||||
"about/", TemplateView.as_view(template_name="pages/about.html"), name="about"
|
||||
),
|
||||
# Django Admin, use {% raw %}{% url 'admin:index' %}{% endraw %}
|
||||
path(settings.ADMIN_URL, admin.site.urls),
|
||||
# User management
|
||||
path(
|
||||
"users/",
|
||||
include("{{ cookiecutter.project_slug }}.users.urls", namespace="users"),
|
||||
),
|
||||
path("users/", include("{{ cookiecutter.project_slug }}.users.urls", namespace="users")),
|
||||
path("accounts/", include("allauth.urls")),
|
||||
# Your stuff: custom urls includes go here
|
||||
] + static(
|
||||
settings.MEDIA_URL, document_root=settings.MEDIA_ROOT
|
||||
)
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
if settings.DEBUG:
|
||||
# This allows the error pages to be debugged during development, just visit
|
||||
|
|
|
@ -20,11 +20,12 @@ from django.core.wsgi import get_wsgi_application
|
|||
|
||||
# This allows easy placement of apps within the interior
|
||||
# {{ cookiecutter.project_slug }} directory.
|
||||
app_path = os.path.abspath(os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)), os.pardir))
|
||||
sys.path.append(os.path.join(app_path, '{{ cookiecutter.project_slug }}'))
|
||||
app_path = os.path.abspath(
|
||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir)
|
||||
)
|
||||
sys.path.append(os.path.join(app_path, "{{ cookiecutter.project_slug }}"))
|
||||
{% if cookiecutter.use_sentry == 'y' -%}
|
||||
if os.environ.get('DJANGO_SETTINGS_MODULE') == 'config.settings.production':
|
||||
if os.environ.get("DJANGO_SETTINGS_MODULE") == "config.settings.production":
|
||||
from raven.contrib.django.raven_compat.middleware.wsgi import Sentry
|
||||
{%- endif %}
|
||||
# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
|
||||
|
@ -38,7 +39,7 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
|
|||
# setting points here.
|
||||
application = get_wsgi_application()
|
||||
{% if cookiecutter.use_sentry == 'y' -%}
|
||||
if os.environ.get('DJANGO_SETTINGS_MODULE') == 'config.settings.production':
|
||||
if os.environ.get("DJANGO_SETTINGS_MODULE") == "config.settings.production":
|
||||
application = Sentry(application)
|
||||
{%- endif %}
|
||||
# Apply WSGI middleware here.
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
Deploy
|
||||
========
|
||||
|
||||
This is where you describe how the project is deployed in production.
|
|
@ -1,186 +0,0 @@
|
|||
Developing with Docker
|
||||
======================
|
||||
|
||||
You can develop your application in a `Docker`_ container for simpler deployment onto bare Linux machines later. This instruction assumes an `Amazon Web Services`_ EC2 instance, but it should work on any machine with Docker > 1.3 and `Docker compose`_ installed.
|
||||
|
||||
.. _Docker: https://www.docker.com/
|
||||
.. _Amazon Web Services: http://aws.amazon.com/
|
||||
.. _Docker compose: https://docs.docker.com/compose/
|
||||
|
||||
Setting up
|
||||
^^^^^^^^^^
|
||||
|
||||
Docker encourages running one container for each process. This might mean one container for your web server, one for Django application and a third for your database. Once you're happy composing containers in this way you can easily add more, such as a `Redis`_ cache.
|
||||
|
||||
.. _Redis: http://redis.io/
|
||||
|
||||
The Docker compose tool (previously known as `fig`_) makes linking these containers easy. An example set up for your Cookiecutter Django project might look like this:
|
||||
|
||||
.. _fig: http://www.fig.sh/
|
||||
|
||||
::
|
||||
|
||||
webapp/ # Your cookiecutter project would be in here
|
||||
Dockerfile
|
||||
...
|
||||
database/
|
||||
Dockerfile
|
||||
...
|
||||
webserver/
|
||||
Dockerfile
|
||||
...
|
||||
production.yml
|
||||
|
||||
Each component of your application would get its own `Dockerfile`_. The rest of this example assumes you are using the `base postgres image`_ for your database. Your database settings in `config/base.py` might then look something like:
|
||||
|
||||
.. _Dockerfile: https://docs.docker.com/reference/builder/
|
||||
.. _base postgres image: https://registry.hub.docker.com/_/postgres/
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
'NAME': 'postgres',
|
||||
'USER': 'postgres',
|
||||
'HOST': 'database',
|
||||
'PORT': 5432,
|
||||
}
|
||||
}
|
||||
|
||||
The `Docker compose documentation`_ explains in detail what you can accomplish in the `production.yml` file, but an example configuration might look like this:
|
||||
|
||||
.. _Docker compose documentation: https://docs.docker.com/compose/#compose-documentation
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
database:
|
||||
build: database
|
||||
webapp:
|
||||
build: webapp:
|
||||
command: /usr/bin/python3.6 manage.py runserver 0.0.0.0:8000 # dev setting
|
||||
# command: gunicorn -b 0.0.0.0:8000 wsgi:application # production setting
|
||||
volumes:
|
||||
- webapp/your_project_name:/path/to/container/workdir/
|
||||
links:
|
||||
- database
|
||||
webserver:
|
||||
build: webserver
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
links:
|
||||
- webapp
|
||||
|
||||
We'll ignore the webserver for now (you'll want to comment that part out while we do). A working Dockerfile to run your cookiecutter application might look like this:
|
||||
|
||||
::
|
||||
|
||||
FROM ubuntu:14.04
|
||||
ENV REFRESHED_AT 2015-01-13
|
||||
|
||||
# update packages and prepare to build software
|
||||
RUN ["apt-get", "update"]
|
||||
RUN ["apt-get", "-y", "install", "build-essential", "vim", "git", "curl"]
|
||||
RUN ["locale-gen", "en_GB.UTF-8"]
|
||||
|
||||
# install latest python
|
||||
RUN ["apt-get", "-y", "build-dep", "python3-dev", "python3-imaging"]
|
||||
RUN ["apt-get", "-y", "install", "python3-dev", "python3-imaging", "python3-pip"]
|
||||
|
||||
# prepare postgreSQL support
|
||||
RUN ["apt-get", "-y", "build-dep", "python3-psycopg2"]
|
||||
|
||||
# move into our working directory
|
||||
# ADD must be after chown see http://stackoverflow.com/a/26145444/1281947
|
||||
RUN ["groupadd", "python"]
|
||||
RUN ["useradd", "python", "-s", "/bin/bash", "-m", "-g", "python", "-G", "python"]
|
||||
ENV HOME /home/python
|
||||
WORKDIR /home/python
|
||||
RUN ["chown", "-R", "python:python", "/home/python"]
|
||||
ADD ./ /home/python
|
||||
|
||||
# manage requirements
|
||||
ENV REQUIREMENTS_REFRESHED_AT 2015-02-25
|
||||
RUN ["pip3", "install", "-r", "requirements.txt"]
|
||||
|
||||
# uncomment the line below to use container as a non-root user
|
||||
USER python:python
|
||||
|
||||
Running `sudo docker-compose -f production.yml build` will follow the instructions in your `production.yml` file and build the database container, then your webapp, before mounting your cookiecutter project files as a volume in the webapp container and linking to the database. Our example yaml file runs in development mode but changing it to production mode is as simple as commenting out the line using `runserver` and uncommenting the line using `gunicorn`.
|
||||
|
||||
Both are set to run on port `0.0.0.0:8000`, which is where the Docker daemon will discover it. You can now run `sudo docker-compose -f production.yml up` and browse to `localhost:8000` to see your application running.
|
||||
|
||||
Deployment
|
||||
^^^^^^^^^^
|
||||
|
||||
You'll need a webserver container for deployment. An example setup for `Nginx`_ might look like this:
|
||||
|
||||
.. _Nginx: http://wiki.nginx.org/Main
|
||||
|
||||
::
|
||||
|
||||
FROM ubuntu:14.04
|
||||
ENV REFRESHED_AT 2015-02-11
|
||||
|
||||
# get the nginx package and set it up
|
||||
RUN ["apt-get", "update"]
|
||||
RUN ["apt-get", "-y", "install", "nginx"]
|
||||
|
||||
# forward request and error logs to docker log collector
|
||||
RUN ln -sf /dev/stdout /var/log/nginx/access.log
|
||||
RUN ln -sf /dev/stderr /var/log/nginx/error.log
|
||||
VOLUME ["/var/cache/nginx"]
|
||||
EXPOSE 80 443
|
||||
|
||||
# load nginx conf
|
||||
ADD ./site.conf /etc/nginx/sites-available/your_cookiecutter_project
|
||||
RUN ["ln", "-s", "/etc/nginx/sites-available/your_cookiecutter_project", "/etc/nginx/sites-enabled/your_cookiecutter_project"]
|
||||
RUN ["rm", "-rf", "/etc/nginx/sites-available/default"]
|
||||
|
||||
#start the server
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
That Dockerfile assumes you have an Nginx conf file named `site.conf` in the same directory as the webserver Dockerfile. A very basic example, which forwards traffic onto the development server or gunicorn for processing, would look like this:
|
||||
|
||||
::
|
||||
|
||||
# see http://serverfault.com/questions/577370/how-can-i-use-environment-variables-in-nginx-conf#comment730384_577370
|
||||
upstream localhost {
|
||||
server webapp_1:8000;
|
||||
}
|
||||
server {
|
||||
location / {
|
||||
proxy_pass http://localhost;
|
||||
}
|
||||
}
|
||||
|
||||
Running `sudo docker-compose -f production.yml build webserver` will build your server container. Running `sudo docker-compose -f production.yml up` will now expose your application directly on `localhost` (no need to specify the port number).
|
||||
|
||||
Building and running your app on EC2
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
All you now need to do to run your app in production is:
|
||||
|
||||
* Create an empty EC2 Linux instance (any Linux machine should do).
|
||||
|
||||
* Install your preferred source control solution, Docker and Docker compose on the news instance.
|
||||
|
||||
* Pull in your code from source control. The root directory should be the one with your `production.yml` file in it.
|
||||
|
||||
* Run `sudo docker-compose -f production.yml build` and `sudo docker-compose -f production.yml up`.
|
||||
|
||||
* Assign an `Elastic IP address`_ to your new machine.
|
||||
|
||||
.. _Elastic IP address: https://aws.amazon.com/articles/1346
|
||||
|
||||
* Point your domain name to the elastic IP.
|
||||
|
||||
**Be careful with Elastic IPs** because, on the AWS free tier, if you assign one and then stop the machine you will incur charges while the machine is down (presumably because you're preventing them allocating the IP to someone else).
|
||||
|
||||
Security advisory
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The setup described in this instruction will get you up-and-running but it hasn't been audited for security. If you are running your own setup like this it is always advisable to, at a minimum, examine your application with a tool like `OWASP ZAP`_ to see what security holes you might be leaving open.
|
||||
|
||||
.. _OWASP ZAP: https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project
|
|
@ -3,23 +3,17 @@
|
|||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to {{ cookiecutter.project_name }}'s documentation!
|
||||
{{ cookiecutter.project_name }} Project Documentation
|
||||
====================================================================
|
||||
|
||||
Contents:
|
||||
Table of Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
install
|
||||
deploy
|
||||
docker_ec2
|
||||
tests
|
||||
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
Indices & Tables
|
||||
================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
Install
|
||||
=========
|
||||
|
||||
This is where you write how to get a new laptop to run this project.
|
|
@ -19,6 +19,7 @@ pytest-sugar==0.9.2 # https://github.com/Frozenball/pytest-sugar
|
|||
# ------------------------------------------------------------------------------
|
||||
flake8==3.7.5 # https://github.com/PyCQA/flake8
|
||||
coverage==4.5.2 # https://github.com/nedbat/coveragepy
|
||||
black==18.9b0 # https://github.com/ambv/black
|
||||
|
||||
# Django
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{% if cookiecutter.use_celery == 'y' %}
|
||||
{% if cookiecutter.use_celery == 'y' -%}
|
||||
import os
|
||||
from celery import Celery
|
||||
from django.apps import apps, AppConfig
|
||||
|
@ -7,26 +7,28 @@ from django.conf import settings
|
|||
|
||||
if not settings.configured:
|
||||
# set the default Django settings module for the 'celery' program.
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.local') # pragma: no cover
|
||||
os.environ.setdefault(
|
||||
"DJANGO_SETTINGS_MODULE", "config.settings.local"
|
||||
) # pragma: no cover
|
||||
|
||||
|
||||
app = Celery('{{cookiecutter.project_slug}}')
|
||||
app = Celery("{{cookiecutter.project_slug}}")
|
||||
# Using a string here means the worker will not have to
|
||||
# pickle the object when using Windows.
|
||||
# - namespace='CELERY' means all celery-related configuration keys
|
||||
# should have a `CELERY_` prefix.
|
||||
app.config_from_object('django.conf:settings', namespace='CELERY')
|
||||
app.config_from_object("django.conf:settings", namespace="CELERY")
|
||||
|
||||
|
||||
class CeleryAppConfig(AppConfig):
|
||||
name = '{{cookiecutter.project_slug}}.taskapp'
|
||||
verbose_name = 'Celery Config'
|
||||
name = "{{cookiecutter.project_slug}}.taskapp"
|
||||
verbose_name = "Celery Config"
|
||||
|
||||
def ready(self):
|
||||
installed_apps = [app_config.name for app_config in apps.get_app_configs()]
|
||||
app.autodiscover_tasks(lambda: installed_apps, force=True)
|
||||
{%- if cookiecutter.use_sentry == 'y' %}
|
||||
|
||||
{% if cookiecutter.use_sentry == 'y' -%}
|
||||
if hasattr(settings, 'RAVEN_CONFIG'):
|
||||
# Celery signal registration
|
||||
{% if cookiecutter.use_pycharm == 'y' -%}
|
||||
|
@ -51,7 +53,7 @@ class CeleryAppConfig(AppConfig):
|
|||
|
||||
@app.task(bind=True)
|
||||
def debug_task(self):
|
||||
print(f'Request: {self.request!r}') # 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
|
||||
|
|
|
@ -7,12 +7,10 @@ from django.http import HttpRequest
|
|||
|
||||
|
||||
class AccountAdapter(DefaultAccountAdapter):
|
||||
|
||||
def is_open_for_signup(self, request: HttpRequest):
|
||||
return getattr(settings, "ACCOUNT_ALLOW_REGISTRATION", True)
|
||||
|
||||
|
||||
class SocialAccountAdapter(DefaultSocialAccountAdapter):
|
||||
|
||||
def is_open_for_signup(self, request: HttpRequest, sociallogin: Any):
|
||||
return getattr(settings, "ACCOUNT_ALLOW_REGISTRATION", True)
|
||||
|
|
|
@ -6,7 +6,6 @@ User = get_user_model()
|
|||
|
||||
|
||||
class UserChangeForm(forms.UserChangeForm):
|
||||
|
||||
class Meta(forms.UserChangeForm.Meta):
|
||||
model = User
|
||||
|
||||
|
|
|
@ -19,9 +19,7 @@ class UserFactory(DjangoModelFactory):
|
|||
digits=True,
|
||||
upper_case=True,
|
||||
lower_case=True,
|
||||
).generate(
|
||||
extra_kwargs={}
|
||||
)
|
||||
).generate(extra_kwargs={})
|
||||
self.set_password(password)
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -7,7 +7,6 @@ pytestmark = pytest.mark.django_db
|
|||
|
||||
|
||||
class TestUserCreationForm:
|
||||
|
||||
def test_clean_username(self):
|
||||
# A user with proto_user params does not exist yet.
|
||||
proto_user = UserFactory.build()
|
||||
|
|
|
@ -40,7 +40,6 @@ class TestUserUpdateView:
|
|||
|
||||
|
||||
class TestUserRedirectView:
|
||||
|
||||
def test_get_redirect_url(
|
||||
self, user: settings.AUTH_USER_MODEL, request_factory: RequestFactory
|
||||
):
|
||||
|
|
Loading…
Reference in New Issue
Block a user