├── project_name ├── __init__.py-tpl ├── users │ ├── __init__.py-tpl │ ├── migrations │ │ ├── __init__.py-tpl │ │ └── 0001_initial.py-tpl │ ├── models.py-tpl │ └── admin.py-tpl ├── urls.py-tpl ├── wsgi.py-tpl ├── asgi.py-tpl └── settings.py-tpl ├── Procfile ├── example.env ├── Pipfile ├── manage.py-tpl ├── .gitignore ├── README.md └── Pipfile.lock /project_name/__init__.py-tpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /project_name/users/__init__.py-tpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /project_name/users/migrations/__init__.py-tpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn -b "0.0.0.0:$PORT" -w 3 {{ project_name }}.wsgi 2 | -------------------------------------------------------------------------------- /example.env: -------------------------------------------------------------------------------- 1 | ENVIRONMENT='DEVELOPMENT' 2 | DJANGO_SECRET_KEY='{{ secret_key }}' 3 | DJANGO_DEBUG='yes' 4 | DJANGO_TEMPLATE_DEBUG='yes' 5 | -------------------------------------------------------------------------------- /project_name/users/models.py-tpl: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import AbstractUser 2 | 3 | 4 | class User(AbstractUser): 5 | pass 6 | -------------------------------------------------------------------------------- /project_name/users/admin.py-tpl: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.contrib.auth.admin import UserAdmin 3 | from .models import User 4 | 5 | admin.site.register(User, UserAdmin) 6 | -------------------------------------------------------------------------------- /project_name/urls.py-tpl: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.contrib import admin 3 | from django.urls import include, path 4 | 5 | urlpatterns = [ 6 | path('admin/', admin.site.urls), 7 | ] 8 | 9 | if settings.DEBUG: 10 | import debug_toolbar 11 | urlpatterns = [ 12 | path('__debug__/', include(debug_toolbar.urls)), 13 | ] + urlpatterns 14 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.python.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [dev-packages] 7 | Werkzeug = "==1.0.1" 8 | 9 | [packages] 10 | dj-database-url = "==0.5.0" 11 | django-configurations = "==2.2" 12 | django-debug-toolbar = "==3.2.1" 13 | django-extensions = "==3.1.3" 14 | Django = "==3.2.*" 15 | psycopg2-binary = "==2.8.6" 16 | gunicorn = "==20.1.0" 17 | whitenoise = "==5.2.0" 18 | -------------------------------------------------------------------------------- /project_name/wsgi.py-tpl: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for {{ project_name }} 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/{{ docs_version }}/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | configuration = os.getenv('ENVIRONMENT', 'development').title() 13 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{{ project_name }}.settings') 14 | os.environ.setdefault('DJANGO_CONFIGURATION', configuration) 15 | 16 | from configurations.wsgi import get_wsgi_application 17 | 18 | application = get_wsgi_application() 19 | -------------------------------------------------------------------------------- /project_name/asgi.py-tpl: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for {{ project_name }} project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/{{ docs_version }}/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | 13 | configuration = os.getenv('ENVIRONMENT', 'development').title() 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{{ project_name }}.settings') 15 | os.environ.setdefault('DJANGO_CONFIGURATION', configuration) 16 | 17 | from configurations import importer # noqa 18 | importer.install() 19 | 20 | from django.core.asgi import get_asgi_application # noqa 21 | application = get_asgi_application() 22 | -------------------------------------------------------------------------------- /manage.py-tpl: -------------------------------------------------------------------------------- 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 | """Run administrative tasks.""" 9 | configuration = os.getenv('ENVIRONMENT', 'development').title() 10 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{{ project_name }}.settings') 11 | os.environ.setdefault('DJANGO_CONFIGURATION', configuration) 12 | 13 | try: 14 | from configurations.management import execute_from_command_line 15 | except ImportError as exc: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) from exc 21 | execute_from_command_line(sys.argv) 22 | 23 | 24 | if __name__ == '__main__': 25 | main() 26 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /project_name/users/migrations/0001_initial.py-tpl: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.2 on 2017-06-20 13:29 3 | from __future__ import unicode_literals 4 | 5 | import django.contrib.auth.models 6 | import django.contrib.auth.validators 7 | from django.db import migrations, models 8 | import django.utils.timezone 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | initial = True 14 | 15 | dependencies = [ 16 | ('auth', '0008_alter_user_username_max_length'), 17 | ] 18 | 19 | operations = [ 20 | migrations.CreateModel( 21 | name='User', 22 | fields=[ 23 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 24 | ('password', models.CharField(max_length=128, verbose_name='password')), 25 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 26 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), 27 | ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), 28 | ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')), 29 | ('last_name', models.CharField(blank=True, max_length=30, verbose_name='last name')), 30 | ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), 31 | ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), 32 | ('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')), 33 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), 34 | ('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')), 35 | ('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')), 36 | ], 37 | options={ 38 | 'verbose_name_plural': 'users', 39 | 'verbose_name': 'user', 40 | 'abstract': False, 41 | }, 42 | managers=[ 43 | ('objects', django.contrib.auth.models.UserManager()), 44 | ], 45 | ), 46 | ] 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Django 3.0+ project template 2 | 3 | This is a simple Django 3.0+ project template with my preferred setup. Most Django project templates make way too many assumptions or are just way too complicated. I try to make the least amount of assumptions possible while still trying provide a useful setup. Most of my projects are deployed to Heroku, so this is optimized for that but is not necessary. 4 | 5 | ## Features 6 | 7 | - Django 3.0+ 8 | - Uses [Pipenv](https://github.com/kennethreitz/pipenv) - the officially recommended Python packaging tool from Python.org. 9 | - Development, Staging and Production settings with [django-configurations](https://django-configurations.readthedocs.org). 10 | - Get value insight and debug information while on Development with [django-debug-toolbar](https://django-debug-toolbar.readthedocs.org). 11 | - Collection of custom extensions with [django-extensions](http://django-extensions.readthedocs.org). 12 | - HTTPS and other security related settings on Staging and Production. 13 | - Procfile for running gunicorn with New Relic's Python agent. 14 | - PostgreSQL database support with psycopg2. 15 | 16 | ## How to install 17 | 18 | ```bash 19 | $ django-admin.py startproject \ 20 | --template=https://github.com/jpadilla/django-project-template/archive/master.zip \ 21 | --name=Procfile \ 22 | --extension=py,md,env \ 23 | project_name 24 | $ mv example.env .env 25 | $ pipenv install --dev 26 | ``` 27 | 28 | ## Environment variables 29 | 30 | These are common between environments. The `ENVIRONMENT` variable loads the correct settings, possible values are: `DEVELOPMENT`, `STAGING`, `PRODUCTION`. 31 | 32 | ``` 33 | ENVIRONMENT='DEVELOPMENT' 34 | DJANGO_SECRET_KEY='dont-tell-eve' 35 | DJANGO_DEBUG='yes' 36 | ``` 37 | 38 | These settings(and their default values) are only used on staging and production environments. 39 | 40 | ``` 41 | DJANGO_SESSION_COOKIE_SECURE='yes' 42 | DJANGO_SECURE_BROWSER_XSS_FILTER='yes' 43 | DJANGO_SECURE_CONTENT_TYPE_NOSNIFF='yes' 44 | DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS='yes' 45 | DJANGO_SECURE_HSTS_SECONDS=31536000 46 | DJANGO_SECURE_REDIRECT_EXEMPT='' 47 | DJANGO_SECURE_SSL_HOST='' 48 | DJANGO_SECURE_SSL_REDIRECT='yes' 49 | DJANGO_SECURE_PROXY_SSL_HEADER='HTTP_X_FORWARDED_PROTO,https' 50 | ``` 51 | 52 | ## Deployment 53 | 54 | It is possible to deploy to Heroku or to your own server. 55 | 56 | ### Heroku 57 | 58 | ```bash 59 | $ heroku create 60 | $ heroku addons:add heroku-postgresql:hobby-dev 61 | $ heroku pg:promote DATABASE_URL 62 | $ heroku config:set ENVIRONMENT=PRODUCTION 63 | $ heroku config:set DJANGO_SECRET_KEY=`./manage.py generate_secret_key` 64 | ``` 65 | 66 | ## License 67 | 68 | The MIT License (MIT) 69 | 70 | Copyright (c) 2012-2017 José Padilla 71 | 72 | Permission is hereby granted, free of charge, to any person obtaining a copy of 73 | this software and associated documentation files (the "Software"), to deal in 74 | the Software without restriction, including without limitation the rights to 75 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 76 | of the Software, and to permit persons to whom the Software is furnished to do 77 | so, subject to the following conditions: 78 | 79 | The above copyright notice and this permission notice shall be included in all 80 | copies or substantial portions of the Software. 81 | 82 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 83 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 84 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 85 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 86 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 87 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 88 | SOFTWARE. 89 | -------------------------------------------------------------------------------- /project_name/settings.py-tpl: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for {{ project_name }} project. 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 | import os 11 | from pathlib import Path 12 | 13 | from configurations import Configuration, values 14 | 15 | 16 | class Common(Configuration): 17 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 18 | BASE_DIR = Path(__file__).resolve().parent.parent 19 | 20 | # SECURITY WARNING: keep the secret key used in production secret! 21 | SECRET_KEY = values.SecretValue() 22 | 23 | # SECURITY WARNING: don't run with debug turned on in production! 24 | DEBUG = values.BooleanValue(False) 25 | 26 | ALLOWED_HOSTS = values.ListValue([]) 27 | 28 | # Application definition 29 | INSTALLED_APPS = [ 30 | 'django.contrib.admin', 31 | 'django.contrib.auth', 32 | 'django.contrib.contenttypes', 33 | 'django.contrib.sessions', 34 | 'django.contrib.messages', 35 | 'whitenoise.runserver_nostatic', 36 | 'django.contrib.staticfiles', 37 | 38 | 'django_extensions', 39 | 'debug_toolbar', 40 | 41 | '{{ project_name }}.users', 42 | ] 43 | 44 | MIDDLEWARE = [ 45 | 'django.middleware.security.SecurityMiddleware', 46 | 'whitenoise.middleware.WhiteNoiseMiddleware', 47 | 'django.contrib.sessions.middleware.SessionMiddleware', 48 | 'django.middleware.common.CommonMiddleware', 49 | 'django.middleware.csrf.CsrfViewMiddleware', 50 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 51 | 'django.contrib.messages.middleware.MessageMiddleware', 52 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 53 | ] 54 | 55 | ROOT_URLCONF = '{{ project_name }}.urls' 56 | 57 | TEMPLATES = [ 58 | { 59 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 60 | 'DIRS': [], 61 | 'APP_DIRS': True, 62 | 'OPTIONS': { 63 | 'context_processors': [ 64 | 'django.template.context_processors.debug', 65 | 'django.template.context_processors.request', 66 | 'django.contrib.auth.context_processors.auth', 67 | 'django.contrib.messages.context_processors.messages', 68 | ], 69 | }, 70 | }, 71 | ] 72 | 73 | WSGI_APPLICATION = '{{ project_name }}.wsgi.application' 74 | 75 | # Database 76 | # https://docs.djangoproject.com/en/{{ docs_version }}/ref/settings/#databases 77 | DATABASES = values.DatabaseURLValue( 78 | 'sqlite:///{}'.format(os.path.join(BASE_DIR, 'db.sqlite3')) 79 | ) 80 | 81 | # Password validation 82 | # https://docs.djangoproject.com/en/{{ docs_version }}/ref/settings/#auth-password-validators 83 | AUTH_PASSWORD_VALIDATORS = [ 84 | { 85 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 86 | }, 87 | { 88 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 89 | }, 90 | { 91 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 92 | }, 93 | { 94 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 95 | }, 96 | ] 97 | 98 | # Internationalization 99 | # https://docs.djangoproject.com/en/{{ docs_version }}/topics/i18n/ 100 | LANGUAGE_CODE = 'en-us' 101 | 102 | TIME_ZONE = 'UTC' 103 | 104 | USE_I18N = True 105 | 106 | USE_L10N = True 107 | 108 | USE_TZ = True 109 | 110 | # Static files (CSS, JavaScript, Images) 111 | # https://docs.djangoproject.com/en/{{ docs_version }}/howto/static-files/ 112 | STATIC_URL = '/static/' 113 | STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') 114 | STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' 115 | 116 | # Default primary key field type 117 | # https://docs.djangoproject.com/en/{{ docs_version }}/ref/settings/#default-auto-field 118 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 119 | 120 | AUTH_USER_MODEL = 'users.User' 121 | 122 | 123 | class Development(Common): 124 | """ 125 | The in-development settings and the default configuration. 126 | """ 127 | DEBUG = True 128 | 129 | ALLOWED_HOSTS = [] 130 | 131 | INTERNAL_IPS = [ 132 | '127.0.0.1' 133 | ] 134 | 135 | MIDDLEWARE = Common.MIDDLEWARE + [ 136 | 'debug_toolbar.middleware.DebugToolbarMiddleware' 137 | ] 138 | 139 | 140 | class Staging(Common): 141 | """ 142 | The in-staging settings. 143 | """ 144 | # Security 145 | SESSION_COOKIE_SECURE = values.BooleanValue(True) 146 | SECURE_BROWSER_XSS_FILTER = values.BooleanValue(True) 147 | SECURE_CONTENT_TYPE_NOSNIFF = values.BooleanValue(True) 148 | SECURE_HSTS_INCLUDE_SUBDOMAINS = values.BooleanValue(True) 149 | SECURE_HSTS_SECONDS = values.IntegerValue(31536000) 150 | SECURE_REDIRECT_EXEMPT = values.ListValue([]) 151 | SECURE_SSL_HOST = values.Value(None) 152 | SECURE_SSL_REDIRECT = values.BooleanValue(True) 153 | SECURE_PROXY_SSL_HEADER = values.TupleValue( 154 | ('HTTP_X_FORWARDED_PROTO', 'https') 155 | ) 156 | 157 | 158 | class Production(Staging): 159 | """ 160 | The in-production settings. 161 | """ 162 | pass 163 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "8322e4ccc0edaaeed452f8dff3900a01658d4a2094086ec05c5e681f950f99ae" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": {}, 8 | "sources": [ 9 | { 10 | "name": "pypi", 11 | "url": "https://pypi.python.org/simple", 12 | "verify_ssl": true 13 | } 14 | ] 15 | }, 16 | "default": { 17 | "asgiref": { 18 | "hashes": [ 19 | "sha256:92906c611ce6c967347bbfea733f13d6313901d54dcca88195eaeb52b2a8e8ee", 20 | "sha256:d1216dfbdfb63826470995d31caed36225dcaf34f182e0fa257a4dd9e86f1b78" 21 | ], 22 | "version": "==3.3.4" 23 | }, 24 | "dj-database-url": { 25 | "hashes": [ 26 | "sha256:4aeaeb1f573c74835b0686a2b46b85990571159ffc21aa57ecd4d1e1cb334163", 27 | "sha256:851785365761ebe4994a921b433062309eb882fedd318e1b0fcecc607ed02da9" 28 | ], 29 | "index": "pypi", 30 | "version": "==0.5.0" 31 | }, 32 | "django": { 33 | "hashes": [ 34 | "sha256:0a1d195ad65c52bf275b8277b3d49680bd1137a5f55039a806f25f6b9752ce3d", 35 | "sha256:18dd3145ddbd04bf189ff79b9954d08fda5171ea7b57bf705789fea766a07d50" 36 | ], 37 | "index": "pypi", 38 | "version": "==3.2.2" 39 | }, 40 | "django-configurations": { 41 | "hashes": [ 42 | "sha256:0b93cb7042739e2d69cfc6fb81676bbd3cbb63ea19b02e8b681f4cad3a8aaf1b", 43 | "sha256:9e3bcea1355ac50a4c9f854f751d214cb17e5f8adf18405a4488d0a1e8945915" 44 | ], 45 | "index": "pypi", 46 | "version": "==2.2" 47 | }, 48 | "django-debug-toolbar": { 49 | "hashes": [ 50 | "sha256:a5ff2a54f24bf88286f9872836081078f4baa843dc3735ee88524e89f8821e33", 51 | "sha256:e759e63e3fe2d3110e0e519639c166816368701eab4a47fed75d7de7018467b9" 52 | ], 53 | "index": "pypi", 54 | "version": "==3.2.1" 55 | }, 56 | "django-extensions": { 57 | "hashes": [ 58 | "sha256:50de8977794a66a91575dd40f87d5053608f679561731845edbd325ceeb387e3", 59 | "sha256:5f0fea7bf131ca303090352577a9e7f8bfbf5489bd9d9c8aea9401db28db34a0" 60 | ], 61 | "index": "pypi", 62 | "version": "==3.1.3" 63 | }, 64 | "gunicorn": { 65 | "hashes": [ 66 | "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e", 67 | "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8" 68 | ], 69 | "index": "pypi", 70 | "version": "==20.1.0" 71 | }, 72 | "psycopg2-binary": { 73 | "hashes": [ 74 | "sha256:0deac2af1a587ae12836aa07970f5cb91964f05a7c6cdb69d8425ff4c15d4e2c", 75 | "sha256:0e4dc3d5996760104746e6cfcdb519d9d2cd27c738296525d5867ea695774e67", 76 | "sha256:11b9c0ebce097180129e422379b824ae21c8f2a6596b159c7659e2e5a00e1aa0", 77 | "sha256:15978a1fbd225583dd8cdaf37e67ccc278b5abecb4caf6b2d6b8e2b948e953f6", 78 | "sha256:1fabed9ea2acc4efe4671b92c669a213db744d2af8a9fc5d69a8e9bc14b7a9db", 79 | "sha256:2dac98e85565d5688e8ab7bdea5446674a83a3945a8f416ad0110018d1501b94", 80 | "sha256:42ec1035841b389e8cc3692277a0bd81cdfe0b65d575a2c8862cec7a80e62e52", 81 | "sha256:6422f2ff0919fd720195f64ffd8f924c1395d30f9a495f31e2392c2efafb5056", 82 | "sha256:6a32f3a4cb2f6e1a0b15215f448e8ce2da192fd4ff35084d80d5e39da683e79b", 83 | "sha256:7312e931b90fe14f925729cde58022f5d034241918a5c4f9797cac62f6b3a9dd", 84 | "sha256:7d92a09b788cbb1aec325af5fcba9fed7203897bbd9269d5691bb1e3bce29550", 85 | "sha256:833709a5c66ca52f1d21d41865a637223b368c0ee76ea54ca5bad6f2526c7679", 86 | "sha256:89705f45ce07b2dfa806ee84439ec67c5d9a0ef20154e0e475e2b2ed392a5b83", 87 | "sha256:8cd0fb36c7412996859cb4606a35969dd01f4ea34d9812a141cd920c3b18be77", 88 | "sha256:950bc22bb56ee6ff142a2cb9ee980b571dd0912b0334aa3fe0fe3788d860bea2", 89 | "sha256:a0c50db33c32594305b0ef9abc0cb7db13de7621d2cadf8392a1d9b3c437ef77", 90 | "sha256:a0eb43a07386c3f1f1ebb4dc7aafb13f67188eab896e7397aa1ee95a9c884eb2", 91 | "sha256:aaa4213c862f0ef00022751161df35804127b78adf4a2755b9f991a507e425fd", 92 | "sha256:ac0c682111fbf404525dfc0f18a8b5f11be52657d4f96e9fcb75daf4f3984859", 93 | "sha256:ad20d2eb875aaa1ea6d0f2916949f5c08a19c74d05b16ce6ebf6d24f2c9f75d1", 94 | "sha256:b4afc542c0ac0db720cf516dd20c0846f71c248d2b3d21013aa0d4ef9c71ca25", 95 | "sha256:b8a3715b3c4e604bcc94c90a825cd7f5635417453b253499664f784fc4da0152", 96 | "sha256:ba28584e6bca48c59eecbf7efb1576ca214b47f05194646b081717fa628dfddf", 97 | "sha256:ba381aec3a5dc29634f20692349d73f2d21f17653bda1decf0b52b11d694541f", 98 | "sha256:bd1be66dde2b82f80afb9459fc618216753f67109b859a361cf7def5c7968729", 99 | "sha256:c2507d796fca339c8fb03216364cca68d87e037c1f774977c8fc377627d01c71", 100 | "sha256:cec7e622ebc545dbb4564e483dd20e4e404da17ae07e06f3e780b2dacd5cee66", 101 | "sha256:d14b140a4439d816e3b1229a4a525df917d6ea22a0771a2a78332273fd9528a4", 102 | "sha256:d1b4ab59e02d9008efe10ceabd0b31e79519da6fb67f7d8e8977118832d0f449", 103 | "sha256:d5227b229005a696cc67676e24c214740efd90b148de5733419ac9aaba3773da", 104 | "sha256:e1f57aa70d3f7cc6947fd88636a481638263ba04a742b4a37dd25c373e41491a", 105 | "sha256:e74a55f6bad0e7d3968399deb50f61f4db1926acf4a6d83beaaa7df986f48b1c", 106 | "sha256:e82aba2188b9ba309fd8e271702bd0d0fc9148ae3150532bbb474f4590039ffb", 107 | "sha256:ee69dad2c7155756ad114c02db06002f4cded41132cc51378e57aad79cc8e4f4", 108 | "sha256:f5ab93a2cb2d8338b1674be43b442a7f544a0971da062a5da774ed40587f18f5" 109 | ], 110 | "index": "pypi", 111 | "version": "==2.8.6" 112 | }, 113 | "pytz": { 114 | "hashes": [ 115 | "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da", 116 | "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798" 117 | ], 118 | "version": "==2021.1" 119 | }, 120 | "six": { 121 | "hashes": [ 122 | "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", 123 | "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" 124 | ], 125 | "version": "==1.16.0" 126 | }, 127 | "sqlparse": { 128 | "hashes": [ 129 | "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0", 130 | "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8" 131 | ], 132 | "version": "==0.4.1" 133 | }, 134 | "whitenoise": { 135 | "hashes": [ 136 | "sha256:05ce0be39ad85740a78750c86a93485c40f08ad8c62a6006de0233765996e5c7", 137 | "sha256:05d00198c777028d72d8b0bbd234db605ef6d60e9410125124002518a48e515d" 138 | ], 139 | "index": "pypi", 140 | "version": "==5.2.0" 141 | } 142 | }, 143 | "develop": { 144 | "werkzeug": { 145 | "hashes": [ 146 | "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43", 147 | "sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c" 148 | ], 149 | "index": "pypi", 150 | "version": "==1.0.1" 151 | } 152 | } 153 | } 154 | --------------------------------------------------------------------------------