├── project_name
├── __init__.py
├── settings
│ ├── __init__.py
│ ├── env.py
│ ├── production.py
│ ├── dev.py
│ └── base.py
├── static
│ └── .gitkeep
├── example.env
├── templates
│ ├── sample_index.html
│ └── base.html
├── urls.py
├── wsgi.py
└── context_processors.py
├── Pipfile
├── manage.py
├── docs
├── template_blocks.md
└── dependencies.md
├── .gitignore
└── README.md
/project_name/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/project_name/settings/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/project_name/static/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/project_name/settings/env.py:
--------------------------------------------------------------------------------
1 | import cfenv
2 |
3 | env = cfenv.AppEnv()
--------------------------------------------------------------------------------
/project_name/example.env:
--------------------------------------------------------------------------------
1 | ENVIRONMENT='DEVELOPMENT'
2 | DJANGO_SECRET_KEY='{{ secret_key }}'
3 | DJANGO_DEBUG='yes'
4 | DJANGO_TEMPLATE_DEBUG='yes'
5 |
--------------------------------------------------------------------------------
/project_name/settings/production.py:
--------------------------------------------------------------------------------
1 | from .base import *
2 |
3 | SESSION_COOKIE_SECURE = True
4 | SESSION_COOKIE_HTTPONLY = True
5 | CSRF_COOKIE_SECURE = True
6 | CSRF_COOKIE_HTTPONLY = True
7 |
8 | DATABASES['default'] = dj_database_url.config(
9 | default='postgres://{{ project_name }}:{{ project_name }}@localhost/{{ project_name }}'
10 | )
11 |
12 | LOGGING = {}
13 |
14 |
--------------------------------------------------------------------------------
/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | url = "https://pypi.python.org/simple"
3 | verify_ssl = true
4 | name = "pypi"
5 |
6 | [dev-packages]
7 | coverage = "*"
8 | django-debug-toolbar = "==1.10.1"
9 | factory-boy = "*"
10 | flake8 = "*"
11 |
12 | [packages]
13 | Django = ">=2.0"
14 | cfenv = "*"
15 | cg-django-uaa = "*"
16 | dj-database-url = "*"
17 | docutils = ">=0.10"
18 | typogrify = ">=2.0.0"
19 | psycopg2-binary = "*"
20 | whitenoise = "*"
21 |
--------------------------------------------------------------------------------
/project_name/templates/sample_index.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block hero %}
4 |
5 |
6 |
7 |
8 | This is sample content.
9 | This is only sample content.
10 |
11 |
You'll want to replace it with content of your own.
12 |
13 |
14 |
15 | {% endblock %}
16 |
--------------------------------------------------------------------------------
/project_name/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.contrib import admin
3 | from django.urls import include, path
4 | from django.views.generic import TemplateView
5 |
6 |
7 | urlpatterns = [
8 | path('admin/', admin.site.urls),
9 | path('', TemplateView.as_view(template_name="sample_index.html"), name="home"),
10 | ]
11 |
12 | if settings.DEBUG:
13 | import debug_toolbar
14 | urlpatterns = [
15 | path('__debug__/', include(debug_toolbar.urls)),
16 | ] + urlpatterns
17 |
--------------------------------------------------------------------------------
/project_name/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for {{ project_name }} project.
3 | It exposes the WSGI callable as a module-level variable named ``application``.
4 | For more information on this file, see
5 | https://docs.djangoproject.com/en/{{ docs_version }}/howto/deployment/wsgi/
6 | """
7 | import os
8 | from django.core.wsgi import get_wsgi_application
9 |
10 | from .settings.env import env
11 |
12 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{{ project_name }}.settings.production')
13 |
14 | application = get_wsgi_application()
15 |
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == '__main__':
6 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{{ project_name }}.settings.dev')
7 |
8 | try:
9 | from django.core.management import execute_from_command_line
10 | except ImportError as exc:
11 | raise ImportError(
12 | 'Couldn\'t import Django. Are you sure it\'s installed and '
13 | 'available on your PYTHONPATH environment variable? Did you '
14 | 'forget to activate a virtual environment?') from exc
15 | execute_from_command_line(sys.argv)
16 |
--------------------------------------------------------------------------------
/project_name/context_processors.py:
--------------------------------------------------------------------------------
1 | import datetime
2 |
3 | from django.conf import settings
4 | from django.contrib.sites.shortcuts import get_current_site
5 |
6 |
7 | def site_processor(request):
8 | # convenience method to avoid DB hit of request.user
9 | authenticated_request = request.user.is_authenticated
10 |
11 | return {
12 | 'site': get_current_site(request),
13 | 'project_name': getattr(settings, 'PROJECT_NAME', None),
14 | 'current_path': request.get_full_path(),
15 | 'authenticated_request': authenticated_request,
16 | 'agency': getattr(settings, 'AGENCY', None),
17 | }
--------------------------------------------------------------------------------
/project_name/settings/dev.py:
--------------------------------------------------------------------------------
1 | from .base import *
2 |
3 | DEBUG = True
4 |
5 | # https://docs.djangoproject.com/en/{{ docs_version }}/ref/settings/#databases
6 | DATABASES = {
7 | 'default': {
8 | 'ENGINE': 'django.db.backends.sqlite3',
9 | 'NAME': '{{ project_name }}_dev_db',
10 | }
11 | }
12 |
13 | INTERNAL_IPS = [
14 | '127.0.0.1'
15 | ]
16 |
17 | MIDDLEWARE = MIDDLEWARE + [
18 | 'debug_toolbar.middleware.DebugToolbarMiddleware'
19 | ]
20 |
21 | INSTALLED_APPS = INSTALLED_APPS + [
22 | 'debug_toolbar'
23 | ]
24 |
25 | UAA_CLIENT_ID = 'testtesttesttesttesttesttesttesttesttesttest'
26 | UAA_CLIENT_SECRET = 'testtesttesttesttesttesttesttesttesttest'
27 | UAA_AUTH_URL = 'fake:'
28 | UAA_TOKEN_URL = 'fake:'
29 |
30 |
--------------------------------------------------------------------------------
/docs/template_blocks.md:
--------------------------------------------------------------------------------
1 | These are template blocks defined in `base.html`
2 |
3 | | Block name | Location and use |
4 | |---------------|----------------------------------------------------|
5 | | title | Appears before the site name in the document title |
6 | | extra_title | Appears after the site name in the document title |
7 | | description | Sets the value of the description meta tag, if you find that sort of thing useful |
8 | | viewport_meta | Allows overriding the viewport meta tag |
9 | | extra_meta | Allows insertion of additional meta tags as needed |
10 | | css | Wraps links to css files |
11 | | canonical | Overrides the canonical URL (default is current path) |
12 | | feeds | Placeholder for RSS feed insertion |
13 | | extra_head | Catch-all for extra items needed in the document head |
14 | | body_id | Allows setting a body ID |
15 | | body_class | Allows setting body classes |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io
2 |
3 | ### OSX ###
4 | .DS_Store
5 | .AppleDouble
6 | .LSOverride
7 |
8 | # Icon must end with two \r
9 | Icon
10 |
11 |
12 | # Thumbnails
13 | ._*
14 |
15 | # Files that might appear on external disk
16 | .Spotlight-V100
17 | .Trashes
18 |
19 | # Directories potentially created on remote AFP share
20 | .AppleDB
21 | .AppleDesktop
22 | Network Trash Folder
23 | Temporary Items
24 | .apdisk
25 |
26 |
27 | ### Python ###
28 | # Byte-compiled / optimized / DLL files
29 | __pycache__/
30 | *.py[cod]
31 |
32 | # C extensions
33 | *.so
34 |
35 | # Distribution / packaging
36 | .Python
37 | env/
38 | build/
39 | develop-eggs/
40 | dist/
41 | downloads/
42 | eggs/
43 | lib/
44 | lib64/
45 | parts/
46 | sdist/
47 | var/
48 | *.egg-info/
49 | .installed.cfg
50 | *.egg
51 |
52 | # PyInstaller
53 | # Usually these files are written by a python script from a template
54 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
55 | *.manifest
56 | *.spec
57 |
58 | # Installer logs
59 | pip-log.txt
60 | pip-delete-this-directory.txt
61 |
62 | # Unit test / coverage reports
63 | htmlcov/
64 | .tox/
65 | .coverage
66 | .cache
67 | nosetests.xml
68 | coverage.xml
69 |
70 | # Translations
71 | *.mo
72 | *.pot
73 |
74 | # Sphinx documentation
75 | docs/_build/
76 |
77 | # PyBuilder
78 | target/
79 |
80 |
81 | ### Django ###
82 | *.log
83 | *.pot
84 | *.pyc
85 | __pycache__/
86 | local_settings.py
87 |
88 | .env
89 | db.sqlite3
90 |
--------------------------------------------------------------------------------
/docs/dependencies.md:
--------------------------------------------------------------------------------
1 | # Dependencies included with the template
2 | In the interest of full transparency, this is the full list of dependencies from the Pipfile
3 | and why we include them:
4 |
5 |
6 | | Name | Usage |
7 | |-----------------|-------------------------------------------------------|
8 | | Django | Well... yeah, of course |
9 | | cfenv | Simplifies interactions with Cloud.gov env variables |
10 | | cg-django-uaa | Cloud.gov UAA authentication |
11 | | dj-database-url | Easier and more secure DB handling |
12 | | docutils | To enable Django built-in documentation |
13 | | psycopg2-binary | For Postgresql |
14 | | typogrify | Nice typographical utilitiies |
15 | | whitenoise | Simple static file serving |
16 |
17 |
18 | ## In Dev environments
19 | The follow are also added in development environments
20 |
21 | | Name | Usage |
22 | |----------------------|--------------------------------------------------|
23 | | coverage | For code coverage |
24 | | django-debug-toolbar | General development debugging |
25 | | factory-boy | Testing |
26 | | flake8 | Linting |
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 18F Django project template
2 |
3 | Django project templates are a [little-known option](https://docs.djangoproject.com/en/2.0/ref/django-admin/#cmdoption-startproject-template) that allows you to create new Django projects based on your own preferences and boilerplate. This means you can pre-install apps, templates and dependencies and have them already in place, predictably and consistently, when you start a new project.
4 |
5 | This (currently WIP) project template is designed to make starting a new USWDS-based Django project as simple as possible, while including additional government-related boilerplate such as federated analytics tags.
6 |
7 | ## Starting a new project
8 |
9 | 1. Be sure pipenv is installed: `pip install pipenv`
10 | 2. Create or navigate to the directory you're building your Django project in. Typically, this should be your Git repository.
11 | 3. Grab the pipfile from this repo: `wget https://raw.githubusercontent.com/18F/18f-django-project-template/master/Pipfile`
12 | 4. Then use it to install Django and other dependencies you'll want for your project: `pipenv install`
13 | 4. Activate the pipenv shell (virtualenv) with `pipenv shell`
14 | 5. Now you'll start your project pulling from this repo for the template: `django-admin.py startproject --template=https://github.com/18F/18f-django-project-template/archive/master.zip project_name`. Be sure to replace `project_name` with whatever your project should be named.
15 | 6. Move into the `project_name` directory you just created.
16 | 7. Create a `.env` for your environment variables. The `example.env` included here is one good example.
17 | 8. Run `python manage.py runserver` and go take a look at your new project.
18 |
19 |
20 | ## What's next
21 | In development, the project template will create a simple sqlite db. When you prep your staging and prod environments, you'll want to be sure postgresql is ready for you.
22 |
23 | Remember to go into your admin and set the site variables, so you don't show up as `example.com`
24 |
25 | By default your AGENCY setting (used for analytics) is set to the project name. Since that's probably not what you want, you'll want to change that.
26 |
27 |
28 | ## What's included with the project template
29 |
30 | - Django 2.0+ support
31 | - `base.html` based on HTML5 Boilerplate, optimized for USWDS, accessibility and best practices.
32 | - Uses [Pipenv](https://github.com/kennethreitz/pipenv) - the officially recommended Python packaging tool from Python.org.
33 | - Get value insight and debug information while on Development with [django-debug-toolbar](https://django-debug-toolbar.readthedocs.org).
34 | - HTTPS and other security related settings.
35 | - Some additional context processors.
36 | - Cloud.gov integration, including Cloud UAA and database readiness.
37 |
38 |
39 | ## Additional docs
40 | Additional documentation is available in the `/docs` folder.
41 |
42 | ## Upcoming additions
43 | - USWDS integration (NPM? Submodule? Preferences?)
44 | - Improved Cloud.gov readiness
45 |
46 |
47 | ## Deployment
48 |
49 | (Coming) It is possible to deploy to Cloud.gov or to your own server.
50 |
51 | ## License
52 |
53 | TBD
54 |
55 | # Notes
56 | - [Checklist of Requirements for Federal Websites and Digital Services](https://digital.gov/resources/checklist-of-requirements-for-federal-digital-services/)
57 | -
58 |
--------------------------------------------------------------------------------
/project_name/settings/base.py:
--------------------------------------------------------------------------------
1 | """
2 | Base Django settings for {{ project_name }}. These settings are common across all environments
3 |
4 | For more information on this file, see
5 | https://docs.djangoproject.com/en/{{ docs_version }}/topics/settings/
6 |
7 | For the full list of settings and their values, see
8 | https://docs.djangoproject.com/en/{{ docs_version }}/ref/settings/
9 | """
10 |
11 | import os
12 | import dj_database_url
13 |
14 | from django.utils.crypto import get_random_string
15 |
16 | from .env import env
17 |
18 | DEBUG = False
19 |
20 | ALLOWED_HOSTS = ['*']
21 | PROJECT_NAME = '{{ project_name }}'
22 |
23 | # Resolve the paths to the project for convenience
24 | # The project itself (the one with settings in it)
25 | BASE_DIR = os.path.dirname(os.path.dirname(__file__))
26 | # The project wrapper, including apps and the project
27 | PARENT_DIR = os.path.abspath(os.path.join(BASE_DIR, os.path.pardir))
28 | # And the root (usually the container)
29 | ROOT_DIR = os.path.abspath(os.path.join(PARENT_DIR, os.path.pardir))
30 |
31 | # You'll want to set this to your Agency name
32 | AGENCY = PROJECT_NAME
33 |
34 | DATABASES = {}
35 |
36 | INSTALLED_APPS = [
37 | 'django.contrib.admin',
38 | 'django.contrib.auth',
39 | 'django.contrib.contenttypes',
40 | 'django.contrib.sessions',
41 | 'django.contrib.messages',
42 | 'django.contrib.staticfiles',
43 | ]
44 |
45 | MIDDLEWARE = [
46 | 'django.middleware.security.SecurityMiddleware',
47 | 'django.contrib.sessions.middleware.SessionMiddleware',
48 | 'django.middleware.common.CommonMiddleware',
49 | 'django.middleware.csrf.CsrfViewMiddleware',
50 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
51 | 'uaa_client.middleware.UaaRefreshMiddleware',
52 | 'django.contrib.messages.middleware.MessageMiddleware',
53 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
54 | ]
55 |
56 | ROOT_URLCONF = '{{ project_name }}.urls'
57 |
58 | SECRET_KEY = env.get_credential('DJANGO_SECRET_KEY', get_random_string(50))
59 |
60 | TEMPLATES = [
61 | {
62 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
63 | 'DIRS': [
64 | BASE_DIR + '/templates',
65 | ],
66 | 'APP_DIRS': True,
67 | 'OPTIONS': {
68 | 'context_processors': [
69 | 'django.template.context_processors.debug',
70 | 'django.template.context_processors.request',
71 | 'django.contrib.auth.context_processors.auth',
72 | 'django.template.context_processors.i18n',
73 | 'django.contrib.messages.context_processors.messages',
74 | '{{ project_name }}.context_processors.site_processor'
75 | ],
76 | },
77 | },
78 | ]
79 |
80 | WSGI_APPLICATION = '{{ project_name }}.wsgi.application'
81 |
82 | LOGIN_URL = '/login/'
83 | LOGIN_REDIRECT_URL = '/'
84 |
85 | # https://docs.djangoproject.com/en/{{ docs_version }}/topics/i18n/
86 | LANGUAGE_CODE = 'en-us'
87 | TIME_ZONE = 'US/Eastern'
88 | USE_I18N = True
89 | USE_L10N = True
90 | USE_TZ = True
91 |
92 | # Static files (CSS, JavaScript, Images)
93 | # https://docs.djangoproject.com/en/{{ docs_version }}/howto/static-files/
94 | # Here we're trying to find both files in the project /static/ directory
95 | # and any USWDS files that were installed.
96 | STATICFILES_DIRS = [
97 | os.path.join(BASE_DIR, "static/"),
98 | os.path.join(ROOT_DIR, "node_modules/uswds/dist/"),
99 | ]
100 | STATIC_ROOT = os.path.join(PARENT_DIR, 'collected_static/')
101 | STATIC_URL = '/static/'
102 |
103 | # CF-Django-UAA config (cg-django-uaa.readthedocs.io)
104 | UAA_APPROVED_DOMAINS = {}
105 | UAA_CLIENT_ID = env.get_credential('UAA_CLIENT_ID', None)
106 | UAA_CLIENT_SECRET = env.get_credential('UAA_CLIENT_SECRET', None)
107 | UAA_AUTH_URL = 'https://login.fr.cloud.gov/oauth/authorize'
108 | UAA_TOKEN_URL = 'https://uaa.fr.cloud.gov/oauth/token'
109 | UAA_LOGOUT_URL = 'https://login.fr.cloud.gov/logout.do'
110 |
111 | #AUTO_LOGOUT_DELAY_MINUTES = 60
112 |
113 |
--------------------------------------------------------------------------------
/project_name/templates/base.html:
--------------------------------------------------------------------------------
1 | {# keep this on the first line #}
2 | {% load i18n static %}
3 |
4 |
5 |
6 |
7 |
8 |
9 | {% block title %}{% endblock %}
10 | {{ site.name }}
11 | {% block extra_title %}{% endblock %}
12 |
13 |
14 | {% block viewport_meta %}
15 |
16 | {% endblock %}
17 | {% block extra_meta %}{% endblock extra_meta %}
18 |
19 | {# TO-DO: Determine if is desirable #}
20 |
21 | {# TO-DO: set defaults for these #}
22 |
23 |
24 |
25 | {% block css %}
26 |
27 | {% endblock %}
28 |
29 | {% block canonical %}
30 |
31 | {% endblock %}
32 |
33 |
34 | {% block feeds %}{% endblock %}
35 | {% block extra_head %}{% endblock %}
36 |
37 | {# hat tip to USWDS... #}
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | Skip to main content
49 |
50 |
51 |
52 |
62 |
63 |
64 |
65 |
66 |
67 | The .gov means it’s official.
68 |
69 | Federal government websites often end in .gov or .mil. Before sharing sensitive information, make sure you’re on a federal government site.
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | The site is secure.
78 |
79 | The https:// ensures that you are connecting to the official website and that any information you provide is encrypted and transmitted securely.
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | {% block banner %}
88 |
107 | {% endblock banner %}
108 | {% block usa_overlay %}
{% endblock %}
109 |
110 | {% block messages %}
111 | {% if messages %}
112 |
113 | {% for message in messages %}
114 |
115 | {{ message }}
116 |
117 | {% endfor %}
118 |
119 | {% endif %}
120 | {% endblock %}
121 |
122 | {% block section_nav %}{% endblock %}
123 |
124 |
125 | {% block hero %}{% endblock %}
126 | {% block content %}{% endblock %}
127 |
128 |
129 |
{% block complementary %}{% endblock %}
130 |
131 | {% block content_bottom %}{% endblock %}
132 |
133 |
134 |
143 |
144 |
145 | {% block init_js %}{% endblock %}{# useful for vars and other initializations #}
146 |
147 | {% block site_js %}
148 | {% endblock %}
149 |
150 | {% block app_js %}{% endblock %}
151 |
152 | {% block extrascript %}{% endblock %}
153 |
154 | {# asynchronous analytics #}
155 |
157 |
158 |
--------------------------------------------------------------------------------