115 | {% endraw %}
116 | {%- if cookiecutter.feature_annotations == "on" -%}
117 | {% raw %}{# END_FEATURE bootstrap_messages #}{% endraw %}
118 | {%- endif %}
119 | {%- endif %}
120 | {%- raw %}
121 |
122 |
123 |
124 | {%- endraw %}
125 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/readme.md:
--------------------------------------------------------------------------------
1 | # Project Template and Optional Features
2 |
3 | This project was created using https://github.com/zagaran/django-template
4 |
5 | See the readme on [django-template](https://github.com/zagaran/django-template) for:
6 | * Instructions on starting your own project
7 | * An explanation of included features.
8 |
9 | # Local Project Setup
10 |
11 | Set up a Python virtual environment: https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment
12 |
13 | ```bash
14 | # Create environment config file.
15 | cp config/.env.example config/.env
16 |
17 | # Fill in appropriate environment values.
18 | nano config/.env
19 |
20 | # Install pip requirements.
21 | pip install --upgrade pip
22 | pip install -r requirements-dev.txt
23 |
24 | # Apply migrations and sync database schema.
25 | python manage.py migrate
26 | {%- if cookiecutter.django_react == "enabled" or cookiecutter.sass_bootstrap == "enabled" %}
27 | # Install Node dependencies
28 | npm install
29 | {%- endif %}
30 | {%- if cookiecutter.sass_bootstrap == "enabled" %}
31 |
32 | {% if cookiecutter.feature_annotations == "on" %}
33 | # START_FEATURE sass_bootstrap
34 | {%- endif %}
35 | # Complie SCSS files
36 | python manage.py compilescss
37 | {%- if cookiecutter.feature_annotations == "on" %}
38 | # END_FEATURE sass_bootstrap
39 | {%- endif %}
40 | {%- endif %}
41 | ```
42 |
43 | To run the project:
44 | ```bash
45 | python manage.py runserver_plus
46 | ```
47 |
48 | {%- if cookiecutter.django_react == "enabled" %}
49 |
50 | To run the frontend with hotloading React assets:
51 | 1. Set `WEBPACK_LOADER_HOTLOAD=True` in `config/.env`
52 | 2. Run the following (in addition to `manage.py runserver_plus`):
53 | ```bash
54 | node_modules/nwb/lib/bin/nwb.js serve
55 | ```
56 |
57 | To make a static build of the frontend (such as when doing development on
58 | non-React parts of the codebase):
59 | 1. Set `WEBPACK_LOADER_HOTLOAD=False` in `config/.env`
60 | 2. Run the following:
61 | ```bash
62 | node_modules/nwb/lib/bin/nwb.js build --no-vendor
63 | ```
64 | {%- endif %}
65 |
66 | To access the database:
67 | ```bash
68 | python manage.py shell_plus
69 | ```
70 |
71 | To run the test suite:
72 | ```bash
73 | python manage.py test
74 | ```
75 |
76 | To get a test coverage report:
77 | ```bash
78 | coverage run --source='.' manage.py test; coverage report
79 | ```
80 |
81 | # Requirements
82 |
83 | The project uses [pip-tools](https://github.com/jazzband/pip-tools) to manage requirements. In addition, it has two requirements files:
84 |
85 | * `requirements.txt`: this is for requirements that should be installed on servers.
86 | * `requirements-dev.txt`: this is a superset of requirements.txt that should be used only for local development. This includes tools that are not needed on server deployments of the codebase and thus omitted in `requirements.txt` to reduce extraneous server installs.
87 |
88 | To add a new dependency to or update requirements, add the entry to requirements.in (if it's needed for the codebase to run) or requirements-dev.in (if it's just needed for development) and run `pip-compile` to generate new .txt files:
89 | ```bash
90 | nano requirements.in # Updating Python dependencies as needed
91 | nano requirements-dev.in # Updating Python dev dependencies as needed
92 | pip-compile requirements.in --upgrade # Generate requirements.txt with updated dependencies
93 | pip-compile requirements-dev.in --upgrade # Generate requirements-dev.txt with updated dependencies
94 | ```
95 |
96 | # Settings
97 |
98 | This project uses [django-environ](https://django-environ.readthedocs.io/en/latest/)
99 | to read configuration from either `config/.env` (for local development)
100 | or from environment varables (for server deployments). For a list of settings,
101 | see the `environ.Env(` object in [config/settings.py](config/settings.py).
102 |
103 |
104 | {%- if cookiecutter.elastic_beanstalk == "enabled" %}
105 | # Elastic Beanstalk
106 |
107 | The following Python packages are useful tools for interacting with AWS and Elastic Beanstalk.
108 | Due to dependency conflicts, these should not be installed in your project's regular virtual environment,
109 | and should instead either be installed globally or in a separate virtual environment that runs them:
110 |
111 | ```bash
112 | pip install awscli # For interacting with general AWS services (note, this package often has conflicts with its botocore dependency)
113 | pip install awsebcli # For interacting with Elastic Beanstalk (note, this package often has conflicts with its botocore dependency)
114 | pip install eb-create-environment # For automating the creation of Elastic Beanstalk applications
115 | pip install eb-ssm # For Elastic Beanstalk SSH functionality without requiring shared private keys
116 | ```
117 |
118 | ## Creating a new environment
119 |
120 | To do create a new Elastic Beanstalk environment, modify the contents of [.elasticbeanstalk/eb_create_environment.yml]([.elasticbeanstalk/eb_create_environment.yml]) and run `eb-create-environment -c .elasticbeanstalk/eb_create_environment.yml`.
121 |
122 | See the docs for [eb-create-environment](https://github.com/zagaran/eb-create-environment/) for more details.
123 |
124 | Then, add the following environment variables:
125 | ```
126 | ALLOWED_HOSTS
127 | SECRET_KEY
128 | {%- if cookiecutter.django_storages == "enabled" %}
129 | AWS_STORAGE_BUCKET_NAME
130 | {%- endif %}
131 | {%- if cookiecutter.django_social == "enabled" %}
132 | GOOGLE_OAUTH2_KEY
133 | GOOGLE_OAUTH2_SECRET
134 | {%- endif %}
135 | {%- if cookiecutter.sentry == "enabled" %}
136 | SENTRY_DSN
137 | {%- endif %}
138 | {%- if cookiecutter.django_ses == "enabled" %}
139 | DEFAULT_FROM_EMAIL
140 | {%- endif %}
141 | ```
142 |
143 | Following that, deploy your code to the environment (see below).
144 |
145 | ## Deploying code
146 |
147 | To deploy new versions of your code to your environment, run `eb deploy ` using the EB CLI to deploy your code to that environment.
148 |
149 | See the [eb-cli](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb-cli3.html) docs on general command line usage of Elastic Beanstalk.
150 |
151 | ## SSH
152 |
153 | To SSH into an Elastic Beanstalk Environment, use [eb-ssm](https://github.com/zagaran/eb-ssm).
154 | {%- endif %}
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # How To Use This Template
2 |
3 | This template is a [cookiecutter](https://github.com/cookiecutter/cookiecutter) template. Create a project from it using the following:
4 | ```
5 | pip install "cookiecutter>=1.7.0"
6 | cookiecutter https://github.com/zagaran/django-template
7 | ```
8 |
9 | The cookiecutter command will give you an interactive prompt to choose which optional features to inlcude (see below)
10 |
11 | Once you have cloned the project, [create a virtual environment](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment)
12 |
13 | Then run the following:
14 |
15 | ```
16 | pip install pip-tools
17 | pip-compile requirements.in --upgrade
18 | pip-compile requirements-dev.in --upgrade
19 | pip install -r requirements-dev.txt
20 | cp config/.env.example config/.env
21 | python manage.py makemigrations
22 |
23 | # If using the `elastic_beanstalk` feature
24 | git add --chmod=+x -- .platform///*.sh
25 |
26 | # Then see the generated README in your new project for the rest of the local setup instructions
27 | ```
28 |
29 | If you have an existing project, you can see a project based on this template here: https://github.com/zagaran/sample-django-app
30 |
31 | # Included Optional Features
32 |
33 | There are a number of optional features that are present in this template. You will be prompted for whether to include each one as part of running `cookiecutter`.
34 |
35 | ## `feature_annotations` (off by default)
36 |
37 | If you turn feature annotations on, the code for each optional feature will be bracketed by comments such as
38 | `# START_FEATURE feature_name` and `# END_FEATURE feature_name`.
39 |
40 |
41 | ## `reference_examples` (on by default)
42 | If you turn on reference examples, the codebase will have a number of reference examples. These are all marked with one of the following comments:
43 |
44 | ```
45 | # TODO: delete me; this is just a reference example
46 | // TODO: delete me; this is just a reference example
47 | {# TODO: delete me; this is just a reference example #}
48 | ```
49 |
50 |
51 | ## Django messages integration with Bootstrap (`bootstrap_messages`)
52 |
53 |
54 | ## Crispy Forms integration (`crispy_forms`)
55 |
56 |
57 | ## Debug Toolbar integration (`debug_toolbar`)
58 |
59 |
60 | ## Django-React integration, aka Djangre (`django_react`)
61 |
62 | ### Files
63 |
64 | The following files and folders are only needed if `django_react` is a desired feature in your app and can be safely
65 | deleted from projects which do not leverage the feature.
66 |
67 | - `nwb.config.js`
68 | - `package.json`
69 | - `webpack-stats.json`
70 | - `config/webpack_loader.py`
71 | - `src/`
72 |
73 | ### Additional Setup
74 |
75 | When using this feature, make sure to install the Node.js requirements using the manager of your choice
76 | (either `npm install` or `yarn install` will work) before proceeding with development.
77 |
78 | ### Special Consideration for Running
79 |
80 | For development on localhost when using Django-React, you should run the following command in a separate terminal to
81 | your standard `runserver` command.
82 |
83 | ```
84 | nwb serve --no-vendor # Note: refer to the nwb docs on when to use --no-vendor vs not
85 | ```
86 |
87 | If you have configured everything correctly, you should see each command complete and notify you
88 | that the project is ready to be viewed.
89 |
90 | - If you include `nwb` as a dependency, you can use the locally-installed `nwb` by running `node_modules/.bin/nwb serve --no-vendor` instead of relying on a globally installed `nwb`.
91 |
92 | ### Adding a new React component
93 |
94 | In this paradigm, React components are compiled and injected into the standard Django template. This means we can take
95 | advantage of the built-in templating functionality of Django and, with a bit of elbow grease, use the power of React to
96 | make those templates responsive.
97 |
98 | `django-react-loader` uses the same basic pattern for any component:
99 |
100 | 1. First, ensure that the library is loaded in your template: `{% load django_react_components %}`
101 | 2. Next, ensure that you have rendered the React runtime bundle: `{% render_bundle 'runtime' %}`
102 | - Note that you only have to do this once per page where React components will be used.
103 | 3. Finally, load your React component on the page. `{% react_component 'Component' %}`
104 | - You can add any number of props as named keywords, e.g. `{% react_component 'Home' id='home' prop1=value_from_context %}`
105 | - You can also choose to pass props as an object instead of individual kwargs, e.g. `{% react_component 'Hello' id='hello' props=sample_props %}`.
106 |
107 | ### Preparing for deployment
108 |
109 | The preferred option for deployment is to add the below compilation step to the deployment configuration rather than
110 | building it locally. However, if you wish to build the app locally:
111 |
112 | - run `nwb build --no-vendor`. This will generate or replace a `webpack_bundles` folder in your `/static` folder
113 | populated with the compiled React components. This then allows `collectstatic` to collect these static assets and
114 | make them available via the usual static assets pipeline set up in the deploy configuration.
115 | - Note that calling `nwb build` does not remove existing compiled data from your static folder. If you are building
116 | static assets locally and committing them to the repo rather than implementing a deploy compilation step, it is import
117 | to delete `/static/webpack_bundles` (in addition to wherever your existing static files are, e.g.
118 | `/staticfiles/webpack_bundles`) before running another build, as even without code changes NWB will generate new
119 | compiled JS files without removing the old ones. If you have implemented a `collecstatic` step in your deployment,
120 | ensure that existing webpack bundles are deleted before the new assets are generated by NWB.
121 |
122 | ### Other notes
123 |
124 | - If you use `nwb serve` in your local development environment, you may see a persistent XHR error in the console -- a
125 | request by the app to something like `http://localhost:8000/sockjs-node/info?t=123456789`. This is normal and will
126 | not appear on production or otherwise effect the function of your app - it is because the React components are being
127 | served by the browser in a different environment than the React components expect to be in.
128 |
129 |
130 | ## AWS SES integration (`django_ses`)
131 |
132 |
133 | ## Third-party authentication integrations (`django_social`)
134 |
135 |
136 | ## AWS S3 (or other cloud blob storage) integration (`django_storages`)
137 |
138 | ## Docker integration (`docker`)
139 |
140 |
141 | ## Elastic Beanstalk deployment (`elastic_beanstalk`)
142 |
143 | As a default for web applications, we strongly recommend using Elastic Beanstalk.
144 |
145 | To create a new deployment, [set up your local AWS credentials](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) e.g. ~/.aws/config,
146 |
147 | Ensure shell files in the `.platform` directory are executable according to git.
148 | You can check if they are executable via `git ls-files -s .platform`;
149 | you should see `100755` before any shell files in the output of this command.
150 | If you see `100644` before any of your shell files,
151 | run `git add --chmod=+x -- .platform/*/*/*.sh` to make them executable.
152 |
153 | Set desired parameters `.elasticbeanstalk/eb-create-environment.yml`
154 |
155 | Use [eb-create-environment](https://github.com/zagaran/eb-create-environment/):
156 | ```
157 | eb-create-environment --config .elasticbeanstalk/eb-create-environment.yml
158 | ```
159 |
160 | To update an existing deployment
161 | ```
162 | eb deploy [ENVIRONMENT_NAME]
163 | ```
164 |
165 | To SSH into a deployment
166 |
167 | Use [eb-ssm](https://github.com/zagaran/eb-ssm/):
168 | ```
169 | eb-ssm [ENVIRONMENT_NAME]
170 | ```
171 |
172 | ## Pre-commit hooks (`pre_commit`)
173 | You can configure pre-commit with `.pre-commit-config.yaml`
174 |
175 | See https://pre-commit.com/hooks.html for more hook options.
176 |
177 | To run style checks and desired formatters:
178 | ```
179 | pre-commit run --all-files
180 | ```
181 | If wish to install pre-commit as a pre-commit git hook, you can run (optional):
182 | ```
183 | pre-commit install
184 | ```
185 |
186 |
187 | ## Sass compilation (`sass_bootstrap`)
188 |
189 | Use this feature to enable Sass processing and Bootstrap styling.
190 |
191 | While you can just include Bootstrap's styling/js via a CDN, using this feature allows you to customize Bootstrap to the
192 | style guide of your project, as well as define custom styling in a cleaner and more maintainable way (compared to plain
193 | CSS). The Bootstrap part of this integration could be swapped out for any other frontend styling framework that also
194 | uses Sass, but there really is no reason to write vanilla CSS.
195 |
196 | In local development, you can simply write scss files and include them using `sass_tags` and your stylesheets should
197 | automatically recompile in reload. This also works seamlessly with `collectstatic` for deploys.
198 |
199 | Note: If you aren't already using npm to install bootstrap, you can alternatively clone the contents of Bootstrap's sass
200 | files directly into your static directory and change your references to point there. There is currently no good way to
201 | install Bootstrap source code using just python.
202 |
203 | ### Production notes
204 |
205 | In development, `.scss` files are compiled on the fly. However, when deploying, these files must be manually generated
206 | using `python manage.py compilescss`. Also note that if your styles folder is in a directory that's collected with
207 | `collectstatic`, you should add the `--ignore *.scss` flag to avoid exposing the raw `.scss` files as staticfiles.
208 |
209 |
210 | ## Security settings (`security_settings`)
211 | These are the recommended security settings. [Explanations for all Django settings can be found here](https://docs.djangoproject.com/en/3.2/ref/settings/). Please pay particular note to what are appropriate cookie and subdomain settings for your application.
212 |
213 | ## Sentry integration (`sentry`)
214 |
215 |
216 | ## User Action Tracking (`user_action_tracking`)
217 |
218 | This feature tracks all URLs accessed by users (along with the status code and user agent) in a table called `UserAction`.
219 | This can be useful for debugging, for analytics, or for auditing. There is a setting `USER_TRACKING_EXEMPT_ROUTES` where
220 | you can add the names of routes that should be excluded from action tracking because they would not be useful
221 | (for example, if your site has a keep_alive route that the frontend regulalry hits automatically). Note that only
222 | actions by authenticated users are tracked.
223 |
224 | # Optional Settings
225 |
226 | `MAINTENANCE_MODE`: Set this flag on a server environment to stop all user requests to the site, such as when you need to make substantial server updates or run a complex database migration.
227 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/config/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings.
3 |
4 | For more information on this file, see
5 | https://docs.djangoproject.com/en/2.2/topics/settings/
6 |
7 | For the full list of settings and their values, see
8 | https://docs.djangoproject.com/en/2.2/ref/settings/
9 | """
10 |
11 | import environ
12 | import os
13 |
14 | from django.contrib.messages import constants as messages
15 |
16 |
17 | env = environ.Env(
18 | # Sets Django's ALLOWED_HOSTS setting
19 | ALLOWED_HOSTS=(list, []),
20 | # Sets Django's DEBUG setting
21 | DEBUG=(bool, False),
22 | # Set to True when running locally for development purposes
23 | LOCALHOST=(bool, False),
24 | # Set to True in order to put the site in maintenance mode
25 | MAINTENANCE_MODE=(bool, False),
26 | # Set to True on the production server environment; setting to False makes the
27 | # site have a "deny all" robots.txt and a non-production warning on all pages
28 | PRODUCTION=(bool, True),
29 | {%- if cookiecutter.django_react == "enabled" %}
30 | {%- if cookiecutter.feature_annotations == "on" %}
31 |
32 | # START_FEATURE django_react
33 | {%- endif %}
34 | # Set to True to use JavaScript assets served on localhost:3000 via `nwb serve`
35 | WEBPACK_LOADER_HOTLOAD=(bool, False),
36 | {%- if cookiecutter.feature_annotations == "on" %}
37 | # END_FEATURE django_react
38 | {%- endif %}
39 | {%- endif %}
40 | {%- if cookiecutter.django_ses == "enabled" %}
41 | {%- if cookiecutter.feature_annotations == "on" %}
42 |
43 | # START_FEATURE django_ses
44 | {%- endif %}
45 | # Set to configure AWS SES to run in a region other than us-east-1
46 | AWS_SES_REGION_NAME=(str, "us-east-1"),
47 | AWS_SES_REGION_ENDPOINT=(str, "email.us-east-1.amazonaws.com"),
48 | {%- if cookiecutter.feature_annotations == "on" %}
49 | # END_FEATURE django_ses
50 | {%- endif %}
51 | {%- endif %}
52 | {%- if cookiecutter.sentry == "enabled" %}
53 | {%- if cookiecutter.feature_annotations == "on" %}
54 |
55 | # START_FEATURE sentry
56 | {%- endif %}
57 | # Set to the DSN from sentry.io to send errors to Sentry
58 | SENTRY_DSN=(str, None),
59 | {%- if cookiecutter.feature_annotations == "on" %}
60 | # END_FEATURE sentry
61 | {%- endif %}
62 | {%- endif %}
63 | {%- if cookiecutter.debug_toolbar == "enabled" %}
64 | {%- if cookiecutter.feature_annotations == "on" %}
65 |
66 | # START_FEATURE debug_toolbar
67 | {%- endif %}
68 | # Set to True to enable the Django Debug Toolbar
69 | DEBUG_TOOLBAR=(bool, False),
70 | {%- if cookiecutter.feature_annotations == "on" %}
71 | # END_FEATURE debug_toolbar
72 | {%- endif %}
73 | {%- endif %}
74 | )
75 | # If ALLWED_HOSTS has been configured, then we're running on a server and
76 | # can skip looking for a .env file (this assumes that .env files
77 | # file is only used for local development and servers use environment variables)
78 | if not env("ALLOWED_HOSTS"):
79 | environ.Env.read_env()
80 |
81 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
82 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
83 |
84 | # SECURITY WARNING: keep the secret key used in production secret!
85 | SECRET_KEY = env("SECRET_KEY")
86 |
87 | # SECURITY WARNING: do not run with debug turned on in production!
88 | DEBUG = env("DEBUG")
89 | {%- if cookiecutter.debug_toolbar == "enabled" %}
90 | {% if cookiecutter.feature_annotations == "on" %}
91 | # START_FEATURE debug_toolbar
92 | {%- endif %}
93 | DEBUG_TOOLBAR = DEBUG and env("DEBUG_TOOLBAR")
94 | {%- if cookiecutter.feature_annotations == "on" %}
95 | # END_FEATURE debug_toolbar
96 | {%- endif %}
97 | {%- endif %}
98 |
99 | # run with this set to False on server environments
100 | LOCALHOST = env("LOCALHOST")
101 |
102 | # set PRODUCTION to be False on non-production server environments to prevent
103 | # them from being indexed by search engines and to have a banner warning
104 | # that this is not the production site
105 | PRODUCTION = env("PRODUCTION")
106 |
107 | ALLOWED_HOSTS = env("ALLOWED_HOSTS")
108 | if LOCALHOST is True:
109 | ALLOWED_HOSTS = ["127.0.0.1", "localhost"]
110 | else:
111 | ALLOWED_HOSTS.append("localhost")
112 |
113 | # Application definition
114 | THIRD_PARTY_APPS = [
115 | "django.contrib.admin",
116 | "django.contrib.auth",
117 | "django.contrib.contenttypes",
118 | "django.contrib.sessions",
119 | "django.contrib.messages",
120 | "django.contrib.staticfiles",
121 | "django_extensions",
122 | {%- if cookiecutter.django_social == "enabled" %}
123 | {%- if cookiecutter.feature_annotations == "on" %}
124 |
125 | # START_FEATURE django_social
126 | {%- endif %}
127 | "social_django",
128 | {%- if cookiecutter.feature_annotations == "on" %}
129 | # END_FEATURE django_social
130 | {%- endif %}
131 | {%- endif %}
132 | {%- if cookiecutter.crispy_forms == "enabled" %}
133 | {%- if cookiecutter.feature_annotations == "on" %}
134 |
135 | # START_FEATURE crispy_forms
136 | {%- endif %}
137 | "crispy_forms",
138 | "crispy_bootstrap5",
139 | {%- if cookiecutter.feature_annotations == "on" %}
140 | # END_FEATURE crispy_forms
141 | {%- endif %}
142 | {%- endif %}
143 | {%- if cookiecutter.django_react == "enabled" %}
144 | {%- if cookiecutter.feature_annotations == "on" %}
145 |
146 | # START_FEATURE django_react
147 | {%- endif %}
148 | "django_react_components",
149 | "webpack_loader",
150 | {%- if cookiecutter.feature_annotations == "on" %}
151 | # END_FEATURE django_react
152 | {%- endif %}
153 | {%- endif %}
154 | {%- if cookiecutter.sass_bootstrap == "enabled" %}
155 | {%- if cookiecutter.feature_annotations == "on" %}
156 |
157 | # START_FEATURE sass_bootstrap
158 | {%- endif %}
159 | "sass_processor",
160 | {%- if cookiecutter.feature_annotations == "on" %}
161 | # END_FEATURE sass_bootstrap
162 | {%- endif %}
163 | {%- endif %}
164 | ]
165 | {%- if cookiecutter.debug_toolbar == "enabled" %}
166 |
167 | {% if cookiecutter.feature_annotations == "on" %}
168 | # START_FEATURE debug_toolbar
169 | {%- endif %}
170 | if DEBUG_TOOLBAR:
171 | THIRD_PARTY_APPS += ["debug_toolbar"]
172 | {%- if cookiecutter.feature_annotations == "on" %}
173 | # END_FEATURE debug_toolbar
174 | {%- endif %}
175 | {%- endif %}
176 |
177 | LOCAL_APPS = [
178 | "common",
179 | ]
180 |
181 | INSTALLED_APPS = THIRD_PARTY_APPS + LOCAL_APPS
182 |
183 | MIDDLEWARE = [
184 | "common.middleware.HealthCheckMiddleware",
185 | "django.middleware.security.SecurityMiddleware",
186 | "django.contrib.sessions.middleware.SessionMiddleware",
187 | "django.middleware.common.CommonMiddleware",
188 | "common.middleware.MaintenanceModeMiddleware",
189 | "django.middleware.csrf.CsrfViewMiddleware",
190 | "django.contrib.auth.middleware.AuthenticationMiddleware",
191 | "django.contrib.messages.middleware.MessageMiddleware",
192 | "django.middleware.clickjacking.XFrameOptionsMiddleware",
193 | {%- if cookiecutter.user_action_tracking == "enabled" %}
194 | {%- if cookiecutter.feature_annotations == "on" %}
195 | # START_FEATURE user_action_tracking
196 | {%- endif %}
197 | "common.middleware.UserActionTrackingMiddleware",
198 | {%- if cookiecutter.feature_annotations == "on" %}
199 | # END_FEATURE user_action_tracking
200 | {%- endif %}
201 | {%- endif %}
202 | ]
203 |
204 | MAINTENANCE_MODE = env("MAINTENANCE_MODE")
205 | {%- if cookiecutter.user_action_tracking == "enabled" %}
206 |
207 | {% if cookiecutter.feature_annotations == "on" %}
208 | # START_FEATURE user_action_tracking
209 | {%- endif %}
210 | USER_TRACKING_EXEMPT_ROUTES = []
211 | {%- if cookiecutter.feature_annotations == "on" %}
212 | # END_FEATURE user_action_tracking
213 | {%- endif %}
214 | {%- endif %}
215 |
216 |
217 | ROOT_URLCONF = "config.urls"
218 |
219 | TEMPLATES = [
220 | {
221 | "BACKEND": "django.template.backends.django.DjangoTemplates",
222 | "DIRS": [],
223 | "APP_DIRS": True,
224 | "OPTIONS": {
225 | "context_processors": [
226 | "django.template.context_processors.debug",
227 | "django.template.context_processors.request",
228 | "django.contrib.auth.context_processors.auth",
229 | "django.contrib.messages.context_processors.messages",
230 | "common.context_processors.django_settings",
231 | ],
232 | },
233 | },
234 | ]
235 |
236 | WSGI_APPLICATION = "config.wsgi.application"
237 |
238 |
239 | # Database
240 | # https://docs.djangoproject.com/en/2.2/ref/settings/#databases
241 |
242 | DATABASES = {"default": env.db()}
243 |
244 | DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
245 |
246 | # Password validation
247 | # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
248 |
249 | AUTH_PASSWORD_VALIDATORS = [
250 | {
251 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
252 | },
253 | {
254 | "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
255 | },
256 | {
257 | "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
258 | },
259 | {
260 | "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
261 | },
262 | ]
263 | {%- if cookiecutter.django_ses == "enabled" %}
264 |
265 | {% if cookiecutter.feature_annotations == "on" %}
266 | # START_FEATURE django_ses
267 | {%- endif %}
268 | if LOCALHOST:
269 | EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
270 | DEFAULT_FROM_EMAIL = "webmaster@localhost"
271 | else:
272 | EMAIL_BACKEND = "django_ses.SESBackend"
273 | AWS_SES_REGION_NAME = env("AWS_SES_REGION_NAME")
274 | AWS_SES_REGION_ENDPOINT = env("AWS_SES_REGION_ENDPOINT")
275 | AWS_SES_RETURN_PATH = env("DEFAULT_FROM_EMAIL")
276 | DEFAULT_FROM_EMAIL = env("DEFAULT_FROM_EMAIL")
277 | {%- if cookiecutter.feature_annotations == "on" %}
278 | # END_FEATURE django_ses
279 | {%- endif %}
280 | {%- endif %}
281 |
282 | # Logging
283 | # https://docs.djangoproject.com/en/dev/topics/logging/#django-security
284 | LOGGING = {
285 | "version": 1,
286 | "disable_existing_loggers": False,
287 | "formatters": {
288 | "verbose": {
289 | "format": "%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s"
290 | },
291 | "simple": {
292 | "format": "%(levelname)s %(message)s"
293 | },
294 | },
295 | "handlers": {
296 | "console": {
297 | "level": "WARNING",
298 | "class": "logging.StreamHandler",
299 | "formatter": "simple",
300 | },
301 | "null": {
302 | "class": "logging.NullHandler",
303 | },
304 | },
305 | "loggers": {
306 | "django.request": {
307 | "handlers": ["console"]
308 | },
309 | "django.security.DisallowedHost": {
310 | "handlers": ["null"],
311 | "propagate": False,
312 | },
313 | },
314 | }
315 |
316 |
317 | # Internationalization
318 | # https://docs.djangoproject.com/en/2.2/topics/i18n/
319 |
320 | LANGUAGE_CODE = "en-us"
321 |
322 | TIME_ZONE = "UTC"
323 |
324 | USE_I18N = True
325 |
326 | USE_L10N = True
327 |
328 | USE_TZ = True
329 |
330 |
331 | # Static files (CSS, JavaScript, Images)
332 | # https://docs.djangoproject.com/en/2.2/howto/static-files/
333 |
334 | STATIC_URL = "/static/"
335 | STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
336 | STATICFILES_DIRS = [
337 | os.path.join(BASE_DIR, "static"),
338 | ]
339 |
340 | AUTH_USER_MODEL = "common.User"
341 | {%- if cookiecutter.django_social == "enabled" %}
342 |
343 | {% if cookiecutter.feature_annotations == "on" %}
344 | # START_FEATURE django_social
345 | {%- endif %}
346 | AUTHENTICATION_BACKENDS = [
347 | "social_core.backends.google.GoogleOAuth2",
348 | ]
349 |
350 | LOGIN_URL = "index"
351 | LOGIN_REDIRECT_URL = "index"
352 |
353 | SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = env("GOOGLE_OAUTH2_KEY")
354 | SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = env("GOOGLE_OAUTH2_SECRET")
355 | {%- if cookiecutter.feature_annotations == "on" %}
356 | # END_FEATURE django_social
357 | {%- endif %}
358 | {%- endif %}
359 | {%- if cookiecutter.crispy_forms == "enabled" %}
360 |
361 | {% if cookiecutter.feature_annotations == "on" %}
362 | # START_FEATURE crispy_forms
363 | {%- endif %}
364 | CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
365 | CRISPY_TEMPLATE_PACK = "bootstrap5"
366 | {%- if cookiecutter.feature_annotations == "on" %}
367 | # END_FEATURE crispy_forms
368 | {%- endif %}
369 | {%- endif %}
370 | {%- if cookiecutter.bootstrap_messages == "enabled" %}
371 |
372 | {% if cookiecutter.feature_annotations == "on" %}
373 | # START_FEATURE bootstrap_messages
374 | {%- endif %}
375 | # Bootstrap styling for Django messages
376 | MESSAGE_TAGS = {
377 | messages.DEBUG: "alert-info",
378 | messages.INFO: "alert-info",
379 | messages.SUCCESS: "alert-success",
380 | messages.WARNING: "alert-warning",
381 | messages.ERROR: "alert-danger",
382 | }
383 | {%- if cookiecutter.feature_annotations == "on" %}
384 | # END_FEATURE bootstrap_messages
385 | {%- endif %}
386 | {%- endif %}
387 |
388 | {%- if cookiecutter.django_storages == "enabled" %}
389 |
390 | {% if cookiecutter.feature_annotations == "on" %}
391 | # START_FEATURE django_storages
392 | {%- endif %}
393 | if LOCALHOST is True:
394 | DEFAULT_STORAGE = {"BACKEND": "django.core.files.storage.FileSystemStorage"}
395 | MEDIA_ROOT = ""
396 | else:
397 | DEFAULT_STORAGE = {
398 | "BACKEND": "storages.backends.s3boto3.S3Boto3Storage",
399 | "OPTIONS": {
400 | "bucket_name": env("AWS_STORAGE_BUCKET_NAME"),
401 | "file_overwrite": False,
402 | "default_acl": "private",
403 | }
404 | }
405 | {%- if cookiecutter.feature_annotations == "on" %}
406 | # END_FEATURE django_storages
407 | {%- endif %}
408 | {%- else %}
409 | DEFAULT_STORAGE = {"BACKEND": "django.core.files.storage.FileSystemStorage"}
410 | {%- endif %}
411 | STORAGES = {
412 | "default": DEFAULT_STORAGE,
413 | {%- if cookiecutter.sass_bootstrap == "enabled" %}
414 | {%- if cookiecutter.feature_annotations == "on" %}
415 | # START_FEATURE sass_bootstrap
416 | {%- endif %}
417 | "sass_processor": {
418 | "BACKEND": "django.core.files.storage.FileSystemStorage",
419 | "OPTIONS": {
420 | "base_url": STATIC_URL,
421 | "location": os.path.join(BASE_DIR, 'static'),
422 | },
423 | "ROOT": os.path.join(BASE_DIR, 'static'),
424 | },
425 | {%- if cookiecutter.feature_annotations == "on" %}
426 | # END_FEATURE sass_bootstrap
427 | {%- endif %}
428 | {%- endif %}
429 | "staticfiles": {
430 | "BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage",
431 | },
432 | }
433 |
434 | STATICFILES_FINDERS = [
435 | 'django.contrib.staticfiles.finders.FileSystemFinder',
436 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
437 | 'sass_processor.finders.CssFinder',
438 | ]
439 |
440 | {%- if cookiecutter.debug_toolbar == "enabled" %}
441 |
442 | {% if cookiecutter.feature_annotations == "on" %}
443 | # START_FEATURE debug_toolbar
444 | {%- endif %}
445 | INTERNAL_IPS = ["127.0.0.1"]
446 | if DEBUG_TOOLBAR:
447 | MIDDLEWARE.append("debug_toolbar.middleware.DebugToolbarMiddleware")
448 | {%- if cookiecutter.feature_annotations == "on" %}
449 | # END_FEATURE debug_toolbar
450 | {%- endif %}
451 | {%- endif %}
452 | {%- if cookiecutter.django_react == "enabled" %}
453 |
454 | {% if cookiecutter.feature_annotations == "on" %}
455 | # START_FEATURE django_react
456 | {%- endif %}
457 | if DEBUG:
458 | WEBPACK_LOADER_HOTLOAD = env("WEBPACK_LOADER_HOTLOAD")
459 | if WEBPACK_LOADER_HOTLOAD:
460 | WEBPACK_LOADER = {
461 | "DEFAULT": {
462 | "LOADER_CLASS": "config.webpack_loader.DynamicWebpackLoader"
463 | }
464 | }
465 | {%- if cookiecutter.feature_annotations == "on" %}
466 | # END_FEATURE django_react
467 | {%- endif %}
468 | {%- endif %}
469 | {%- if cookiecutter.sentry == "enabled" %}
470 |
471 | {% if cookiecutter.feature_annotations == "on" %}
472 | # START_FEATURE sentry
473 | {%- endif %}
474 | SENTRY_DSN = env("SENTRY_DSN")
475 | if LOCALHOST is False and SENTRY_DSN:
476 | import sentry_sdk
477 | from sentry_sdk.integrations.django import DjangoIntegration
478 | from sentry_sdk.integrations.logging import ignore_logger
479 | sentry_sdk.init(
480 | dsn=env("SENTRY_DSN"),
481 | integrations=[DjangoIntegration()],
482 | traces_sample_rate=1.0,
483 | )
484 | # Silence "invalid HTTP_HOST" errors
485 | ignore_logger("django.security.DisallowedHost")
486 |
487 | {%- if cookiecutter.feature_annotations == "on" %}
488 | # END_FEATURE sentry
489 | {%- endif %}
490 | {%- endif %}
491 | {%- if cookiecutter.security_settings == "enabled" %}
492 |
493 | {% if cookiecutter.feature_annotations == "on" %}
494 | # START_FEATURE security_settings
495 | {%- endif %}
496 | if LOCALHOST is False:
497 | SECURE_SSL_REDIRECT = True
498 | SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
499 |
500 | USE_X_FORWARDED_HOST = True
501 | X_FRAME_OPTIONS = "DENY"
502 | SECURE_REFERRER_POLICY = "same-origin"
503 | SECURE_BROWSER_XSS_FILTER = True
504 | SECURE_CONTENT_TYPE_NOSNIFF = True
505 |
506 | SECURE_HSTS_SECONDS = 3600 # 1 hour
507 | # TODO: increase SECURE_HSTS_SECONDS and register with hstspreload.org once production deploy is stable
508 | # SECURE_HSTS_SECONDS = 3600 * 24 * 365 * 2 # 2 years
509 | SECURE_HSTS_INCLUDE_SUBDOMAINS = True
510 | SECURE_HSTS_PRELOAD = True
511 |
512 | SESSION_COOKIE_SECURE = True
513 | SESSION_COOKIE_HTTPONLY = True
514 | SESSION_COOKIE_AGE = 60 * 60 * 3 # 3 hours
515 | CSRF_COOKIE_SECURE = True
516 | CSRF_COOKIE_HTTPONLY = True # Only do this if you are not accessing the CSRF cookie with JS
517 | {%- if cookiecutter.feature_annotations == "on" %}
518 | # END_FEATURE security_settings
519 | {%- endif %}
520 | {%- endif %}
521 | {%- if cookiecutter.sass_bootstrap == "enabled" %}
522 |
523 | {% if cookiecutter.feature_annotations == "on" %}
524 | # START_FEATURE sass_bootstrap
525 | {%- endif %}
526 | SASS_PRECISION = 8 # Bootstrap's sass requires a precision of at least 8 to prevent layout errors
527 | SASS_PROCESSOR_CUSTOM_FUNCTIONS = {
528 | 'django-static': 'django.templatetags.static.static',
529 | }
530 | SASS_PROCESSOR_INCLUDE_DIRS = [
531 | os.path.join(BASE_DIR, 'static/styles'),
532 | os.path.join(BASE_DIR, 'node_modules'),
533 | ]
534 | COMPRESS_ROOT = STORAGES["sass_processor"]["ROOT"]
535 | {%- if cookiecutter.feature_annotations == "on" %}
536 | # END_FEATURE sass_bootstrap
537 | {%- endif %}
538 | {%- endif %}
539 |
--------------------------------------------------------------------------------