├── LICENSE
├── cookiecutter.json
├── hooks
└── post_gen_project.py
├── readme.md
└── {{cookiecutter.project_slug}}
├── .dockerignore
├── .ebextensions
└── django.config
├── .elasticbeanstalk
└── eb_create_environment.yml
├── .gitignore
├── .platform
├── confighooks
│ └── predeploy
│ │ └── predeploy.sh
├── hooks
│ └── predeploy
│ │ └── predeploy.sh
└── httpd
│ └── conf.d
│ └── ssl_rewrite.conf
├── .pre-commit-config.yaml
├── Dockerfile
├── common
├── __init__.py
├── admin.py
├── apps.py
├── constants.py
├── context_processors.py
├── forms.py
├── managers.py
├── middleware
│ ├── __init__.py
│ ├── maintenance_mode_middleware.py
│ └── user_action_tracking.py
├── migrations
│ ├── 0001_initial.py
│ └── __init__.py
├── models.py
├── templates
│ ├── base_templates
│ │ └── base.html
│ ├── common
│ │ ├── index.html
│ │ ├── maintenance_mode.html
│ │ ├── sample_django_react.html
│ │ └── sample_form.html
│ └── errors
│ │ ├── 404.html
│ │ └── 500.html
├── tests.py
├── urls.py
└── views.py
├── config
├── .env.example
├── __init__.py
├── settings.py
├── setup.cfg
├── urls.py
├── webpack_loader.py
└── wsgi.py
├── manage.py
├── nwb.config.js
├── package.json
├── readme.md
├── requirements-dev.in
├── requirements.in
├── src
├── Components
│ └── Hello.js
└── Pages
│ └── Home.js
└── static
├── favicon.png
└── styles
├── _variables.scss
├── base.scss
└── pages
└── index.scss
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Zagaran, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/cookiecutter.json:
--------------------------------------------------------------------------------
1 | {
2 | "project_name": "[PROJECT]",
3 | "project_slug": "{{ cookiecutter.project_name.lower()|replace(' ', '_')|replace('-', '_')|replace('.', '_')|trim() }}",
4 |
5 | "feature_annotations": ["off", "on"],
6 | "reference_examples": ["on", "off"],
7 |
8 | "bootstrap_messages": ["enabled", "disabled"],
9 | "crispy_forms": ["enabled", "disabled"],
10 | "debug_toolbar": ["enabled", "disabled"],
11 | "django_react": ["enabled", "disabled"],
12 | "django_ses": ["enabled", "disabled"],
13 | "django_social": ["enabled", "disabled"],
14 | "django_storages": ["enabled", "disabled"],
15 | "docker": ["enabled", "disabled"],
16 | "elastic_beanstalk": ["enabled", "disabled"],
17 | "pre_commit": ["enabled", "disabled"],
18 | "sass_bootstrap": ["enabled", "disabled"],
19 | "security_settings": ["enabled", "disabled"],
20 | "sentry": ["enabled", "disabled"],
21 | "user_action_tracking": ["enabled", "disabled"]
22 | }
23 |
--------------------------------------------------------------------------------
/hooks/post_gen_project.py:
--------------------------------------------------------------------------------
1 | import glob
2 | import os
3 | import shutil
4 | from os import path
5 |
6 |
7 | CONDITIONAL_REMOVE_PATHS = [
8 | "{% if cookiecutter.elastic_beanstalk == 'disabled' %}.ebextensions{% endif %}",
9 | "{% if cookiecutter.elastic_beanstalk == 'disabled' %}.elasticbeanstalk{% endif %}",
10 | "{% if cookiecutter.elastic_beanstalk == 'disabled' %}.platform{% endif %}",
11 | "{% if cookiecutter.docker == 'disabled' %}Dockerfile{% endif %}",
12 | "{% if cookiecutter.docker == 'disabled' %}.dockerignore{% endif %}",
13 | "{% if cookiecutter.pre_commit == 'disabled' %}.pre-commit-config.yaml{% endif %}",
14 | "{% if cookiecutter.django_react == 'disabled' %}config/webpack_loader.py{% endif %}",
15 | "{% if cookiecutter.django_react == 'disabled' %}nwb.config.js{% endif %}",
16 | "{% if cookiecutter.django_react == 'disabled' and cookiecutter.sass_bootstrap == 'disabled' %}package.json{% endif %}",
17 | ]
18 |
19 |
20 | def main():
21 | delete_conditional_paths()
22 | delete_empty_files()
23 | print_next_steps()
24 |
25 |
26 | def delete_conditional_paths():
27 | for path in CONDITIONAL_REMOVE_PATHS:
28 | if path and os.path.exists(path):
29 | if os.path.isdir(path):
30 | shutil.rmtree(path)
31 | else:
32 | os.remove(path)
33 |
34 |
35 | def delete_empty_files():
36 | exempt_files = ["__init__.py"]
37 | file_paths = glob.glob("**", recursive=True)
38 |
39 | for file_path in file_paths:
40 | if not path.isfile(file_path):
41 | continue
42 | if path.split(file_path)[-1] in exempt_files:
43 | continue
44 | with open(file_path, 'r') as file:
45 | try:
46 | contents = file.read()
47 | except UnicodeDecodeError:
48 | pass
49 | if not contents.strip():
50 | os.remove(file_path)
51 |
52 |
53 | def print_next_steps():
54 | print("\n\nWelcome to your new project.")
55 | print("\ncd {{cookiecutter.project_slug}}")
56 | print("\nSet up a virtual environment (https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment)")
57 | print("Then run the following commands for first-time setup:\n")
58 | commands = [
59 | "pip install pip-tools",
60 | "pip-compile requirements.in --upgrade",
61 | "pip-compile requirements-dev.in --upgrade",
62 | "pip install -r requirements-dev.txt",
63 | "cp config/.env.example config/.env",
64 | "python manage.py makemigrations",
65 | ]
66 | if "{{ cookiecutter.elastic_beanstalk }}".lower() == "enabled":
67 | commands.append("git add --chmod=+x -- .platform/*/*/*.sh")
68 | for command in commands:
69 | print(command)
70 | print("\nSee the README for the rest of the local setup instructions")
71 |
72 |
73 | if __name__ == "__main__":
74 | main()
75 |
--------------------------------------------------------------------------------
/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}}/.dockerignore:
--------------------------------------------------------------------------------
1 | # Ignore pyc files
2 | **/*.pyc
3 |
4 | # Ignore local databases
5 | **/*.sqlite3
6 |
7 | # Local environment files
8 | */.env
9 |
10 | # Environments
11 | .venv
12 |
13 | # Editors
14 | .idea/
15 | .vscode/
16 |
17 | # Operating system metafiles
18 | .DS_Store
19 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/.ebextensions/django.config:
--------------------------------------------------------------------------------
1 | {% if cookiecutter.feature_annotations == "on" -%}# START_FEATURE elastic_beanstalk{%- endif %}
2 | packages:
3 | yum:
4 | postgresql15-server-devel: []
5 | nodejs: []
6 | htop: []
7 |
8 | container_commands:
9 | 01_update_pip:
10 | command: |
11 | source $PYTHONPATH/activate
12 | pip install --upgrade pip
13 | 02_install_requirements:
14 | command: |
15 | source $PYTHONPATH/activate
16 | pip install -r requirements.txt
17 | 03_migratedb:
18 | command: |
19 | source $PYTHONPATH/activate
20 | python manage.py migrate --noinput
21 | leader_only: true
22 |
23 | option_settings:
24 | aws:elasticbeanstalk:environment:proxy:
25 | ProxyServer: apache
26 | aws:elasticbeanstalk:application:environment:
27 | DJANGO_SETTINGS_MODULE: "config.settings"
28 | aws:elasticbeanstalk:container:python:
29 | WSGIPath: config.wsgi:application
30 | aws:elasticbeanstalk:environment:proxy:staticfiles:
31 | /static: staticfiles
32 | aws:elasticbeanstalk:environment:process:default:
33 | HealthCheckPath: /health-check/
34 |
35 | files:
36 | "/home/ec2-user/.bashrc":
37 | mode: "000644"
38 | owner: ec2-user
39 | group: ec2-user
40 | content: |
41 | # .bashrc
42 | # Source global definitions
43 | if [ -f /etc/bashrc ]; then
44 | . /etc/bashrc
45 | fi
46 | # User specific aliases and functions
47 | set -a; source <(sudo cat /opt/elasticbeanstalk/deployment/env); cd /var/app/current
48 | alias db="cd /var/app/current/; python manage.py shell_plus"
49 |
50 | "/home/ec2-user/.inputrc":
51 | mode: "000644"
52 | owner: ec2-user
53 | group: ec2-user
54 | content: |
55 | ## arrow up
56 | "\e[A":history-search-backward
57 | ## arrow down
58 | "\e[B":history-search-forward
59 |
60 | "/opt/elasticbeanstalk/tasks/taillogs.d/cfn-init-cmd.conf":
61 | mode: "000755"
62 | owner: root
63 | group: root
64 | content: |
65 | /var/log/cfn-init-cmd.log
66 | {% if cookiecutter.feature_annotations == "on" -%}# END_FEATURE elastic_beanstalk{%- endif %}
67 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/.elasticbeanstalk/eb_create_environment.yml:
--------------------------------------------------------------------------------
1 | {% if cookiecutter.feature_annotations == "on" -%}# START_FEATURE elastic_beanstalk{%- endif %}
2 | # https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/command-options-general.html
3 | ElasticBeanstalk:
4 | # aws elasticbeanstalk list-available-solution-stacks --region us-east-1
5 | SolutionStackName: "64bit Amazon Linux 2023 * Python 3.11"
6 | NumProcesses: "1"
7 | InstanceTypes: "t3.micro"
8 | IamInstanceProfile: "aws-elasticbeanstalk-ec2-role"
9 | AssociatePublicIpAddress: True
10 | ProxyServer: "apache" # apache, nginx
11 | InstancePublicSubnets: True
12 | # Omit the load balancer block to have it be not load balanced
13 | LoadBalancer:
14 | LoadBalancerType: "application"
15 | SSLCertificateId: null
16 | ELBScheme: "public" # public, private
17 | MinSize: "1"
18 | MaxSize: "1"
19 | PublicSubnets: True
20 | # Omit the managed updates block to disable managed updates
21 | ManagedUpdates:
22 | PreferredStartTime: "TUE:05:15"
23 | UpdateLevel: "minor" # patch, minor
24 | ServiceRoleForManagedUpdates: "AWSServiceRoleForElasticBeanstalkManagedUpdates"
25 | RDS:
26 | AllocatedStorage: 100
27 | DBInstanceClass: "db.t3.micro"
28 | MasterUsername: "db_admin"
29 | BackupRetentionPeriod: 30
30 | MultiAZ: False
31 | AutoMinorVersionUpgrade: False
32 | PubliclyAccessible: False
33 | StorageType: "gp2"
34 | StorageEncrypted: True
35 | CopyTagsToSnapshot: True
36 | MonitoringInterval: 0 # 60 but 0 disables this
37 | DeletionProtection: False
38 | MaxAllocatedStorage: 1000
39 | Postgres:
40 | DBName: "ebdb"
41 | Engine: "postgres"
42 | EngineVersion: "15.*"
43 | Port: 5432
44 | DBParameterGroupName: "default.postgres15"
45 | LicenseModel: "postgresql-license"
46 | {% if cookiecutter.feature_annotations == "on" -%}# END_FEATURE elastic_beanstalk{%- endif %}
47 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Installer logs
10 | pip-log.txt
11 | pip-delete-this-directory.txt
12 |
13 | # Unit test / coverage reports
14 | htmlcov/
15 | .tox/
16 | .nox/
17 | .coverage
18 | .coverage.*
19 | .cache
20 | nosetests.xml
21 | coverage.xml
22 | *.cover
23 | *.py,cover
24 | .hypothesis/
25 | .pytest_cache/
26 | cover/
27 |
28 | # Translations
29 | *.mo
30 | *.pot
31 |
32 | # Django stuff:
33 | *.log
34 | *.sqlite3
35 | *.sqlite3-journal
36 |
37 | # Environments
38 | .env
39 | .venv
40 | env/
41 | venv/
42 | ENV/
43 | env.bak/
44 | venv.bak/
45 |
46 | # Editors
47 | .idea
48 | .vscode
49 |
50 | # Operating systems
51 | .DS_Store
52 |
53 | # Installed packages
54 | node_modules/
55 |
56 | # Compiled Files
57 | staticfiles/
58 | staticfiles/*
59 | # START_FEATURE sass_bootstrap
60 | /static/styles/**/*.css
61 | /static/styles/**/*.map
62 | # END_FEATURE sass_bootstrap
63 | # START_FEATURE django_react
64 | static/webpack_bundles/
65 | webpack-stats.json
66 | # END_FEATURE django_react
67 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/.platform/confighooks/predeploy/predeploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | {% if cookiecutter.feature_annotations == "on" -%}# START_FEATURE elastic_beanstalk{%- endif %}
5 | source $PYTHONPATH/activate
6 |
7 | {% if cookiecutter.django_react == "enabled" or cookiecutter.sass_bootstrap == "enabled" -%}
8 | {%- if cookiecutter.feature_annotations == "on" -%}# START_FEATURE django_react, sass_bootstrap{%- endif %}
9 | NODE_OPTIONS=--max_old_space_size=1000 npm install --production
10 | {% if cookiecutter.feature_annotations == "on" -%}# END_FEATURE django_react, sass_bootstrap{%- endif %}
11 | {%- endif %}
12 |
13 | {% if cookiecutter.django_react == "enabled" -%}
14 | {%- if cookiecutter.feature_annotations == "on" -%}# START_FEATURE django_react{%- endif %}
15 | # delete old webpack static resources
16 | rm -rf static/webpack_bundles/ || echo "no webpack bundles to remove"
17 | rm -rf staticfiles/webpack_bundles/ || echo "no staticfiles webpack bundles to remove"
18 | NODE_OPTIONS=--openssl-legacy-provider node_modules/.bin/nwb build --no-vendor
19 | {% if cookiecutter.feature_annotations == "on" -%}# END_FEATURE django_react{%- endif %}
20 | {%- endif %}
21 |
22 | {% if cookiecutter.sass_bootstrap == "enabled" -%}
23 | {%- if cookiecutter.feature_annotations == "on" -%}# START_FEATURE sass_bootstrap{%- endif %}
24 | python manage.py compilescss
25 | {% if cookiecutter.feature_annotations == "on" -%}# END_FEATURE sass_bootstrap{%- endif %}
26 | {%- endif %}
27 |
28 | python manage.py collectstatic --noinput --ignore *.scss
29 | {% if cookiecutter.feature_annotations == "on" -%}# END_FEATURE elastic_beanstalk{%- endif %}
30 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/.platform/hooks/predeploy/predeploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | {% if cookiecutter.feature_annotations == "on" -%}# START_FEATURE elastic_beanstalk{%- endif %}
5 | source $PYTHONPATH/activate
6 |
7 | {% if cookiecutter.django_react == "enabled" or cookiecutter.sass_bootstrap == "enabled" -%}
8 | {%- if cookiecutter.feature_annotations == "on" -%}# START_FEATURE django_react, sass_bootstrap{%- endif %}
9 | NODE_OPTIONS=--max_old_space_size=1000 npm install --production
10 | {% if cookiecutter.feature_annotations == "on" -%}# END_FEATURE django_react, sass_bootstrap{%- endif %}
11 | {%- endif %}
12 |
13 | {% if cookiecutter.django_react == "enabled" -%}
14 | {%- if cookiecutter.feature_annotations == "on" -%}# START_FEATURE django_react{%- endif %}
15 | # delete old webpack static resources
16 | rm -rf static/webpack_bundles/ || echo "no webpack bundles to remove"
17 | rm -rf staticfiles/webpack_bundles/ || echo "no staticfiles webpack bundles to remove"
18 | NODE_OPTIONS=--openssl-legacy-provider node_modules/.bin/nwb build --no-vendor
19 | {% if cookiecutter.feature_annotations == "on" -%}# END_FEATURE django_react{%- endif %}
20 | {%- endif %}
21 |
22 | {% if cookiecutter.sass_bootstrap == "enabled" -%}
23 | {%- if cookiecutter.feature_annotations == "on" -%}# START_FEATURE sass_bootstrap{%- endif %}
24 | python manage.py compilescss
25 | {% if cookiecutter.feature_annotations == "on" -%}# END_FEATURE sass_bootstrap{%- endif %}
26 | {%- endif %}
27 |
28 | python manage.py collectstatic --noinput --ignore *.scss
29 | {% if cookiecutter.feature_annotations == "on" -%}# END_FEATURE elastic_beanstalk{%- endif %}
30 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/.platform/httpd/conf.d/ssl_rewrite.conf:
--------------------------------------------------------------------------------
1 | {% if cookiecutter.feature_annotations == "on" -%}# START_FEATURE elastic_beanstalk{%- endif %}
2 | RewriteEngine On
3 |
4 | RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R,L]
5 |
6 | {% if cookiecutter.feature_annotations == "on" -%}# END_FEATURE elastic_beanstalk{%- endif %}
7 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | {%- if cookiecutter.feature_annotations == "on" -%}# START_FEATURE pre_commit{%- endif %}
2 | # See https://pre-commit.com for more information
3 | # See https://pre-commit.com/hooks.html for more hooks
4 | default_language_version:
5 | python: python3.8
6 | exclude: /migrations/
7 | repos:
8 | - repo: https://github.com/pre-commit/pre-commit-hooks
9 | rev: v3.2.0
10 | hooks:
11 | - id: trailing-whitespace
12 | - id: end-of-file-fixer
13 | - id: check-yaml
14 | - id: check-merge-conflict
15 | - id: check-added-large-files
16 | - repo: https://gitlab.com/PyCQA/flake8
17 | rev: 3.9.0
18 | hooks:
19 | - id: flake8
20 | types: [file, python]
21 | args: ['--config=config/setup.cfg']
22 | - repo: https://github.com/pycqa/isort
23 | rev: 5.8.0
24 | hooks:
25 | - id: isort
26 | types: [file, python]
27 | {% if cookiecutter.feature_annotations == "on" -%}# END_FEATURE pre_commit{%- endif %}
28 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/Dockerfile:
--------------------------------------------------------------------------------
1 | {% if cookiecutter.feature_annotations == "on" -%}# START_FEATURE docker{%- endif %}
2 | FROM python:3.11.4-slim-buster
3 |
4 | WORKDIR /app
5 |
6 | ADD requirements.txt /app/requirements.txt
7 |
8 | RUN set -ex \
9 | && buildDeps=" \
10 | build-essential \
11 | libpq-dev \
12 | " \
13 | && deps=" \
14 | postgresql-client \
15 | " \
16 | && apt-get update && apt-get install -y $buildDeps $deps --no-install-recommends \
17 | && pip install --no-cache-dir -r /app/requirements.txt \
18 | && apt-get purge -y --auto-remove $buildDeps \
19 | $(! command -v gpg > /dev/null || echo 'gnupg dirmngr') \
20 | && rm -rf /var/lib/apt/lists/*
21 |
22 | ENV VIRTUAL_ENV /env
23 | ENV PATH /env/bin:$PATH
24 |
25 | {% if cookiecutter.django_react == "enabled" -%}
26 | {% if cookiecutter.feature_annotations == "on" -%}# START_FEATURE django_react{%- endif %}
27 | COPY ./nwb.config.js /app/nwb.config.js
28 | COPY ./package.json /app/package.json
29 | COPY ./package-lock.json /app/package-lock.json
30 | RUN npm install
31 | {% if cookiecutter.feature_annotations == "on" -%}# END_FEATURE django_react{%- endif %}
32 |
33 | {% endif -%}
34 |
35 |
36 | COPY . /app/
37 | COPY ./config/.env.example /app/config/.env
38 |
39 | {% if cookiecutter.django_react == "enabled" -%}
40 | {% if cookiecutter.feature_annotations == "on" -%}# START_FEATURE django_react{%- endif %}
41 | RUN ./node_modules/.bin/nwb build --no-vendor
42 | {% if cookiecutter.feature_annotations == "on" -%}# END_FEATURE django_react{%- endif %}
43 |
44 | {% endif -%}
45 |
46 |
47 | {% if cookiecutter.sass_bootstrap == "enabled" -%}
48 | {%- if cookiecutter.feature_annotations == "on" -%}# START_FEATURE sass_bootstrap{%- endif %}
49 | RUN python manage.py compilescss
50 | {% if cookiecutter.feature_annotations == "on" -%}# END_FEATURE sass_bootstrap{%- endif %}
51 |
52 | {% endif -%}
53 |
54 | RUN python manage.py collectstatic --noinput
55 |
56 | RUN rm /app/config/.env
57 |
58 | EXPOSE 8000
59 |
60 | CMD ["gunicorn", "--bind", ":8000", "--workers", "3", "config.wsgi:application"]
61 | {% if cookiecutter.feature_annotations == "on" %}# END_FEATURE docker{%- endif %}
62 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zagaran/django-template/92fb23aae04d6bbdcbc308152982c083395ca480/{{cookiecutter.project_slug}}/common/__init__.py
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class CommonConfig(AppConfig):
5 | name = 'common'
6 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/constants.py:
--------------------------------------------------------------------------------
1 | from django.db.models import TextChoices
2 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/context_processors.py:
--------------------------------------------------------------------------------
1 | def django_settings(request):
2 | from django.conf import settings
3 | return {
4 | "PRODUCTION": settings.PRODUCTION,
5 | "LOCALHOST": settings.LOCALHOST,
6 | }
7 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/forms.py:
--------------------------------------------------------------------------------
1 | {%- if cookiecutter.crispy_forms == "enabled" -%}
2 | {%- if cookiecutter.feature_annotations == "on" -%}
3 | # START_FEATURE crispy_forms
4 | {%- endif %}
5 | from django import forms
6 |
7 | from crispy_forms.helper import FormHelper
8 | from crispy_forms.layout import Submit
9 |
10 |
11 | class CrispyFormMixin(object):
12 | submit_label = "Save"
13 | form_action = ""
14 |
15 | def __init__(self, *args, **kwargs):
16 | super().__init__(*args, **kwargs)
17 | self.helper = FormHelper()
18 | self.helper.form_method = "POST"
19 | self.helper.form_action = self.form_action
20 | self.helper.add_input(Submit("submit", self.submit_label))
21 | {%- if cookiecutter.feature_annotations == "on" %}
22 | # END_FEATURE crispy_forms
23 | {%- endif %}
24 | {%- endif %}
25 |
26 |
27 | {%- if cookiecutter.reference_examples == "on" %}
28 | {%- if cookiecutter.crispy_forms == "enabled" %}
29 |
30 | {% if cookiecutter.feature_annotations == "on" %}
31 | # START_FEATURE crispy_forms
32 | {%- endif %}
33 | class SampleForm(CrispyFormMixin, forms.Form):
34 | # TODO: delete me; this is just a reference example
35 | is_company = forms.CharField(label="company", required=False, widget=forms.CheckboxInput())
36 | email = forms.EmailField(
37 | label="email", max_length=30, required=True, widget=forms.TextInput(), help_text="Insert your email"
38 | )
39 | first_name = forms.CharField(label="first name", max_length=5, required=True, widget=forms.TextInput())
40 | last_name = forms.CharField(label="last name", max_length=5, required=True, widget=forms.TextInput())
41 | datetime_field = forms.SplitDateTimeField(label="date time", widget=forms.SplitDateTimeWidget())
42 |
43 | def clean(self):
44 | super().clean()
45 | password1 = self.cleaned_data.get("password1", None)
46 | password2 = self.cleaned_data.get("password2", None)
47 | if not password1 and not password2 or password1 != password2:
48 | raise forms.ValidationError("Passwords dont match")
49 | return self.cleaned_data
50 | {%- if cookiecutter.feature_annotations == "on" %}
51 | # END_FEATURE crispy_forms
52 | {%- endif %}
53 | {%- endif %}
54 | {%- endif %}
55 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/managers.py:
--------------------------------------------------------------------------------
1 | {%- if cookiecutter.django_social == "enabled" -%}
2 | {%- if cookiecutter.feature_annotations == "on" -%}
3 | # START_FEATURE django_social
4 | {%- endif %}
5 | from django.contrib.auth.base_user import BaseUserManager
6 |
7 |
8 | class UserManager(BaseUserManager):
9 |
10 | """
11 | Custom user model manager where email is the unique identifiers
12 | for authentication instead of usernames.
13 | """
14 | def create_user(self, email, password=None, **extra_fields):
15 | """
16 | Create and save a User with the given email and password.
17 | """
18 | if not email:
19 | raise ValueError('The Email must be set')
20 | email = self.normalize_email(email)
21 | user = self.model(email=email, **extra_fields)
22 | user.set_password(password)
23 | user.save()
24 | return user
25 |
26 | def create_superuser(self, email, password, **extra_fields):
27 | """
28 | Create and save a SuperUser with the given email and password.
29 | """
30 | extra_fields.setdefault('is_staff', True)
31 | extra_fields.setdefault('is_superuser', True)
32 | extra_fields.setdefault('is_active', True)
33 |
34 | if extra_fields.get('is_staff') is not True:
35 | raise ValueError('Superuser must have is_staff=True.')
36 | if extra_fields.get('is_superuser') is not True:
37 | raise ValueError('Superuser must have is_superuser=True.')
38 | return self.create_user(email, password, **extra_fields)
39 | {%- if cookiecutter.feature_annotations == "on" %}
40 | # END_FEATURE django_social
41 | {%- endif %}
42 | {%- endif %}
43 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/middleware/__init__.py:
--------------------------------------------------------------------------------
1 | from common.middleware.maintenance_mode_middleware import MaintenanceModeMiddleware
2 | {%- if cookiecutter.user_action_tracking == "enabled" %}
3 | {%- if cookiecutter.feature_annotations == "on" %}
4 | # START_FEATURE user_action_tracking
5 | {%- endif %}
6 | from common.middleware.user_action_tracking import UserActionTrackingMiddleware
7 | {%- if cookiecutter.feature_annotations == "on" %}
8 | # END_FEATURE user_action_tracking
9 | {%- endif %}
10 | {%- endif %}
11 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/middleware/maintenance_mode_middleware.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.core.exceptions import MiddlewareNotUsed
3 | from django.http.response import HttpResponse
4 | from django.template import loader
5 |
6 |
7 | class MaintenanceModeMiddleware:
8 | def __init__(self, get_response):
9 | # One-time configuration and initialization.
10 | self.get_response = get_response
11 |
12 | # Disable middleware if MAINTENANCE_MODE is not on
13 | if not settings.MAINTENANCE_MODE:
14 | raise MiddlewareNotUsed()
15 |
16 | def __call__(self, request):
17 | # Render the template directly without the request to skip other steps
18 | content = loader.render_to_string("common/maintenance_mode.html")
19 | return HttpResponse(content)
20 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/middleware/user_action_tracking.py:
--------------------------------------------------------------------------------
1 | {%- if cookiecutter.user_action_tracking == "enabled" -%}
2 | {%- if cookiecutter.feature_annotations == "on" -%}
3 | # START_FEATURE user_action_tracking
4 | {%- endif %}
5 | from django.conf import settings
6 | from common.models import UserAction
7 |
8 |
9 | class UserActionTrackingMiddleware:
10 | def __init__(self, get_response):
11 | self.get_response = get_response
12 |
13 | def __call__(self, request):
14 | response = self.get_response(request)
15 | self.create_user_action(request, response)
16 | return response
17 |
18 | def create_user_action(self, request, response):
19 | try:
20 | user = request.user
21 | except AttributeError:
22 | return
23 |
24 | if not user.is_authenticated:
25 | return
26 |
27 | url_name = None
28 | if request.resolver_match:
29 | url_name = request.resolver_match.url_name
30 |
31 | if url_name in settings.USER_TRACKING_EXEMPT_ROUTES:
32 | return
33 |
34 | UserAction.objects.create(
35 | user=user,
36 | url=request.build_absolute_uri(),
37 | method=request.method,
38 | url_name=url_name,
39 | status_code=response.status_code,
40 | user_agent=request.META.get("HTTP_USER_AGENT")
41 | )
42 | {%- if cookiecutter.feature_annotations == "on" %}
43 | # END_FEATURE user_action_tracking
44 | {%- endif %}
45 | {%- endif %}
46 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.6 on 2021-10-04 19:37
2 |
3 | import common.models
4 | from django.conf import settings
5 | from django.db import migrations, models
6 | import django.db.models.deletion
7 | import django.utils.timezone
8 | import uuid
9 |
10 |
11 | class Migration(migrations.Migration):
12 |
13 | initial = True
14 |
15 | dependencies = [
16 | ('auth', '0012_alter_user_first_name_max_length'),
17 | ]
18 |
19 | operations = [
20 | migrations.CreateModel(
21 | name='User',
22 | fields=[
23 | ('password', models.CharField(max_length=128, verbose_name='password')),
24 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
25 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
26 | ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
27 | ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
28 | ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
29 | ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
30 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
31 | ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
32 | ('created_on', models.DateTimeField(auto_now_add=True)),
33 | ('updated_on', models.DateTimeField(auto_now=True)),
34 | ('email', models.EmailField(max_length=254, unique=True)),
35 | ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
36 | ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
37 | ],
38 | options={
39 | 'verbose_name': 'user',
40 | 'verbose_name_plural': 'users',
41 | 'abstract': False,
42 | },
43 | ),
44 | migrations.CreateModel(
45 | name='UserAction',
46 | fields=[
47 | ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
48 | ('created_on', models.DateTimeField(auto_now_add=True)),
49 | ('updated_on', models.DateTimeField(auto_now=True)),
50 | ('url', models.URLField(max_length=2083)),
51 | ('method', models.CharField(max_length=64)),
52 | ('url_name', models.CharField(max_length=256, null=True)),
53 | ('status_code', models.IntegerField()),
54 | ('user_agent', models.TextField(null=True)),
55 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='user_actions', to=settings.AUTH_USER_MODEL)),
56 | ],
57 | options={
58 | 'abstract': False,
59 | },
60 | ),
61 | ]
62 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zagaran/django-template/92fb23aae04d6bbdcbc308152982c083395ca480/{{cookiecutter.project_slug}}/common/migrations/__init__.py
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/models.py:
--------------------------------------------------------------------------------
1 | import uuid
2 |
3 | from django.contrib.auth.models import AbstractUser
4 | from django.db import models
5 |
6 | {% if cookiecutter.django_social == "enabled" -%}
7 | from common.managers import UserManager
8 | {%- endif %}
9 |
10 |
11 | class TimestampedModel(models.Model):
12 | id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
13 |
14 | created_on = models.DateTimeField(auto_now_add=True)
15 | updated_on = models.DateTimeField(auto_now=True)
16 |
17 | def update(self, update_dict=None, **kwargs):
18 | """ Helper method to update objects """
19 | if not update_dict:
20 | update_dict = kwargs
21 | update_fields = {"updated_on"}
22 | for k, v in update_dict.items():
23 | setattr(self, k, v)
24 | update_fields.add(k)
25 | self.save(update_fields=update_fields)
26 |
27 | class Meta:
28 | abstract = True
29 |
30 |
31 | # Create your models here.
32 | class User(AbstractUser, TimestampedModel):
33 | email = models.EmailField(unique=True)
34 | {%- if cookiecutter.django_social == "enabled" %}
35 | {%- if cookiecutter.feature_annotations == "on" %}
36 | # START_FEATURE django_social
37 | {%- endif %}
38 | username = None # disable the AbstractUser.username field
39 | USERNAME_FIELD = "email"
40 | REQUIRED_FIELDS = []
41 |
42 | objects = UserManager()
43 | {%- if cookiecutter.feature_annotations == "on" %}
44 | # END_FEATURE django_social
45 | {%- endif %}
46 | {%- endif %}
47 |
48 | {%- if cookiecutter.reference_examples == "on" %}
49 | {%- if cookiecutter.django_storages == "enabled" %}
50 | {%- if cookiecutter.feature_annotations == "on" %}
51 |
52 |
53 | # START_FEATURE django_storages
54 | {%- endif %}
55 | # TODO: delete me; this is just a reference example
56 | def get_s3_path(instance, filename):
57 | return "%s/%s/%s" % (
58 | "uploads",
59 | instance.user_id,
60 | filename,
61 | )
62 |
63 |
64 | class UploadFile(TimestampedModel):
65 | user = models.ForeignKey(User, related_name="files", on_delete=models.PROTECT)
66 | file = models.FileField(
67 | max_length=1024,
68 | upload_to=get_s3_path
69 | )
70 |
71 | class Meta:
72 | abstract = True
73 | {%- if cookiecutter.feature_annotations == "on" %}
74 | # END_FEATURE django_storages
75 | {%- endif %}
76 | {%- endif %}
77 | {%- endif %}
78 | {%- if cookiecutter.user_action_tracking == "enabled" %}
79 | {%- if cookiecutter.feature_annotations == "on" %}
80 |
81 |
82 | # START_FEATURE user_action_tracking
83 | {%- endif %}
84 | class UserAction(TimestampedModel):
85 | user = models.ForeignKey(User, related_name="user_actions", on_delete=models.PROTECT)
86 | url = models.URLField(max_length=2083)
87 | method = models.CharField(max_length=64)
88 | url_name = models.CharField(max_length=256, null=True)
89 | status_code = models.IntegerField()
90 | user_agent = models.TextField(null=True)
91 | {%- if cookiecutter.feature_annotations == "on" %}
92 | # END_FEATURE user_action_tracking
93 | {%- endif %}
94 | {%- endif %}
95 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/templates/base_templates/base.html:
--------------------------------------------------------------------------------
1 | {%- if cookiecutter.sass_bootstrap == "enabled" %}
2 | {%- if cookiecutter.feature_annotations == "on" -%}
3 | {% raw %}{# START_FEATURE sass_bootstrap #}{% endraw %}
4 | {%- endif %}
5 | {%- raw %}
6 | {% load sass_tags %}
7 | {%- endraw %}
8 | {%- if cookiecutter.feature_annotations == "on" %}
9 | {% raw %}{# END_FEATURE sass_bootstrap #}{% endraw %}
10 | {%- endif %}
11 | {%- endif %}
12 |
13 | {%- raw %}
14 |
15 |
16 |
17 |
18 |
19 | {% endraw %}{{ cookiecutter.project_name }}{% raw %} - {% block title %}{% endblock %}
20 | {%- endraw %}
21 |
22 | {%- if cookiecutter.sass_bootstrap == "enabled" %}
23 | {%- if cookiecutter.feature_annotations == "on" %}
24 | {% raw %}{# START_FEATURE sass_bootstrap #}{% endraw %}
25 | {%- endif %}
26 | {%- raw %}
27 |
28 | {%- endraw %}
29 | {%- if cookiecutter.feature_annotations == "on" %}
30 | {% raw %}{# END_FEATURE sass_bootstrap #}{% endraw %}
31 | {%- endif %}
32 | {%- else %}
33 | {%- raw %}
34 |
35 | {%- endraw %}
36 | {%- endif %}
37 |
38 | {%- raw %}
39 |
40 | {% block head %}{% endblock %}
41 |
42 |
43 |
86 | {% if not PRODUCTION and not LOCALHOST %}
87 |
88 |
89 |
90 |
91 |
Warning
92 |
This is a test site. Do not enter personal or sensitive data on this site.
93 |
94 |
95 |
96 | {% endif %}
97 |
98 | {% endraw %}
99 | {%- if cookiecutter.bootstrap_messages == "enabled" %}
100 | {%- if cookiecutter.feature_annotations == "on" -%}
101 | {% raw %}{# START_FEATURE bootstrap_messages #}{% endraw %}
102 | {%- endif %}
103 | {%- raw %}
104 |
105 | {% for message in messages %}
106 |
107 |
110 | {{ message }}
111 |
112 | {% endfor %}
113 | {% block body %}{% endblock %}
114 |
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}}/common/templates/common/index.html:
--------------------------------------------------------------------------------
1 | {%- raw -%}
2 | {% extends "base_templates/base.html" %}
3 |
4 | {% endraw %}
5 | {%- if cookiecutter.sass_bootstrap == "enabled" %}
6 | {%- if cookiecutter.feature_annotations == "on" %}
7 | {% raw %}{# START_FEATURE sass_bootstrap #}{% endraw %}
8 | {%- endif %}
9 | {%- raw %}
10 | {% load sass_tags %}
11 | {%- endraw %}
12 | {%- if cookiecutter.feature_annotations == "on" %}
13 | {% raw %}{# END_FEATURE sass_bootstrap #}{% endraw %}
14 | {%- endif %}
15 | {%- endif %}
16 | {%- raw %}
17 |
18 | {% block title %}Home{% endblock %}
19 |
20 | {% block head %}
21 | {% endraw %}
22 | {%- if cookiecutter.sass_bootstrap == "enabled" %}
23 | {%- if cookiecutter.feature_annotations == "on" %}
24 | {% raw %}{# START_FEATURE sass_bootstrap #}{% endraw %}
25 | {%- endif %}
26 | {%- raw %}
27 |
28 | {%- endraw %}
29 | {%- if cookiecutter.feature_annotations == "on" %}
30 | {% raw %}{# END_FEATURE sass_bootstrap #}{% endraw %}
31 | {%- endif %}
32 | {%- endif %}
33 | {%- raw %}
34 | {% endblock %}
35 |
36 | {% block body %}
37 |
38 |
Welcome to {% endraw %}{{ cookiecutter.project_name }}{% raw %}. Please log in to continue.
39 | {% endraw %}
40 | {%- if cookiecutter.sass_bootstrap == "enabled" %}
41 | {%- if cookiecutter.feature_annotations == "on" %}
42 | {% raw %}{# START_FEATURE sass_bootstrap #}{% endraw %}
43 | {%- endif %}
44 | {%- if cookiecutter.reference_examples == "on" %}
45 | {%- raw %}
46 | {# TODO: delete me; this is just a reference example #}
47 |
48 | {%- endraw %}
49 | {%- endif %}
50 | {%- if cookiecutter.feature_annotations == "on" %}
51 | {% raw %}{# END_FEATURE sass_bootstrap #}{% endraw %}
52 | {%- endif %}
53 | {%- endif %}
54 | {%- raw %}
55 |
56 | {% endblock %}
57 | {%- endraw %}
58 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/templates/common/maintenance_mode.html:
--------------------------------------------------------------------------------
1 | {%- raw -%}
2 | {% extends "base_templates/base.html" %}
3 | {% load crispy_forms_tags %}
4 |
5 | {% block title %}Down for Maintenance{% endblock %}
6 |
7 | {% block body %}
8 | This site is currently down for maintenance
9 | Please check back soon. Thank you for your patience.
10 | {% endblock %}
11 | {%- endraw %}
12 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/templates/common/sample_django_react.html:
--------------------------------------------------------------------------------
1 | {%- if cookiecutter.reference_examples == "on" -%}
2 | {%- if cookiecutter.django_react == "enabled" -%}
3 | {%- if cookiecutter.feature_annotations == "on" -%}
4 | {% raw %}{# START_FEATURE django_react #}{% endraw %}
5 | {%- endif %}
6 | {%- raw %}
7 |
8 | {# TODO: delete me; this is just a reference example #}
9 |
10 | {% extends 'base_templates/base.html' %}
11 | {% load django_react_components %}
12 | {% load render_bundle from webpack_loader %}
13 |
14 | {% block body %}
15 | {% render_bundle 'runtime' %}
16 | {% render_bundle 'Home' %}
17 | {% render_bundle 'Hello' %}
18 | {% react_component 'Home' id='home' message=message %}
19 | {% react_component 'Hello' id='hello' props=sample_props %}
20 | {% endblock %}
21 |
22 | {%- endraw %}
23 | {%- if cookiecutter.feature_annotations == "on" %}
24 |
25 | {% raw %}{# END_FEATURE django_react #}{% endraw %}
26 | {%- endif %}
27 | {%- endif %}
28 | {%- endif %}
29 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/templates/common/sample_form.html:
--------------------------------------------------------------------------------
1 | {%- if cookiecutter.reference_examples == "on" -%}
2 | {%- if cookiecutter.crispy_forms == "enabled" -%}
3 | {%- if cookiecutter.feature_annotations == "on" -%}
4 | {% raw %}{# START_FEATURE crispy_forms #}{% endraw %}
5 | {%- endif %}
6 | {%- raw %}
7 | {# TODO: delete me; this is just a reference example #}
8 | {% extends "base_templates/base.html" %}
9 | {% load crispy_forms_tags %}
10 |
11 | {% block title %}Sample Form{% endblock %}
12 |
13 | {% block body %}
14 | {% crispy form %}
15 | {% endblock %}
16 | {%- endraw %}
17 | {%- if cookiecutter.feature_annotations == "on" %}
18 | {% raw %}{# END_FEATURE crispy_forms #}{% endraw %}
19 | {%- endif %}
20 | {%- endif %}
21 | {%- endif %}
22 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/templates/errors/404.html:
--------------------------------------------------------------------------------
1 | {%- raw -%}
2 | {% extends "base_templates/base.html" %}
3 |
4 | {% block title %}Not Found (404){% endblock %}
5 |
6 | {% block body %}
7 |
8 |
Page not found (404)
9 |
The page you are looking for doesn't exist.
10 |
Back to home
11 |
12 | {% endblock %}
13 | {%- endraw %}
14 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/templates/errors/500.html:
--------------------------------------------------------------------------------
1 | {%- raw -%}
2 | {% extends "base_templates/base.html" %}
3 |
4 | {% block title %}Internal Server Error (500){% endblock %}
5 |
6 | {% block body %}
7 |
8 |
Internal Server Error (500)
9 |
The system encountered an error. Please try again later.
10 |
Back to home
11 |
12 | {% endblock %}
13 | {%- endraw %}
14 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.urls import include, path
3 |
4 | from common import views
5 |
6 | urlpatterns = [
7 | path("", views.IndexView.as_view(), name="index"),
8 | {%- if cookiecutter.reference_examples == "on" %}
9 | {%- if cookiecutter.django_react == "enabled" %}
10 | {%- if cookiecutter.feature_annotations == "on" %}
11 | # START_FEATURE django_react
12 | {%- endif %}
13 | # TODO: delete me; this is just a reference example
14 | path("django-react/", views.DjangoReactView.as_view(), name='django_react_demo'),
15 | {%- if cookiecutter.feature_annotations == "on" %}
16 | # END_FEATURE django_react
17 | {%- endif %}
18 | {%- endif %}
19 | {%- endif %}
20 | path("logout", views.LogoutView.as_view(), name="logout"),
21 | path("robots.txt", views.RobotsTxtView.as_view(), name="robots_txt"),
22 | ]
23 | {%- if cookiecutter.debug_toolbar == "enabled" %}
24 | {%- if cookiecutter.feature_annotations == "on" %}
25 |
26 | # START_FEATURE debug_toolbar
27 | {%- endif %}
28 | if settings.DEBUG_TOOLBAR:
29 | urlpatterns += [
30 | path("__debug__/", include("debug_toolbar.urls")),
31 | ]
32 | {%- if cookiecutter.feature_annotations == "on" %}
33 | # END_FEATURE debug_toolbar
34 | {%- endif %}
35 | {%- endif %}
36 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/common/views.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth import logout
2 | from django.conf import settings
3 | from django.shortcuts import redirect, render
4 | from django.views.generic.base import TemplateView, View
5 | from django.http.response import HttpResponse
6 | {%- if cookiecutter.crispy_forms == "enabled" %}
7 | {%- if cookiecutter.feature_annotations == "on" %}
8 | # START_FEATURE crispy_forms
9 | {%- endif %}
10 | from django.views.generic.edit import FormView
11 |
12 | {%- if cookiecutter.reference_examples == "on" %}
13 | from common.forms import SampleForm
14 | {%- endif %}
15 | {%- if cookiecutter.feature_annotations == "on" %}
16 | # END_FEATURE crispy_forms
17 | {%- endif %}
18 | {%- endif %}
19 |
20 |
21 | class IndexView(TemplateView):
22 | template_name = "common/index.html"
23 |
24 |
25 | class LogoutView(View):
26 | def post(self, request):
27 | logout(request)
28 | return redirect("index")
29 |
30 |
31 | class RobotsTxtView(View):
32 | def get(self, request):
33 | if settings.PRODUCTION:
34 | # Allow all (note that a blank Disallow block means "allow all")
35 | lines = ["User-agent: *", "Disallow:"]
36 | else:
37 | # Block all
38 | lines = ["User-agent: *", "Disallow: /"]
39 | return HttpResponse("\n".join(lines), content_type="text/plain")
40 | {%- if cookiecutter.reference_examples == "on" %}
41 | {%- if cookiecutter.django_react == "enabled" %}
42 |
43 |
44 | {% if cookiecutter.feature_annotations == "on" %}
45 | # START_FEATURE django_react
46 | {%- endif %}
47 | class DjangoReactView(TemplateView):
48 | # TODO: delete me; this is just a reference example
49 | template_name = 'common/sample_django_react.html'
50 |
51 | def get_context_data(self, **kwargs):
52 | context = super().get_context_data(**kwargs)
53 | context['hello_msg'] = 'Component'
54 | context['sample_props'] = {'msg': 'sample props'}
55 | return context
56 | {%- if cookiecutter.feature_annotations == "on" %}
57 | # END_FEATURE django_react
58 | {%- endif %}
59 | {%- endif %}
60 | {%- endif %}
61 | {%- if cookiecutter.reference_examples == "on" %}
62 | {%- if cookiecutter.crispy_forms == "enabled" %}
63 |
64 | {% if cookiecutter.feature_annotations == "on" %}
65 | # START_FEATURE crispy_forms
66 | {%- endif %}
67 | class SampleFormView(FormView):
68 | # TODO: delete me; this is just a reference example
69 | form_class = SampleForm
70 | {%- if cookiecutter.feature_annotations == "on" %}
71 | # END_FEATURE crispy_forms
72 | {%- endif %}
73 | {%- endif %}
74 | {%- endif %}
75 |
76 | def error_404(request, exception):
77 | return render(request, "errors/404.html", status=404)
78 |
79 | def error_500(request):
80 | return render(request, "errors/500.html", status=500)
81 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/config/.env.example:
--------------------------------------------------------------------------------
1 | DEBUG=True
2 | {%- if cookiecutter.debug_toolbar == "enabled" %}
3 | {%- if cookiecutter.feature_annotations == "on" %}
4 | # START_FEATURE debug_toolbar
5 | {%- endif %}
6 | DEBUG_TOOLBAR=True
7 | {%- if cookiecutter.feature_annotations == "on" %}
8 | # END_FEATURE debug_toolbar
9 | {%- endif %}
10 | {%- endif %}
11 | LOCALHOST=True
12 | PRODUCTION=False
13 | SECRET_KEY=[FILL_ME_IN]
14 | DATABASE_URL=sqlite:///db.sqlite3
15 | MAINTENANCE_MODE=False
16 | {%- if cookiecutter.django_social == "enabled" %}
17 | {%- if cookiecutter.feature_annotations == "on" %}
18 | # START_FEATURE django_social
19 | {%- endif %}
20 | GOOGLE_OAUTH2_KEY=[FILL_ME_IN]
21 | GOOGLE_OAUTH2_SECRET=[FILL_ME_IN]
22 | {%- if cookiecutter.feature_annotations == "on" %}
23 | # END_FEATURE django_social
24 | {%- endif %}
25 | {%- endif %}
26 | {%- if cookiecutter.sentry == "enabled" %}
27 | {%- if cookiecutter.feature_annotations == "on" %}
28 | # START_FEATURE sentry
29 | {%- endif %}
30 | SENTRY_DSN=[FILL_ME_IN]
31 | {%- if cookiecutter.feature_annotations == "on" %}
32 | # END_FEATURE sentry
33 | {%- endif %}
34 | {%- endif %}
35 | {%- if cookiecutter.django_react == "enabled" %}
36 | {%- if cookiecutter.feature_annotations == "on" %}
37 | # START_FEATURE django_react
38 | {%- endif %}
39 | # this setting is only relevant to local development
40 | WEBPACK_LOADER_HOTLOAD=False
41 | {%- if cookiecutter.feature_annotations == "on" %}
42 | # END_FEATURE django_react
43 | {%- endif %}
44 | {%- endif %}
45 | {%- if cookiecutter.django_ses == "enabled" %}
46 | {%- if cookiecutter.feature_annotations == "on" %}
47 | # START_FEATURE django_ses
48 | {%- endif %}
49 | DEFAULT_FROM_EMAIL=[FILL_ME_IN]
50 | {%- if cookiecutter.feature_annotations == "on" %}
51 | # END_FEATURE django_ses
52 | {%- endif %}
53 | {%- endif %}
54 |
55 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/config/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zagaran/django-template/92fb23aae04d6bbdcbc308152982c083395ca480/{{cookiecutter.project_slug}}/config/__init__.py
--------------------------------------------------------------------------------
/{{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 os
12 |
13 | from django.contrib.messages import constants as messages
14 |
15 | import environ
16 |
17 |
18 | env = environ.Env(
19 | # Sets Django's ALLOWED_HOSTS setting
20 | ALLOWED_HOSTS=(list, []),
21 | # Sets Django's DEBUG setting
22 | DEBUG=(bool, False),
23 | # Set to True when running locally for development purposes
24 | LOCALHOST=(bool, False),
25 | # Set to True in order to put the site in maintenance mode
26 | MAINTENANCE_MODE=(bool, False),
27 | # Set to True on the production server environment; setting to False makes the
28 | # site have a "deny all" robots.txt and a non-production warning on all pages
29 | PRODUCTION=(bool, True),
30 | {%- if cookiecutter.django_react == "enabled" %}
31 | {%- if cookiecutter.feature_annotations == "on" %}
32 |
33 | # START_FEATURE django_react
34 | {%- endif %}
35 | # Set to True to use JavaScript assets served on localhost:3000 via `nwb serve`
36 | WEBPACK_LOADER_HOTLOAD=(bool, False),
37 | {%- if cookiecutter.feature_annotations == "on" %}
38 | # END_FEATURE django_react
39 | {%- endif %}
40 | {%- endif %}
41 | {%- if cookiecutter.django_ses == "enabled" %}
42 | {%- if cookiecutter.feature_annotations == "on" %}
43 |
44 | # START_FEATURE django_ses
45 | {%- endif %}
46 | # Set to configure AWS SES to run in a region other than us-east-1
47 | AWS_SES_REGION_NAME=(str, "us-east-1"),
48 | AWS_SES_REGION_ENDPOINT=(str, "email.us-east-1.amazonaws.com"),
49 | {%- if cookiecutter.feature_annotations == "on" %}
50 | # END_FEATURE django_ses
51 | {%- endif %}
52 | {%- endif %}
53 | {%- if cookiecutter.sentry == "enabled" %}
54 | {%- if cookiecutter.feature_annotations == "on" %}
55 |
56 | # START_FEATURE sentry
57 | {%- endif %}
58 | # Set to the DSN from sentry.io to send errors to Sentry
59 | SENTRY_DSN=(str, None),
60 | {%- if cookiecutter.feature_annotations == "on" %}
61 | # END_FEATURE sentry
62 | {%- endif %}
63 | {%- endif %}
64 | {%- if cookiecutter.debug_toolbar == "enabled" %}
65 | {%- if cookiecutter.feature_annotations == "on" %}
66 |
67 | # START_FEATURE debug_toolbar
68 | {%- endif %}
69 | # Set to True to enable the Django Debug Toolbar
70 | DEBUG_TOOLBAR=(bool, False),
71 | {%- if cookiecutter.feature_annotations == "on" %}
72 | # END_FEATURE debug_toolbar
73 | {%- endif %}
74 | {%- endif %}
75 | )
76 | # If ALLWED_HOSTS has been configured, then we're running on a server and
77 | # can skip looking for a .env file (this assumes that .env files
78 | # file is only used for local development and servers use environment variables)
79 | if not env("ALLOWED_HOSTS"):
80 | environ.Env.read_env()
81 |
82 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
83 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
84 |
85 | # SECURITY WARNING: keep the secret key used in production secret!
86 | SECRET_KEY = env("SECRET_KEY")
87 |
88 | # SECURITY WARNING: do not run with debug turned on in production!
89 | DEBUG = env("DEBUG")
90 | {%- if cookiecutter.debug_toolbar == "enabled" %}
91 | {% if cookiecutter.feature_annotations == "on" %}
92 | # START_FEATURE debug_toolbar
93 | {%- endif %}
94 | DEBUG_TOOLBAR = DEBUG and env("DEBUG_TOOLBAR")
95 | {%- if cookiecutter.feature_annotations == "on" %}
96 | # END_FEATURE debug_toolbar
97 | {%- endif %}
98 | {%- endif %}
99 |
100 | # run with this set to False on server environments
101 | LOCALHOST = env("LOCALHOST")
102 |
103 | # set PRODUCTION to be False on non-production server environments to prevent
104 | # them from being indexed by search engines and to have a banner warning
105 | # that this is not the production site
106 | PRODUCTION = env("PRODUCTION")
107 |
108 | ALLOWED_HOSTS = env("ALLOWED_HOSTS")
109 | if LOCALHOST is True:
110 | ALLOWED_HOSTS = ["127.0.0.1", "localhost"]
111 | {%- if cookiecutter.elastic_beanstalk == "enabled" %}
112 | else:
113 | {%- if cookiecutter.feature_annotations == "on" %}
114 | # START_FEATURE elastic_beanstalk
115 | {%- endif %}
116 | # if using AWS hosting
117 | from ec2_metadata import ec2_metadata
118 | ALLOWED_HOSTS.append(ec2_metadata.private_ipv4)
119 | {%- if cookiecutter.feature_annotations == "on" %}
120 | # END_FEATURE elastic_beanstalk
121 | {%- endif %}
122 | {%- endif %}
123 |
124 | # Application definition
125 | THIRD_PARTY_APPS = [
126 | "django.contrib.admin",
127 | "django.contrib.auth",
128 | "django.contrib.contenttypes",
129 | "django.contrib.sessions",
130 | "django.contrib.messages",
131 | "django.contrib.staticfiles",
132 | "django_extensions",
133 | {%- if cookiecutter.django_social == "enabled" %}
134 | {%- if cookiecutter.feature_annotations == "on" %}
135 |
136 | # START_FEATURE django_social
137 | {%- endif %}
138 | "social_django",
139 | {%- if cookiecutter.feature_annotations == "on" %}
140 | # END_FEATURE django_social
141 | {%- endif %}
142 | {%- endif %}
143 | {%- if cookiecutter.crispy_forms == "enabled" %}
144 | {%- if cookiecutter.feature_annotations == "on" %}
145 |
146 | # START_FEATURE crispy_forms
147 | {%- endif %}
148 | "crispy_forms",
149 | "crispy_bootstrap5",
150 | {%- if cookiecutter.feature_annotations == "on" %}
151 | # END_FEATURE crispy_forms
152 | {%- endif %}
153 | {%- endif %}
154 | {%- if cookiecutter.django_react == "enabled" %}
155 | {%- if cookiecutter.feature_annotations == "on" %}
156 |
157 | # START_FEATURE django_react
158 | {%- endif %}
159 | "django_react_components",
160 | "webpack_loader",
161 | {%- if cookiecutter.feature_annotations == "on" %}
162 | # END_FEATURE django_react
163 | {%- endif %}
164 | {%- endif %}
165 | {%- if cookiecutter.sass_bootstrap == "enabled" %}
166 | {%- if cookiecutter.feature_annotations == "on" %}
167 |
168 | # START_FEATURE sass_bootstrap
169 | {%- endif %}
170 | "sass_processor",
171 | {%- if cookiecutter.feature_annotations == "on" %}
172 | # END_FEATURE sass_bootstrap
173 | {%- endif %}
174 | {%- endif %}
175 | ]
176 | {%- if cookiecutter.debug_toolbar == "enabled" %}
177 |
178 | {% if cookiecutter.feature_annotations == "on" %}
179 | # START_FEATURE debug_toolbar
180 | {%- endif %}
181 | if DEBUG_TOOLBAR:
182 | THIRD_PARTY_APPS += ["debug_toolbar"]
183 | {%- if cookiecutter.feature_annotations == "on" %}
184 | # END_FEATURE debug_toolbar
185 | {%- endif %}
186 | {%- endif %}
187 |
188 | LOCAL_APPS = [
189 | "common",
190 | ]
191 |
192 | INSTALLED_APPS = THIRD_PARTY_APPS + LOCAL_APPS
193 |
194 | MIDDLEWARE = [
195 | "django.middleware.security.SecurityMiddleware",
196 | "django.contrib.sessions.middleware.SessionMiddleware",
197 | "django.middleware.common.CommonMiddleware",
198 | "common.middleware.MaintenanceModeMiddleware",
199 | "django.middleware.csrf.CsrfViewMiddleware",
200 | "django.contrib.auth.middleware.AuthenticationMiddleware",
201 | "django.contrib.messages.middleware.MessageMiddleware",
202 | "django.middleware.clickjacking.XFrameOptionsMiddleware",
203 | {%- if cookiecutter.user_action_tracking == "enabled" %}
204 | {%- if cookiecutter.feature_annotations == "on" %}
205 | # START_FEATURE user_action_tracking
206 | {%- endif %}
207 | "common.middleware.UserActionTrackingMiddleware",
208 | {%- if cookiecutter.feature_annotations == "on" %}
209 | # END_FEATURE user_action_tracking
210 | {%- endif %}
211 | {%- endif %}
212 | ]
213 |
214 | MAINTENANCE_MODE = env("MAINTENANCE_MODE")
215 | {%- if cookiecutter.user_action_tracking == "enabled" %}
216 |
217 | {% if cookiecutter.feature_annotations == "on" %}
218 | # START_FEATURE user_action_tracking
219 | {%- endif %}
220 | USER_TRACKING_EXEMPT_ROUTES = []
221 | {%- if cookiecutter.feature_annotations == "on" %}
222 | # END_FEATURE user_action_tracking
223 | {%- endif %}
224 | {%- endif %}
225 |
226 |
227 | ROOT_URLCONF = "config.urls"
228 |
229 | TEMPLATES = [
230 | {
231 | "BACKEND": "django.template.backends.django.DjangoTemplates",
232 | "DIRS": [],
233 | "APP_DIRS": True,
234 | "OPTIONS": {
235 | "context_processors": [
236 | "django.template.context_processors.debug",
237 | "django.template.context_processors.request",
238 | "django.contrib.auth.context_processors.auth",
239 | "django.contrib.messages.context_processors.messages",
240 | "common.context_processors.django_settings",
241 | ],
242 | },
243 | },
244 | ]
245 |
246 | WSGI_APPLICATION = "config.wsgi.application"
247 |
248 |
249 | # Database
250 | # https://docs.djangoproject.com/en/2.2/ref/settings/#databases
251 |
252 | DATABASES = {"default": env.db()}
253 |
254 | DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
255 |
256 | # Password validation
257 | # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
258 |
259 | AUTH_PASSWORD_VALIDATORS = [
260 | {
261 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
262 | },
263 | {
264 | "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
265 | },
266 | {
267 | "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
268 | },
269 | {
270 | "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
271 | },
272 | ]
273 | {%- if cookiecutter.django_ses == "enabled" %}
274 |
275 | {% if cookiecutter.feature_annotations == "on" %}
276 | # START_FEATURE django_ses
277 | {%- endif %}
278 | if LOCALHOST:
279 | EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
280 | DEFAULT_FROM_EMAIL = "webmaster@localhost"
281 | else:
282 | EMAIL_BACKEND = "django_ses.SESBackend"
283 | AWS_SES_REGION_NAME = env("AWS_SES_REGION_NAME")
284 | AWS_SES_REGION_ENDPOINT = env("AWS_SES_REGION_ENDPOINT")
285 | AWS_SES_RETURN_PATH = env("DEFAULT_FROM_EMAIL")
286 | DEFAULT_FROM_EMAIL = env("DEFAULT_FROM_EMAIL")
287 | {%- if cookiecutter.feature_annotations == "on" %}
288 | # END_FEATURE django_ses
289 | {%- endif %}
290 | {%- endif %}
291 |
292 | # Logging
293 | # https://docs.djangoproject.com/en/dev/topics/logging/#django-security
294 | LOGGING = {
295 | "version": 1,
296 | "disable_existing_loggers": False,
297 | "formatters": {
298 | "verbose": {
299 | "format": "%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s"
300 | },
301 | "simple": {
302 | "format": "%(levelname)s %(message)s"
303 | },
304 | },
305 | "handlers": {
306 | "console": {
307 | "level": "WARNING",
308 | "class": "logging.StreamHandler",
309 | "formatter": "simple",
310 | },
311 | "null": {
312 | "class": "logging.NullHandler",
313 | },
314 | },
315 | "loggers": {
316 | "django.request": {
317 | "handlers": ["console"]
318 | },
319 | "django.security.DisallowedHost": {
320 | "handlers": ["null"],
321 | "propagate": False,
322 | },
323 | },
324 | }
325 |
326 |
327 | # Internationalization
328 | # https://docs.djangoproject.com/en/2.2/topics/i18n/
329 |
330 | LANGUAGE_CODE = "en-us"
331 |
332 | TIME_ZONE = "UTC"
333 |
334 | USE_I18N = True
335 |
336 | USE_L10N = True
337 |
338 | USE_TZ = True
339 |
340 |
341 | # Static files (CSS, JavaScript, Images)
342 | # https://docs.djangoproject.com/en/2.2/howto/static-files/
343 |
344 | STATIC_URL = "/static/"
345 | STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
346 | STATICFILES_DIRS = [
347 | os.path.join(BASE_DIR, "static"),
348 | ]
349 |
350 | AUTH_USER_MODEL = "common.User"
351 | {%- if cookiecutter.django_social == "enabled" %}
352 |
353 | {% if cookiecutter.feature_annotations == "on" %}
354 | # START_FEATURE django_social
355 | {%- endif %}
356 | AUTHENTICATION_BACKENDS = [
357 | "social_core.backends.google.GoogleOAuth2",
358 | ]
359 |
360 | LOGIN_URL = "index"
361 | LOGIN_REDIRECT_URL = "index"
362 |
363 | SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = env("GOOGLE_OAUTH2_KEY")
364 | SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = env("GOOGLE_OAUTH2_SECRET")
365 | {%- if cookiecutter.feature_annotations == "on" %}
366 | # END_FEATURE django_social
367 | {%- endif %}
368 | {%- endif %}
369 | {%- if cookiecutter.crispy_forms == "enabled" %}
370 |
371 | {% if cookiecutter.feature_annotations == "on" %}
372 | # START_FEATURE crispy_forms
373 | {%- endif %}
374 | CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
375 | CRISPY_TEMPLATE_PACK = "bootstrap5"
376 | {%- if cookiecutter.feature_annotations == "on" %}
377 | # END_FEATURE crispy_forms
378 | {%- endif %}
379 | {%- endif %}
380 | {%- if cookiecutter.bootstrap_messages == "enabled" %}
381 |
382 | {% if cookiecutter.feature_annotations == "on" %}
383 | # START_FEATURE bootstrap_messages
384 | {%- endif %}
385 | # Bootstrap styling for Django messages
386 | MESSAGE_TAGS = {
387 | messages.DEBUG: "alert-info",
388 | messages.INFO: "alert-info",
389 | messages.SUCCESS: "alert-success",
390 | messages.WARNING: "alert-warning",
391 | messages.ERROR: "alert-danger",
392 | }
393 | {%- if cookiecutter.feature_annotations == "on" %}
394 | # END_FEATURE bootstrap_messages
395 | {%- endif %}
396 | {%- endif %}
397 |
398 | {%- if cookiecutter.django_storages == "enabled" %}
399 |
400 | {% if cookiecutter.feature_annotations == "on" %}
401 | # START_FEATURE django_storages
402 | {%- endif %}
403 | if LOCALHOST is True:
404 | DEFAULT_STORAGE = {"BACKEND": "django.core.files.storage.FileSystemStorage"}
405 | MEDIA_ROOT = ""
406 | else:
407 | DEFAULT_STORAGE = {
408 | "BACKEND": "storages.backends.s3boto3.S3Boto3Storage",
409 | "OPTIONS": {
410 | "bucket_name": env("AWS_STORAGE_BUCKET_NAME"),
411 | "file_overwrite": False,
412 | "default_acl": "private",
413 | }
414 | }
415 | {%- if cookiecutter.feature_annotations == "on" %}
416 | # END_FEATURE django_storages
417 | {%- endif %}
418 | {%- else %}
419 | DEFAULT_STORAGE = {"BACKEND": "django.core.files.storage.FileSystemStorage"}
420 | {%- endif %}
421 | STORAGES = {
422 | "default": DEFAULT_STORAGE,
423 | {%- if cookiecutter.sass_bootstrap == "enabled" %}
424 | {%- if cookiecutter.feature_annotations == "on" %}
425 | # START_FEATURE sass_bootstrap
426 | {%- endif %}
427 | "sass_processor": {
428 | "BACKEND": "django.core.files.storage.FileSystemStorage",
429 | "OPTIONS": {
430 | "base_url": STATIC_URL,
431 | "location": os.path.join(BASE_DIR, 'static'),
432 | },
433 | "ROOT": os.path.join(BASE_DIR, 'static'),
434 | },
435 | {%- if cookiecutter.feature_annotations == "on" %}
436 | # END_FEATURE sass_bootstrap
437 | {%- endif %}
438 | {%- endif %}
439 | "staticfiles": {
440 | "BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage",
441 | },
442 | }
443 |
444 | STATICFILES_FINDERS = [
445 | 'django.contrib.staticfiles.finders.FileSystemFinder',
446 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
447 | 'sass_processor.finders.CssFinder',
448 | ]
449 |
450 | {%- if cookiecutter.debug_toolbar == "enabled" %}
451 |
452 | {% if cookiecutter.feature_annotations == "on" %}
453 | # START_FEATURE debug_toolbar
454 | {%- endif %}
455 | INTERNAL_IPS = ["127.0.0.1"]
456 | if DEBUG_TOOLBAR:
457 | MIDDLEWARE.append("debug_toolbar.middleware.DebugToolbarMiddleware")
458 | {%- if cookiecutter.feature_annotations == "on" %}
459 | # END_FEATURE debug_toolbar
460 | {%- endif %}
461 | {%- endif %}
462 | {%- if cookiecutter.django_react == "enabled" %}
463 |
464 | {% if cookiecutter.feature_annotations == "on" %}
465 | # START_FEATURE django_react
466 | {%- endif %}
467 | if DEBUG:
468 | WEBPACK_LOADER_HOTLOAD = env("WEBPACK_LOADER_HOTLOAD")
469 | if WEBPACK_LOADER_HOTLOAD:
470 | WEBPACK_LOADER = {
471 | "DEFAULT": {
472 | "LOADER_CLASS": "config.webpack_loader.DynamicWebpackLoader"
473 | }
474 | }
475 | {%- if cookiecutter.feature_annotations == "on" %}
476 | # END_FEATURE django_react
477 | {%- endif %}
478 | {%- endif %}
479 | {%- if cookiecutter.sentry == "enabled" %}
480 |
481 | {% if cookiecutter.feature_annotations == "on" %}
482 | # START_FEATURE sentry
483 | {%- endif %}
484 | SENTRY_DSN = env("SENTRY_DSN")
485 | if LOCALHOST is False and SENTRY_DSN:
486 | import sentry_sdk
487 | from sentry_sdk.integrations.django import DjangoIntegration
488 | from sentry_sdk.integrations.logging import ignore_logger
489 | sentry_sdk.init(
490 | dsn=env("SENTRY_DSN"),
491 | integrations=[DjangoIntegration()],
492 | traces_sample_rate=1.0,
493 | )
494 | # Silence "invalid HTTP_HOST" errors
495 | ignore_logger("django.security.DisallowedHost")
496 |
497 | {%- if cookiecutter.feature_annotations == "on" %}
498 | # END_FEATURE sentry
499 | {%- endif %}
500 | {%- endif %}
501 | {%- if cookiecutter.security_settings == "enabled" %}
502 |
503 | {% if cookiecutter.feature_annotations == "on" %}
504 | # START_FEATURE security_settings
505 | {%- endif %}
506 | if LOCALHOST is False:
507 | SECURE_SSL_REDIRECT = True
508 | SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
509 |
510 | USE_X_FORWARDED_HOST = True
511 | X_FRAME_OPTIONS = "DENY"
512 | SECURE_REFERRER_POLICY = "same-origin"
513 | SECURE_BROWSER_XSS_FILTER = True
514 | SECURE_CONTENT_TYPE_NOSNIFF = True
515 |
516 | SECURE_HSTS_SECONDS = 3600 # 1 hour
517 | # TODO: increase SECURE_HSTS_SECONDS and register with hstspreload.org once production deploy is stable
518 | # SECURE_HSTS_SECONDS = 3600 * 24 * 365 * 2 # 2 years
519 | SECURE_HSTS_INCLUDE_SUBDOMAINS = True
520 | SECURE_HSTS_PRELOAD = True
521 |
522 | SESSION_COOKIE_SECURE = True
523 | SESSION_COOKIE_HTTPONLY = True
524 | SESSION_COOKIE_AGE = 60 * 60 * 3 # 3 hours
525 | CSRF_COOKIE_SECURE = True
526 | CSRF_COOKIE_HTTPONLY = True # Only do this if you are not accessing the CSRF cookie with JS
527 | {%- if cookiecutter.feature_annotations == "on" %}
528 | # END_FEATURE security_settings
529 | {%- endif %}
530 | {%- endif %}
531 | {%- if cookiecutter.sass_bootstrap == "enabled" %}
532 |
533 | {% if cookiecutter.feature_annotations == "on" %}
534 | # START_FEATURE sass_bootstrap
535 | {%- endif %}
536 | SASS_PRECISION = 8 # Bootstrap's sass requires a precision of at least 8 to prevent layout errors
537 | SASS_PROCESSOR_CUSTOM_FUNCTIONS = {
538 | 'django-static': 'django.templatetags.static.static',
539 | }
540 | SASS_PROCESSOR_INCLUDE_DIRS = [
541 | os.path.join(BASE_DIR, 'static/styles'),
542 | os.path.join(BASE_DIR, 'node_modules'),
543 | ]
544 | COMPRESS_ROOT = STORAGES["sass_processor"]["ROOT"]
545 | {%- if cookiecutter.feature_annotations == "on" %}
546 | # END_FEATURE sass_bootstrap
547 | {%- endif %}
548 | {%- endif %}
549 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/config/setup.cfg:
--------------------------------------------------------------------------------
1 | [flake8]
2 | max-line-length = 120
3 | exclude = migrations,env
4 |
5 | [isort]
6 | default_section = THIRDPARTY
7 | known_first_party = core
8 | known_django = django
9 | sections = FUTURE,STDLIB,DJANGO,THIRDPARTY,FIRSTPARTY,LOCALFOLDER
10 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/config/urls.py:
--------------------------------------------------------------------------------
1 | """config URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/2.2/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: path('', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.urls import include, path
14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
15 | """
16 | from django.conf.urls import include
17 | from django.contrib import admin
18 | from django.http import HttpResponse
19 | from django.urls import path
20 |
21 | urlpatterns = [
22 | path('admin/', admin.site.urls),
23 | {%- if cookiecutter.django_social == "enabled" %}
24 | path("oauth/", include("social_django.urls", namespace="social")),
25 | {%- endif %}
26 | path('', include("common.urls")),
27 | path("health-check/", lambda request: HttpResponse("ok")),
28 | ]
29 |
30 | handler404 = "common.views.error_404"
31 | handler500 = "common.views.error_500"
32 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/config/webpack_loader.py:
--------------------------------------------------------------------------------
1 | {%- if cookiecutter.django_react == "enabled" -%}
2 | {%- if cookiecutter.feature_annotations == "on" -%}
3 | # START_FEATURE django_react
4 | {%- endif %}
5 |
6 | from django.conf import settings
7 |
8 | from webpack_loader.loader import WebpackLoader
9 |
10 |
11 | class DynamicWebpackLoader(WebpackLoader):
12 | """
13 | Custom django-webpack-loader loader to allow for hotloading in development.
14 | """
15 |
16 | def get_chunk_url(self, chunk):
17 | path = super().get_chunk_url(chunk)
18 | if settings.WEBPACK_LOADER_HOTLOAD:
19 | path = f"http://localhost:3000{path}"
20 | return path
21 | {%- if cookiecutter.feature_annotations == "on" %}
22 |
23 | # END_FEATURE django_react
24 | {%- endif %}
25 | {%- endif %}
26 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/config/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for config project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 | import os
4 | import sys
5 |
6 |
7 | def main():
8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
9 | try:
10 | from django.core.management import execute_from_command_line
11 | except ImportError as exc:
12 | raise ImportError(
13 | "Couldn't import Django. Are you sure it's installed and "
14 | "available on your PYTHONPATH environment variable? Did you "
15 | "forget to activate a virtual environment?"
16 | ) from exc
17 | execute_from_command_line(sys.argv)
18 |
19 |
20 | if __name__ == '__main__':
21 | main()
22 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/nwb.config.js:
--------------------------------------------------------------------------------
1 | {%- if cookiecutter.django_react == "enabled" -%}
2 | {%- if cookiecutter.feature_annotations == "on" -%}
3 | /*
4 | * START_FEATURE django_react
5 | */
6 |
7 |
8 | {% endif -%}
9 | const path = require('path')
10 | const BundleTracker = require('webpack-bundle-tracker')
11 |
12 |
13 | /**
14 | * Entry point configuration.
15 | *
16 | * This is where you should configure your webpack entry points, for example, a different entry point per page.
17 | */
18 |
19 | const ENTRIES = {
20 | {%- if cookiecutter.reference_examples == "on" %}
21 | // TODO delete me; this is just a reference example
22 | Home: './src/Pages/Home.js',
23 | Hello: './src/Components/Hello.js'
24 | {%- endif %}
25 | }
26 |
27 | const SHARED_ENTRIES = [
28 | // we have not included react-app-polyfill in this sample application because it is not technically required for
29 | // projects which do not require polyfill. However, we do recommend installing and uncommenting this line of code
30 | // for projects which will need to have guaranteed functionality on older browsers.
31 | // './node_modules/react-app-polyfill/ie11.js',
32 | ]
33 |
34 | /**
35 | * nwb config
36 | */
37 | module.exports = function({command}) {
38 |
39 | /* Set config */
40 | const config = {
41 | type: 'react-app',
42 | }
43 | config.webpack = {
44 | config(webpackConfig) {
45 |
46 | // Set new entry configuration
47 | webpackConfig.entry = {}
48 | Object.keys(ENTRIES).forEach((entryKey) => {
49 | webpackConfig.entry[entryKey] = [...SHARED_ENTRIES, ENTRIES[entryKey]]
50 | })
51 | return webpackConfig
52 | },
53 | extra: {
54 | output: {
55 | filename: '[name].js',
56 | chunkFilename: '[name].js',
57 | path: path.resolve('./static/webpack_bundles/'),
58 | },
59 | module: {
60 | rules: [
61 | {
62 | test: /\.js$/,
63 | exclude: /node_modules/,
64 | loader: 'django-react-loader',
65 | },
66 | ],
67 | },
68 | plugins: [
69 | new BundleTracker({filename: './webpack-stats.json'}),
70 | ],
71 | },
72 | publicPath: '/static/webpack_bundles/',
73 | }
74 | return config
75 | }
76 | {% if cookiecutter.feature_annotations == "on" %}
77 | /*
78 | * END_FEATURE django_react
79 | */
80 | {%- endif %}
81 | {%- endif %}
82 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sample-django-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | {% if cookiecutter.sass_bootstrap == "enabled" -%}
7 | "bootstrap": "^5.1.1"{% if cookiecutter.django_react == "enabled" %},{% endif %}
8 | {%- endif %}
9 | {% if cookiecutter.django_react == "enabled" -%}
10 | "django-react-loader": "^0.1.7",
11 | "nwb": "^0.24.7",
12 | "react": "^16.13.1",
13 | "react-dom": "^16.13.1",
14 | "webpack-bundle-tracker": "^0.4.3",
15 | "@babel/plugin-transform-react-jsx": "~7.16.7"
16 | {%- endif %}
17 | },
18 | {% if cookiecutter.django_react == "enabled" -%}
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | {%- endif -%}
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/{{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 %}
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/requirements-dev.in:
--------------------------------------------------------------------------------
1 | -r requirements.in
2 | -c requirements.txt
3 |
4 | werkzeug
5 | pip-tools
6 | coverage
7 |
8 | ####### OPTIONAL FEATURES #######
9 | {%- if cookiecutter.elastic_beanstalk == "enabled" %}
10 | {%- if cookiecutter.feature_annotations == "on" %}
11 | # START_FEATURE elastic_beanstalk
12 | {%- endif %}
13 | {%- if cookiecutter.feature_annotations == "on" %}
14 | # END_FEATURE elastic_beanstalk
15 | {%- endif %}
16 | {%- endif %}
17 | {%- if cookiecutter.pre_commit == "enabled" %}
18 | {%- if cookiecutter.feature_annotations == "on" %}
19 |
20 | # START_FEATURE pre_commit
21 | {%- endif %}
22 | pre-commit
23 | flake8
24 | isort
25 | {%- if cookiecutter.feature_annotations == "on" %}
26 | # END_FEATURE pre_commit
27 | {%- endif %}
28 | {%- endif %}
29 | {%- if cookiecutter.debug_toolbar == "enabled" %}
30 | {%- if cookiecutter.feature_annotations == "on" %}
31 |
32 | # START_FEATURE debug_toolbar
33 | {%- endif %}
34 | django-debug-toolbar
35 | {%- if cookiecutter.feature_annotations == "on" %}
36 | # END_FEATURE debug_toolbar
37 | {%- endif %}
38 | {%- endif %}
39 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/requirements.in:
--------------------------------------------------------------------------------
1 | Django==4.2.* # Latest LTS version
2 | django-environ
3 | django-extensions>=3.2
4 | requests
5 | psycopg2
6 | {%- if cookiecutter.feature_annotations == "on" %}
7 |
8 | ####### OPTIONAL FEATURES #######
9 | {%- endif %}
10 | {%- if cookiecutter.elastic_beanstalk == "enabled" %}
11 | {%- if cookiecutter.feature_annotations == "on" %}
12 | # START_FEATURE elastic_beanstalk
13 | {%- endif %}
14 | ec2_metadata
15 | {%- if cookiecutter.feature_annotations == "on" %}
16 | # END_FEATURE elastic_beanstalk
17 | {%- endif %}
18 | {%- endif %}
19 | {%- if cookiecutter.django_social == "enabled" %}
20 | {%- if cookiecutter.feature_annotations == "on" %}
21 |
22 | # START_FEATURE django_social
23 | {%- endif %}
24 | social-auth-app-django
25 | {%- if cookiecutter.feature_annotations == "on" %}
26 | # END_FEATURE django_social
27 | {%- endif %}
28 | {%- endif %}
29 | {%- if cookiecutter.crispy_forms == "enabled" %}
30 | {%- if cookiecutter.feature_annotations == "on" %}
31 |
32 | # START_FEATURE crispy_forms
33 | {%- endif %}
34 | django-crispy-forms
35 | crispy-bootstrap5
36 | {%- if cookiecutter.feature_annotations == "on" %}
37 | # END_FEATURE crispy_forms
38 | {%- endif %}
39 | {%- endif %}
40 | {%- if cookiecutter.django_react == "enabled" %}
41 | {%- if cookiecutter.feature_annotations == "on" %}
42 |
43 | # START_FEATURE django_react
44 | {%- endif %}
45 | django-react-components
46 | django-webpack-loader==0.7.0
47 | {%- if cookiecutter.feature_annotations == "on" %}
48 | # END_FEATURE django_react
49 | {%- endif %}
50 | {%- endif %}
51 | {%- if cookiecutter.sentry == "enabled" %}
52 | {%- if cookiecutter.feature_annotations == "on" %}
53 |
54 | # START_FEATURE sentry
55 | {%- endif %}
56 | sentry-sdk
57 | {%- if cookiecutter.feature_annotations == "on" %}
58 | # END_FEATURE sentry
59 | {%- endif %}
60 | {%- endif %}
61 | {%- if cookiecutter.django_storages == "enabled" %}
62 | {%- if cookiecutter.feature_annotations == "on" %}
63 |
64 | # START_FEATURE django_storages
65 | {%- endif %}
66 | django-storages
67 | {%- if cookiecutter.feature_annotations == "on" %}
68 | # END_FEATURE django_storages
69 | {%- endif %}
70 | {%- endif %}
71 | {%- if cookiecutter.docker == "enabled" %}
72 | {%- if cookiecutter.feature_annotations == "on" %}
73 |
74 | # START_FEATURE docker
75 | {%- endif %}
76 | gunicorn
77 | {%- if cookiecutter.feature_annotations == "on" %}
78 | # END_FEATURE docker
79 | {%- endif %}
80 | {%- endif %}
81 | {%- if cookiecutter.sentry == "enabled" %}
82 | {%- if cookiecutter.feature_annotations == "on" %}
83 |
84 | {%- endif %}
85 | {%- endif %}
86 | {%- if cookiecutter.django_ses == "enabled" %}
87 | {%- if cookiecutter.feature_annotations == "on" %}
88 |
89 | # START_FEATURE django_ses
90 | {%- endif %}
91 | django-ses
92 | {%- if cookiecutter.feature_annotations == "on" %}
93 | # END_FEATURE django_ses
94 | {%- endif %}
95 | {%- endif %}
96 |
97 | {%- if cookiecutter.sass_bootstrap == "enabled" %}
98 | {%- if cookiecutter.feature_annotations == "on" %}
99 |
100 | # START_FEATURE sass_bootstrap
101 | {%- endif %}
102 | django-compressor
103 | django-sass-processor
104 | libsass
105 | {%- if cookiecutter.feature_annotations == "on" %}
106 | # END_FEATURE sass_bootstrap
107 | {%- endif %}
108 | {%- endif %}
109 | {%- if cookiecutter.feature_annotations == "on" %}
110 |
111 | ################################
112 | {%- endif %}
113 |
114 | # Convenience
115 | ipython
116 | {%- if cookiecutter.debug_toolbar == "on" %}
117 | django-debug-toolbar
118 | {%- endif %}
119 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/src/Components/Hello.js:
--------------------------------------------------------------------------------
1 | {%- if cookiecutter.reference_examples == "on" -%}
2 | /*
3 | * # TODO: delete me; this is just a reference example
4 | */
5 |
6 | import React from 'react';
7 |
8 | function Hello(props) {
9 | return (
10 |
11 |
Hello {props.msg}
12 |
13 | );
14 | }
15 |
16 | export default Hello;
17 | {%- endif %}
18 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/src/Pages/Home.js:
--------------------------------------------------------------------------------
1 | {%- if cookiecutter.reference_examples == "on" -%}
2 | /*
3 | * # TODO: delete me; this is just a reference example
4 | */
5 |
6 | import React from 'react';
7 | import Hello from "../Components/Hello";
8 |
9 | function Home(props) {
10 | const {message} = props;
11 | return (
12 |
13 |
You may want to wrap several components in a page, but you don't have to.
14 |
15 |
16 | );
17 | }
18 |
19 | export default Home;
20 | {%- endif %}
21 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/static/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zagaran/django-template/92fb23aae04d6bbdcbc308152982c083395ca480/{{cookiecutter.project_slug}}/static/favicon.png
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/static/styles/_variables.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Sass variables
3 | */
4 |
5 | /* Override Bootstrap default variables */
6 | {%- if cookiecutter.reference_examples == "on" %}
7 |
8 | // TODO: delete me; this is just a reference example
9 | $primary: #2123a7;
10 | {%- endif %}
11 |
12 | @import 'bootstrap/scss/_functions.scss';
13 | @import 'bootstrap/scss/_variables.scss';
14 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/static/styles/base.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Base app styling
3 | */
4 | @import '_variables.scss';
5 | @import 'bootstrap/scss/bootstrap.scss';
6 |
--------------------------------------------------------------------------------
/{{cookiecutter.project_slug}}/static/styles/pages/index.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Base app index
3 | */
4 | {% if cookiecutter.reference_examples == "on" -%}
5 | // TODO: delete me; this is just a reference example
6 | .does-nothing-button {
7 | &:focus {
8 | -webkit-animation: rainbow 2s ease-in-out infinite;
9 | animation: rainbow 2s ease-in-out infinite;
10 | background: linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet);
11 | background-size: 1000% 1000%;
12 |
13 | &:after {
14 | content: "... just kidding!";
15 | }
16 | }
17 |
18 | @keyframes rainbow {
19 | 0% {
20 | background-position: 0% 80%;
21 | }
22 | 25% {
23 | background-position: 50% 50%;
24 | }
25 | 50% {
26 | background-position: 100% 20%;
27 | }
28 | 75% {
29 | background-position: 50% 50%;
30 | }
31 | 100% {
32 | background-position: 0% 80%;
33 | }
34 | }
35 | }
36 | {%- endif %}
37 |
--------------------------------------------------------------------------------