├── 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 |
53 |
54 | U.S. flag 55 |

An official website of the United States government

56 | 60 |
61 |
62 |
63 |
64 | Dot gov 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 | Https 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 | 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 | --------------------------------------------------------------------------------