├── hooks
└── post_gen_project.py
├── {{cookiecutter.repo_name}}
├── .nvmrc
├── runtime.txt
├── {{cookiecutter.package_name}}
│ ├── __init__.py
│ ├── users
│ │ ├── __init__.py
│ │ ├── migrations
│ │ │ ├── __init__.py
│ │ │ ├── 0002_auto_20190123_1458.py
│ │ │ └── 0001_initial.py
│ │ ├── tests.py
│ │ ├── views.py
│ │ ├── apps.py
│ │ ├── models.py
│ │ └── admin.py
│ ├── static_src
│ │ ├── sass
│ │ │ ├── settings.scss
│ │ │ └── app.scss
│ │ └── js
│ │ │ └── app.js
│ ├── static
│ │ └── README.md
│ ├── views.py
│ ├── apps.py
│ ├── templates
│ │ ├── README.md
│ │ ├── home.html
│ │ ├── 404.html
│ │ └── base.html
│ ├── wsgi.py
│ ├── storage.py
│ ├── urls.py
│ └── settings.py
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── favicon-152.png
│ └── humans.txt
├── .babelrc
├── Procfile
├── mo.yml
├── bin
│ └── post_compile
├── Procfile.dev
├── env.example
├── manage.py
├── .editorconfig
├── .gitignore
├── tox.ini
├── app.json
├── gulp
│ ├── utils.js
│ ├── build.js
│ └── production.js
├── Pipfile
├── banner.txt
├── webpack.config.js
├── LICENSE
├── gulpfile.babel.js
├── package.json
├── README.md
└── package-lock.json
├── circle.yml
├── .gitignore
├── tox.ini
├── Pipfile
├── cookiecutter.json
├── tests
└── test_mo_project.py
├── LICENSE
└── README.md
/hooks/post_gen_project.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/.nvmrc:
--------------------------------------------------------------------------------
1 | 8.11
2 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/runtime.txt:
--------------------------------------------------------------------------------
1 | python-3.7.0
2 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/users/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env"]
3 | }
4 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/users/migrations/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/static_src/sass/settings.scss:
--------------------------------------------------------------------------------
1 | $color: #000000;
2 |
--------------------------------------------------------------------------------
/circle.yml:
--------------------------------------------------------------------------------
1 | dependencies:
2 | override:
3 | - pip install tox tox-pyenv
4 | - pyenv local 2.7.10 3.4.3
5 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/islco/mo-django/HEAD/{{cookiecutter.repo_name}}/public/favicon.ico
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/users/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/users/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 |
3 | # Create your views here.
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | *.DS_Store
3 | *.lock
4 | *.swp
5 | *.out
6 | *.pyc
7 | *.swp
8 | *~
9 | .env
10 | .cache/
11 | .tox/
12 | __pycache__/
13 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/public/favicon-152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/islco/mo-django/HEAD/{{cookiecutter.repo_name}}/public/favicon-152.png
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | skipsdist = true
3 | envlist = py27,py34
4 |
5 | [testenv]
6 | deps =
7 | pytest-cookies
8 | commands = py.test {posargs:tests}
9 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/static_src/sass/app.scss:
--------------------------------------------------------------------------------
1 | @import "settings";
2 |
3 | html {
4 | border: 5px solid $color;
5 | }
6 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/users/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class UsersConfig(AppConfig):
5 | name = 'users'
6 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/Procfile:
--------------------------------------------------------------------------------
1 | web: waitress-serve --port=$PORT {{ cookiecutter.package_name }}.wsgi:application
2 | rqworker: python manage.py rqworkers high default low
3 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/users/models.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.models import AbstractUser
2 |
3 |
4 | class User(AbstractUser):
5 | pass
6 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/mo.yml:
--------------------------------------------------------------------------------
1 | domain: {{ cookiecutter.domain_name }}
2 | heroku:
3 | staging: {{ cookiecutter.repo_name }}-staging
4 | production: {{ cookiecutter.repo_name }}
5 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/static/README.md:
--------------------------------------------------------------------------------
1 | Do not put anything in this directory. The gulp build process will generate files from static_src and put them here.
2 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/views.py:
--------------------------------------------------------------------------------
1 | from django.views.generic import TemplateView
2 |
3 |
4 | class HomePageView(TemplateView):
5 | template_name = 'home.html'
6 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/bin/post_compile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | indent() {
4 | sed "s/^/ /"
5 | }
6 |
7 | echo " $ python manage.py migrate"
8 | python manage.py migrate 2>&1 | indent
9 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/Procfile.dev:
--------------------------------------------------------------------------------
1 | web: PYTHONUNBUFFERED=True python manage.py runserver 0.0.0.0:$PORT
2 | static: npm run dev
3 | rqworker: PYTHONUNBUFFERED=True python manage.py rqworkers high default low --autoreload
4 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class {{ cookiecutter.package_name | title }}Config(AppConfig):
5 | name = '{{ cookiecutter.package_name }}'
6 |
--------------------------------------------------------------------------------
/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | url = "https://pypi.python.org/simple"
3 | verify_ssl = true
4 |
5 | [packages]
6 | cookiecutter = "<1.6,>=1.5"
7 | pytest = "<3.1,>=3.0"
8 | pytest-cookies = "<0.3,>=0.2"
9 | tox = "<2.7,>=2.6"
10 | tox-pyenv = "<1.1,>=1.0"
11 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/templates/README.md:
--------------------------------------------------------------------------------
1 | Your project templates go here. They should be namespaced by app name:
2 |
3 | ```
4 | templates/
5 | app1/
6 | list.html
7 | app2/
8 | base.html
9 | base.html
10 | ```
11 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/public/humans.txt:
--------------------------------------------------------------------------------
1 | # humanstxt.org/
2 | # The humans responsible & technology colophon
3 |
4 | # TEAM
5 |
6 | ISL -- Digital Creative Agency -- @istrategylabs
7 |
8 | # THANKS
9 |
10 | # TECHNOLOGY COLOPHON
11 |
12 | Python, Django
13 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/templates/home.html:
--------------------------------------------------------------------------------
1 | {% raw %}{% extends 'base.html' %}
2 |
3 | {% block content %}
4 |
5 | Hello, {% endraw %}{{ cookiecutter.project_name }}{% raw %}!
6 |
7 | {% endblock content %}{% endraw %}
8 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/wsgi.py:
--------------------------------------------------------------------------------
1 | import os
2 | os.environ.setdefault(
3 | "DJANGO_SETTINGS_MODULE", "{{ cookiecutter.package_name }}.settings")
4 |
5 | from django.core.wsgi import get_wsgi_application
6 |
7 | application = get_wsgi_application()
8 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/users/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from django.contrib.auth.admin import UserAdmin as UAdmin
3 |
4 | from .models import User
5 |
6 |
7 | @admin.register(User)
8 | class UserAdmin(UAdmin):
9 | pass
10 |
--------------------------------------------------------------------------------
/cookiecutter.json:
--------------------------------------------------------------------------------
1 | {
2 | "project_name": "Project Name",
3 | "repo_name": "{{ cookiecutter.project_name|lower|replace(' ', '-') }}",
4 | "package_name": "{{ cookiecutter.repo_name|lower|replace('-', '') }}",
5 | "author": "ISL",
6 | "description": "",
7 | "domain_name": "example.isl.codes"
8 | }
9 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/static_src/js/app.js:
--------------------------------------------------------------------------------
1 | // example
2 | const dateDisplayEl = document.createElement('div')
3 | dateDisplayEl.style.textAlign = 'center'
4 | dateDisplayEl.innerHTML = new Date()
5 | document.body.appendChild(dateDisplayEl)
6 |
7 | console.log('[app.js] hello from mo-django')
8 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/env.example:
--------------------------------------------------------------------------------
1 | PORT=8000
2 | DEBUG=True
3 |
4 | SECRET_KEY='FORTHELOVEOFGODTHISNOTSECURE'
5 |
6 | ALLOWED_HOSTS=*
7 |
8 | SECURE_SSL_REDIRECT=False
9 |
10 | # To create the database locally: createdb {{ cookiecutter.package_name }}
11 | DATABASE_URL=postgres://localhost/{{ cookiecutter.package_name }}
12 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/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", "{{ cookiecutter.package_name }}.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
11 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | trim_trailing_whitespace = true
8 |
9 | [*.{py,rst,ini,html,css,scss}]
10 | indent_style = space
11 | indent_size = 4
12 |
13 | [*.{js,json,yml}]
14 | indent_style = space
15 | indent_size = 2
16 |
17 | [*.md]
18 | trim_trailing_whitespace = false
19 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | *.DS_Store
3 | *.lock
4 | *.swp
5 | *.out
6 | *.pid
7 | *.pyc
8 | *.sql
9 | *.sqlite
10 | *.db
11 | *.swp
12 | *~
13 | __pycache__/
14 | /tmp/
15 | .bundle/
16 | .sass-cache/
17 | bower_components/
18 | bundle/
19 | dist/
20 | public/media/
21 | public/static/
22 | node_modules/
23 | .env
24 | npm-debug.log
25 | newrelic.ini
26 | .idea
27 | .vscode/
28 |
29 | {{ cookiecutter.package_name }}/static/
30 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/tox.ini:
--------------------------------------------------------------------------------
1 | [flake8]
2 | exclude = migrations,node_modules
3 | max-line-length = 99
4 |
5 | [pep8]
6 | exclude = migrations,node_modules
7 | max-line-length = 99
8 |
9 | [pylama:pep8]
10 | exclude = migrations,node_modules
11 | max_line_length = 99
12 |
13 | [pylama:mccabe]
14 | complexity = 10
15 |
16 | [pycodestyle]
17 | exclude = migrations,node_modules
18 | max-line-length = 99
19 |
20 | [pylama:pycodestyle]
21 | exclude = migrations,node_modules
22 | max_line_length = 99
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/users/migrations/0002_auto_20190123_1458.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.5 on 2019-01-23 14:58
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('users', '0001_initial'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='user',
15 | name='last_name',
16 | field=models.CharField(blank=True, max_length=150, verbose_name='last name'),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "{{ cookiecutter.repo_name }}",
3 | "env":{
4 | "DJANGO_SETTINGS_MODULE":{
5 | "value": "{{ cookiecutter.package_name }}.settings"
6 | },
7 | "SECRET_KEY": {
8 | "generator": "secret"
9 | }
10 | },
11 | "addons":[
12 | "heroku-postgresql:hobby-dev"
13 | ],
14 | "buildpacks": [
15 | {
16 | "url": "heroku/nodejs"
17 | },
18 | {
19 | "url": "https://github.com/istrategylabs/heroku-buildpack-node-cleanup"
20 | },
21 | {
22 | "url": "heroku/python"
23 | }
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/gulp/utils.js:
--------------------------------------------------------------------------------
1 | import gulp from 'gulp'
2 | import gutil from 'gulp-util'
3 | import tinify from 'gulp-tinify'
4 |
5 |
6 | gulp.task('tinify', (done) => {
7 | if (!process.env.TINIFY_API_KEY) {
8 | gutil.log(gutil.colors.red(
9 | 'No TINIFY_API_KEY specified, get one at https://tinypng.com/developers/subscription'))
10 | return done()
11 | }
12 |
13 | return gulp.src('./{{ cookiecutter.package_name }}/static_src/**/*.{jpg,jpeg,png}')
14 | .pipe(tinify(process.env.TINIFY_API_KEY))
15 | .pipe(gulp.dest('./{{ cookiecutter.package_name }}/static_src/'))
16 | })
17 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/storage.py:
--------------------------------------------------------------------------------
1 | from django.contrib.staticfiles import finders
2 | from django.conf import settings
3 |
4 | from whitenoise.storage import CompressedManifestStaticFilesStorage
5 |
6 | class DebugErroringCompressedManifestStaticFilesStorage(CompressedManifestStaticFilesStorage):
7 |
8 | def url(self, name, force=False):
9 | if settings.DEBUG:
10 | if finders.find(name) is None:
11 | raise ValueError("The file '%s' could not be found with %r." % (name, self))
12 | return super(DebugErroringCompressedManifestStaticFilesStorage, self).url(name)
13 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | url = "https://pypi.python.org/simple"
3 | verify_ssl = true
4 |
5 | [packages]
6 | django = "<2.2"
7 | boto = "< 2.50.0,>= 2.49.0"
8 | dj-database-url = "==0.5.0"
9 | django-debug-toolbar = "<1.12,>=1.11"
10 | django-redis = "<4.11.0,>=4.10.0"
11 | django-rq = "<1.4.0,>=1.3.0"
12 | django-rq-wrapper = "<2.2,>=2.1"
13 | django-storages = "<1.8,>=1.7"
14 | psycopg2 = "<2.8,>=2.7"
15 | python-decouple = "==3.0"
16 | pytz = "==2018.7"
17 | raven = "<7.00,>=6.00"
18 | redis = "<3.1,>=3.0"
19 | rq = "<0.14.0,>=0.13.0"
20 | sqlparse = "<0.3,>=0.2"
21 | waitress = "<1.2,>=1.1"
22 | whitenoise = "<4.2,>=4.1"
23 | pillow = "==5.3.0"
24 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/banner.txt:
--------------------------------------------------------------------------------
1 | /*!
2 | _____ _____ _ ______ _ _
3 | |_ _|/ ____| | | ____| (_) (_)
4 | | | | (___ | | | |__ _ __ __ _ _ _ __ ___ ___ _ __ _ _ __ __ _
5 | | | \___ \| | | __| | '_ \ / _` | | '_ \ / _ \/ _ \ '__| | '_ \ / _` |
6 | _| |_ ____) | |____ | |____| | | | (_| | | | | | __/ __/ | | | | | | (_| |
7 | |_____|_____/|______| |______|_| |_|\__, |_|_| |_|\___|\___|_| |_|_| |_|\__, |
8 | __/ | __/ |
9 | |___/ |___/
10 | Made with <3 in DC by https://isl.co
11 | @date
12 | */
13 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const webpack = require('webpack')
3 |
4 | const config = {
5 | devtool: 'source-map',
6 | entry: './{{ cookiecutter.package_name }}/static_src/js/app',
7 | mode: process.env.NODE_ENV || 'development',
8 | output: {
9 | path: path.resolve(__dirname, 'public/static/js'),
10 | filename: 'bundle.js'
11 | },
12 | module: {
13 | rules: [
14 | {
15 | test: /\.js$/,
16 | exclude: [/node_modules/],
17 | loader: 'babel-loader',
18 | options: {
19 | presets: ['@babel/env']
20 | }
21 | }
22 | ]
23 | },
24 | optimization: {
25 | minimize: process.env.NODE_ENV === 'production'
26 | }
27 | }
28 |
29 | module.exports = config
30 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.conf.urls import include, url
3 | from django.urls import path
4 | from django.contrib import admin
5 |
6 | from {{ cookiecutter.package_name }}.views import HomePageView
7 |
8 |
9 | urlpatterns = [
10 | # Examples:
11 | path('', HomePageView.as_view(), name='home'),
12 | # path('blog/', include('blog.urls')),
13 |
14 | path('admin/rq/', include('django_rq.urls')),
15 | path('admin/', admin.site.urls),
16 | ]
17 |
18 | if settings.DEBUG:
19 | import debug_toolbar
20 | from django.conf.urls.static import static
21 |
22 | urlpatterns = [
23 | url(r'^__debug__/', include(debug_toolbar.urls)),
24 | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + \
25 | urlpatterns
26 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/templates/404.html:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Page Not Found · {{ cookiecutter.project_name }}
19 |
24 |
25 |
26 | 404 Page Not Found
27 |
28 |
29 |
--------------------------------------------------------------------------------
/tests/test_mo_project.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from contextlib import contextmanager
3 |
4 |
5 | @pytest.fixture
6 | def context():
7 | return {
8 | 'project_name': 'Mo Django Test',
9 | 'repo_name': 'mo-django-test',
10 | 'package_name': 'mo_django_test',
11 | 'author_name': 'ISL Tester',
12 | 'description': 'A short description of the project is good.',
13 | 'domain_name': 'mo-django-test.isl.co',
14 | }
15 |
16 |
17 | # utility methods
18 |
19 | @contextmanager
20 | def content_of(project, path):
21 | f = project.join(path)
22 | assert f.check(), '{} does not exist'.format(path)
23 | yield f.read()
24 |
25 |
26 | def generate(cookies, context):
27 | result = cookies.bake(extra_context=context)
28 | assert result.exit_code == 0
29 | assert result.exception is None
30 | return result
31 |
32 |
33 | # test methods
34 |
35 | def test_mo_django(cookies, context):
36 | result = generate(cookies, context)
37 | assert result.project.basename == context['repo_name']
38 | assert result.project.isdir()
39 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/gulp/build.js:
--------------------------------------------------------------------------------
1 | import gulp from 'gulp'
2 | import del from 'del'
3 | import sourcemaps from 'gulp-sourcemaps'
4 | import sass from 'gulp-sass'
5 | import webpack from 'webpack'
6 | import webpackStream from 'webpack-stream'
7 |
8 | export const EXTRAS_GLOB = './{{ cookiecutter.package_name }}/static_src/**/*.{txt,json,xml,ico,jpeg,jpg,png,gif,svg,ttf,otf,eot,woff,woff2,mp3,mp4,ogv,ogg,webm}'
9 |
10 | gulp.task('clean', () => del('./{{ cookiecutter.package_name }}/static/'))
11 |
12 | gulp.task('sass', () =>
13 | gulp.src('./{{ cookiecutter.package_name }}/static_src/sass/app.scss')
14 | .pipe(sourcemaps.init())
15 | .pipe(sass())
16 | .pipe(sourcemaps.write())
17 | .pipe(gulp.dest('./{{ cookiecutter.package_name }}/static/css/'))
18 | )
19 |
20 | gulp.task('webpack', () =>
21 | webpackStream(require('../webpack.config.js'), webpack)
22 | .pipe(gulp.dest('./{{ cookiecutter.package_name }}/static/js/'))
23 | )
24 |
25 | gulp.task('extras', () =>
26 | gulp.src(EXTRAS_GLOB)
27 | .pipe(gulp.dest('./{{ cookiecutter.package_name }}/static/')))
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2015-2016 iStrategyLabs, LLC
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.repo_name}}/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) 2016 {{ cookiecutter.author }}
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
20 | OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/gulpfile.babel.js:
--------------------------------------------------------------------------------
1 | import gulp from 'gulp'
2 | import runSequence from 'run-sequence'
3 | import './gulp/build'
4 | import './gulp/production'
5 | import './gulp/utils'
6 | import EXTRAS_GLOB from './gulp/build'
7 |
8 | gulp.task('build', (done) => {
9 | runSequence('clean', ['sass', 'webpack', 'extras'], done)
10 | })
11 |
12 | gulp.task('build:production', (done) => {
13 | runSequence('build', 'rev', 'rev:replace', ['minify:css', 'minify:js'], done)
14 | })
15 |
16 | gulp.task('watch', ['build'], () => {
17 | const browserSync = require('browser-sync').create()
18 | browserSync.init({
19 | ghostMode: false,
20 | proxy: '127.0.0.1:' + (parseInt(process.env.PORT, 10) - 100), // subtract 100 because foreman adds 100 to each success worker in the Procfile
21 | files: './{{ cookiecutter.package_name }}/static/**/*',
22 | open: false,
23 | port: process.env.PORT,
24 | ui: {
25 | port: (parseInt(process.env.PORT, 10) + 1)
26 | }
27 | })
28 |
29 | gulp.watch('./{{ cookiecutter.package_name }}/static_src/js/**/*.js', ['js'])
30 | gulp.watch('./{{ cookiecutter.package_name }}/static_src/sass/**/*.scss', ['sass'])
31 | gulp.watch(EXTRAS_GLOB, ['extras'])
32 | })
33 |
34 | gulp.task('default', ['build'])
35 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/gulp/production.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs'
2 | import gulp from 'gulp'
3 | import rev from 'gulp-rev'
4 | import cleancss from 'gulp-clean-css'
5 | import uglify from 'gulp-uglify'
6 | import revReplace from 'gulp-rev-replace'
7 | import header from 'gulp-header'
8 |
9 | const BANNER = fs.readFileSync('banner.txt', 'utf8').replace('@date', (new Date()))
10 |
11 | const MANIFEST_PATH = './{{ cookiecutter.package_name }}/static/rev-manifest.json'
12 |
13 | gulp.task('rev', () =>
14 | gulp.src(['{{ cookiecutter.package_name }}/static/**/*', '!**/*.txt', '!**/*.ico'])
15 | .pipe(rev())
16 | .pipe(gulp.dest('{{ cookiecutter.package_name }}/static/'))
17 | .pipe(rev.manifest())
18 | .pipe(gulp.dest('{{ cookiecutter.package_name }}/static/')))
19 |
20 | gulp.task('rev:replace', () =>
21 | gulp.src([
22 | './{{ cookiecutter.package_name }}/static/**/*.css',
23 | './{{ cookiecutter.package_name }}/static/**/*.js'
24 | ])
25 | .pipe(revReplace({ manifest: gulp.src(MANIFEST_PATH) }))
26 | .pipe(gulp.dest('./{{ cookiecutter.package_name }}/static/')))
27 |
28 | gulp.task('minify:css', () =>
29 | gulp.src('./{{ cookiecutter.package_name }}/static/**/*.css')
30 | .pipe(cleancss())
31 | .pipe(header(BANNER))
32 | .pipe(gulp.dest('./{{ cookiecutter.package_name }}/static/')))
33 |
34 | gulp.task('minify:js', () =>
35 | gulp.src('./{{ cookiecutter.package_name }}/static/**/*.js')
36 | .pipe(uglify({
37 | output: {
38 | preamble: BANNER,
39 | },
40 | }))
41 | .pipe(gulp.dest('./{{ cookiecutter.package_name }}/static/')))
42 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "{{ cookiecutter.repo_name }}",
3 | "version": "0.1.0",
4 | "description": "{{ cookiecutter.description }}",
5 | "scripts": {
6 | "build": "gulp build:production",
7 | "lint": "stylelint './{{ cookiecutter.package_name }}/static_src/css/**/*.css' && standard './{{ cookiecutter.package_name }}/static_src/js/**/*.js'",
8 | "dev": "gulp watch",
9 | "postinstall": "if [ \"$NODE_ENV\" = \"production\" ]; then npm run build; fi",
10 | "test": "npm run lint",
11 | "tinify": "gulp tinify"
12 | },
13 | "author": "{{ cookiecutter.author }}",
14 | "devDependencies": {
15 | "@babel/core": "^7.2.2",
16 | "@babel/preset-env": "^7.2.3",
17 | "@babel/register": "^7.0.0",
18 | "autoprefixer": "9.4.4",
19 | "babel-loader": "^8.0.5",
20 | "browser-sync": "^2.18.8",
21 | "browserify": "16.2.3",
22 | "del": "3.0.0",
23 | "factor-vinylify": "3.0.4",
24 | "gulp": "3.9.1",
25 | "gulp-clean-css": "^4.0.0",
26 | "gulp-header": "^2.0.7",
27 | "gulp-rev": "^9.0.0",
28 | "gulp-rev-replace": "^0.4.4",
29 | "gulp-sass": "^4.0.2",
30 | "gulp-sourcemaps": "2.6.4",
31 | "gulp-tinify": "^1.0.2",
32 | "gulp-uglify": "^3.0.1",
33 | "node-sass": "^4.11.0",
34 | "run-sequence": "2.2.1",
35 | "sass": "^1.15.3",
36 | "standard": "^10.0.1",
37 | "stylelint": "^7.10.1",
38 | "stylelint-config-suitcss": "^11.0.0",
39 | "webpack": "^4.28.4",
40 | "webpack-stream": "^5.2.1"
41 | },
42 | "engines": {
43 | "node": "^8.12",
44 | "npm": "^6.4"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/templates/base.html:
--------------------------------------------------------------------------------
1 | {% raw %}
2 | {% load static %}
3 |
4 |
5 |
6 |
7 |
8 |
9 | {% endraw %}{{ cookiecutter.project_name }}{% raw %}
10 |
11 |
12 |
13 |
14 | {# Facebook Open Graph Tags #}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | {# Twitter Summary Card with Large Image tags #}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | {% block content %}
36 | {% endblock content %}
37 |
38 | {# Structured Data for Logo and Social Profile Links #}
39 |
55 |
56 | {% endraw %}
57 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mo-django
2 |
3 | 
4 |
5 | A [cookiecutter](https://github.com/audreyr/cookiecutter) template for ISL Django projects.
6 |
7 | [](https://circleci.com/gh/istrategylabs/mo-django)
8 |
9 | ## Features
10 |
11 | ### Development Environment
12 |
13 | * [editorconfig](http://editorconfig.org/)
14 | * [eslint](http://eslint.org)
15 | * [stylelint](https://stylelint.io)
16 |
17 | ### Front-end
18 |
19 | * [ES2015](http://www.ecma-international.org/ecma-262/6.0/index.html) support via [Babel](https://babeljs.io)
20 | * [Gulp](http://gulpjs.com/)
21 | * [SASS](https://github.com/dlmanning/gulp-sass) (w/[Autoprefixer](https://autoprefixer.github.io/))
22 | * [Browserify](http://browserify.org/)
23 | * [BrowserSync](http://www.browsersync.io/)
24 | * [UglifyJS](https://github.com/mishoo/UglifyJS2/)
25 | * [CleanCSS](https://github.com/jakubpawlowicz/clean-css/tree/3.4)
26 |
27 | ### Python Packages
28 |
29 | * [Django 1.10](https://www.djangoproject.com)
30 | * [dj-database-url](https://github.com/kennethreitz/dj-database-url)
31 | * [django-debug-toolbar](https://github.com/django-debug-toolbar/django-debug-toolbar)
32 | * [django-redis](https://github.com/niwinz/django-redis)
33 | * [django-rq](https://github.com/ui/django-rq)
34 | * [django-rq-wrapper](https://github.com/istrategylabs/django-rq-wrapper)
35 | * [django-storages](https://github.com/jschneier/django-storages/)
36 | * [psycopg2](http://initd.org/psycopg/)
37 | * [python-decouple](https://github.com/henriquebastos/python-decouple/)
38 | * [pytz](http://pytz.sourceforge.net)
39 | * [raven](https://github.com/getsentry/raven-python)
40 | * [rq](http://python-rq.org)
41 |
42 | mo-django is served with:
43 |
44 | * [waitress](http://waitress.readthedocs.org/en/latest/)
45 | * [whitenoise](http://whitenoise.readthedocs.org/en/stable/)
46 |
47 |
48 | ## Starting a new project
49 |
50 | First, make sure you have [cookiecutter](https://github.com/audreyr/cookiecutter) installed. If you are using OS X, [Homebrew](http://brew.sh) can take care of that for you:
51 |
52 | brew install cookiecutter
53 |
54 | Cookiecutter templates can be installed directly from GitHub. Navigate to the directory where you want your project to be created and run:
55 |
56 | cookiecutter gh:istrategylabs/mo-django
57 |
58 | Answer the questions as you are prompted. Once the project has been generated, navigate to your project and link it to github with:
59 |
60 | cd yourprojectname
61 | git init
62 | git remote add origin git@github.com:organization/repo-name.git
63 | git add .
64 | git commit -am 'Mo init'
65 | git push -u origin master
66 |
67 | The project contains a README with instructions on how to get your Django project up and running.
68 |
69 | Go write beautiful code.
70 |
71 |
72 | ## Developing mo-django
73 |
74 | Install requirements:
75 |
76 | pipenv install --dev --python 3.6
77 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/users/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.6 on 2017-03-17 15:58
3 | import django.contrib.auth.models
4 | import django.contrib.auth.validators
5 | from django.db import migrations, models
6 | import django.utils.timezone
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | initial = True
12 |
13 | dependencies = [
14 | ('auth', '0008_alter_user_username_max_length'),
15 | ]
16 |
17 | operations = [
18 | migrations.CreateModel(
19 | name='User',
20 | fields=[
21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
22 | ('password', models.CharField(max_length=128, verbose_name='password')),
23 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
24 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
25 | ('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')),
26 | ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
27 | ('last_name', models.CharField(blank=True, max_length=30, verbose_name='last name')),
28 | ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
29 | ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
30 | ('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')),
31 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
32 | ('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')),
33 | ('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')),
34 | ],
35 | options={
36 | 'abstract': False,
37 | 'verbose_name': 'user',
38 | 'verbose_name_plural': 'users',
39 | },
40 | managers=[
41 | ('objects', django.contrib.auth.models.UserManager()),
42 | ],
43 | ),
44 | ]
45 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/README.md:
--------------------------------------------------------------------------------
1 | # {{ cookiecutter.project_name }}
2 |
3 | {{ cookiecutter.description }}
4 |
5 | ---
6 |
7 | **Congratulations on your new mo-django project!**
8 |
9 | Below you will find instructions on how to bootstrap the project.
10 | This README should be updated to reflect the current state of the project,
11 | with any additions or modifications to the setup procedures or other items
12 | of note.
13 |
14 | **Now just delete this block and let's get going!**
15 |
16 | ---
17 |
18 | ## Developing
19 |
20 | ### Requirements
21 |
22 | * [Python 3](https://www.python.org) (with [pipenv](http://pipenv.readthedocs.io/en/latest/))
23 | * [foreman](http://ddollar.github.io/foreman/)
24 | * [PostgreSQL](https://www.postgresql.org)
25 | * [Redis](https://redis.io)
26 | * [nvm](https://github.com/creationix/nvm)
27 |
28 | ### Python and Django
29 |
30 | First you need to configure your environment:
31 |
32 | ```
33 | cp env.example .env
34 | ```
35 |
36 | Edit *.env* and set the values you need to run the project locally. Foreman will take care
37 | of loading these values into the environment when you execute a command.
38 |
39 | Next, create a Python 3 virtual environment and install the requirements:
40 |
41 | ```
42 | pipenv install --dev --python 3.7
43 | pipenv shell
44 | ```
45 |
46 | Create the database specified in *.env*, run the initial model migration,
47 | and create a super user:
48 |
49 | ```
50 | createdb {{ cookiecutter.package_name }}
51 | foreman run python manage.py migrate
52 | foreman run python manage.py createsuperuser
53 | ```
54 |
55 | ### Front End Tools
56 |
57 | Use nvm to install the correct version
58 | of Node.js and install the front-end dependencies:
59 |
60 | ```
61 | nvm install
62 | npm install
63 | ```
64 |
65 | Do an initial build of assets:
66 |
67 | ```
68 | npm run build
69 | ```
70 |
71 |
72 | ## Running the Project
73 |
74 | First load the virtualenv:
75 |
76 | ```
77 | pipenv shell
78 | ```
79 |
80 | Then use [foreman](http://ddollar.github.io/foreman/) to run the development processes:
81 |
82 | ```
83 | foreman start -f Procfile.dev
84 | ```
85 |
86 | *Procfile.dev* defines the following processes:
87 |
88 | * web: the Django development server
89 | * static: the gulp watch process
90 | * rqworker: the RQ worker process (high, low, and default)
91 |
92 | `foreman start -f Procfile.dev` will start all of the processes at once. If you
93 | want to run a specific process, you can specify it directly:
94 |
95 | ```
96 | foreman start -f Procfile.dev web
97 | ```
98 |
99 | or
100 |
101 | ```
102 | foreman start -f Procfile.dev rqworker
103 | ```
104 |
105 |
106 | ### Procfile
107 |
108 | When deployed to production or staging, the application and any other processes will be run as defined in the Procfile. You can run this file locally using [foreman](http://ddollar.github.io/foreman/) to launch the application the same way it will be run in production:
109 |
110 | ```
111 | foreman start
112 | ```
113 |
114 | You are **highly encouraged** to do this before finishing features to make sure the app runs as expected before it is deployed.
115 |
116 |
117 | ## Deploying the Project
118 |
119 | ### Set Environment Variables
120 |
121 | | Environment Variable | Description |
122 | |----------------------|-------------|
123 | | DEBUG | `True` in development, `False` otherwise |
124 | | SECRET_KEY | Set this to a different value in every environment |
125 | | ALLOWED_HOSTS | comma separated list of allowed domains |
126 | | DATABASE_URL | database config URI |
127 | | SECURE_SSL_REDIRECT | disables SSL check when `True` |
128 |
129 |
130 | ## Deploying on Heroku
131 |
132 | In addition to [deploying the project](#deploying-the-project), some additional steps are necessary for deployment to [Heroku](https://heroku.com).
133 |
134 | ### Multiple Buildpacks
135 |
136 | In order to build static assets, we'll include the nodejs buildpack in addition
137 | to the Python buildpack.
138 |
139 | ```
140 | heroku buildpacks:set https://github.com/heroku/heroku-buildpack-python
141 | heroku buildpacks:add --index 1 https://github.com/istrategylabs/heroku-buildpack-node-cleanup
142 | heroku buildpacks:add --index 2 https://github.com/heroku/heroku-buildpack-nodejs
143 | ```
144 |
145 | For more information, see Heroku's [multiple buildpack guide](
146 | https://devcenter.heroku.com/articles/using-multiple-buildpacks-for-an-app).
147 |
148 |
149 | ### Database
150 |
151 | ```
152 | heroku addons:create heroku-postgresql:hobby-basic
153 | ```
154 |
155 |
156 | ### Redis
157 |
158 | ```
159 | heroku addons:create heroku-redis:hobby-dev --maxmemory volatile-lru
160 | ```
161 |
162 |
163 | ### Scheduler
164 |
165 | Heroku provides a basic scheduled task runner, but don't worry about installing
166 | it unless you project needs one.
167 |
168 | ```
169 | heroku addons:create scheduler:standard
170 | ```
171 |
172 | To create a new scheduled task, run:
173 |
174 | ```
175 | heroku addons:open scheduler
176 | ```
177 |
178 | The scheduler admin will open in your browser, then click the
179 | **Add new job** button.
180 |
181 | ### HTTPS
182 |
183 | All projects should use HTTPS in production. Some projects will terminate on
184 | Heroku, others on CloudFront. Ask your system administrator if
185 | Heroku SSL is right for you.
186 |
187 | ```
188 | heroku addons:create ssl:endpoint
189 | ```
190 |
191 | Follow the
192 | [SSL Endpoint documentation](https://devcenter.heroku.com/articles/ssl-endpoint)
193 | to upload the custom cert and finish configuration.
194 |
195 | ## Operational Notes
196 |
197 | The setting `RQ_SHOW_ADMIN_LINK = True` tells django-rq to override the base django admin template. If your project wants to override the base admin template, you should disable this feature and add a link to django-rq yourself. [Documentation](https://github.com/ui/django-rq#queue-statistics)
198 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for {{ cookiecutter.project_name }} project.
3 |
4 | For more information on this file, see
5 | https://docs.djangoproject.com/en/1.7/topics/settings/
6 |
7 | For the full list of settings and their values, see
8 | https://docs.djangoproject.com/en/1.7/ref/settings/
9 | """
10 |
11 | import os
12 |
13 | import dj_database_url
14 | from decouple import config
15 |
16 |
17 | BASE_DIR = os.path.dirname(os.path.dirname(__file__))
18 | PROJECT_ROOT = os.path.dirname(BASE_DIR)
19 |
20 | # Quick-start development settings - unsuitable for production
21 | # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
22 |
23 | # SECURITY WARNING: keep the secret key used in production secret!
24 | SECRET_KEY = config('SECRET_KEY')
25 |
26 | # SECURITY WARNING: don't run with debug turned on in production!
27 | DEBUG = config('DEBUG', default=False, cast=bool)
28 |
29 | ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='',
30 | cast=lambda v: [s.strip() for s in v.split(',')])
31 |
32 |
33 | # Application definition
34 |
35 | INSTALLED_APPS = [
36 | '{{ cookiecutter.package_name }}',
37 | '{{ cookiecutter.package_name }}.users',
38 | 'django.contrib.admin',
39 | 'django.contrib.auth',
40 | 'django.contrib.contenttypes',
41 | 'django.contrib.sessions',
42 | 'django.contrib.messages',
43 | 'django.contrib.staticfiles',
44 | 'django_rq',
45 | 'django_rq_wrapper',
46 | ]
47 |
48 | MIDDLEWARE = [
49 | 'whitenoise.middleware.WhiteNoiseMiddleware',
50 | 'django.middleware.security.SecurityMiddleware',
51 | 'django.contrib.sessions.middleware.SessionMiddleware',
52 | 'django.middleware.common.CommonMiddleware',
53 | 'django.middleware.csrf.CsrfViewMiddleware',
54 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
55 | 'django.contrib.messages.middleware.MessageMiddleware',
56 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
57 | ]
58 |
59 | AUTH_USER_MODEL = 'users.User'
60 |
61 | if DEBUG and config('DEBUG_TOOLBAR', default=True, cast=bool):
62 | INSTALLED_APPS.extend(['debug_toolbar', ])
63 | MIDDLEWARE.append(
64 | 'debug_toolbar.middleware.DebugToolbarMiddleware')
65 |
66 | INTERNAL_IPS = '127.0.0.1'
67 |
68 | ROOT_URLCONF = '{{ cookiecutter.package_name }}.urls'
69 |
70 | TEMPLATES = [
71 | {
72 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
73 | 'DIRS': [],
74 | 'APP_DIRS': True,
75 | 'OPTIONS': {
76 | 'context_processors': [
77 | 'django.template.context_processors.debug',
78 | 'django.template.context_processors.request',
79 | 'django.contrib.auth.context_processors.auth',
80 | 'django.contrib.messages.context_processors.messages',
81 | ],
82 | 'debug': config('TEMPLATE_DEBUG', default=DEBUG, cast=bool),
83 | },
84 | },
85 | ]
86 |
87 | WSGI_APPLICATION = '{{ cookiecutter.package_name }}.wsgi.application'
88 |
89 |
90 | # Database
91 | # https://docs.djangoproject.com/en/1.7/ref/settings/#databases
92 |
93 | DATABASES = {
94 | 'default': dj_database_url.config(conn_max_age=600),
95 | }
96 |
97 | # Internationalization
98 | # https://docs.djangoproject.com/en/1.7/topics/i18n/
99 |
100 | LANGUAGE_CODE = 'en-us'
101 |
102 | TIME_ZONE = 'UTC'
103 | USE_I18N = True
104 | USE_L10N = True
105 | USE_TZ = True
106 |
107 | # Static files (CSS, JavaScript, Images)
108 | # https://docs.djangoproject.com/en/1.7/howto/static-files/
109 |
110 | STATIC_URL = '/static/'
111 | STATIC_ROOT = os.path.join(BASE_DIR, 'public', 'static')
112 |
113 | STATICFILES_STORAGE = '{{ cookiecutter.package_name }}.' \
114 | 'storage.DebugErroringCompressedManifestStaticFilesStorage'
115 |
116 | WHITENOISE_ROOT = os.path.join(BASE_DIR, 'public')
117 |
118 | # Media
119 |
120 | MEDIA_URL = '/media/'
121 |
122 | # AWS Settings for Storages
123 | AWS_ACCESS_KEY_ID = config('AWS_ACCESS_KEY_ID', default='')
124 | AWS_SECRET_ACCESS_KEY = config('AWS_SECRET_ACCESS_KEY', default='')
125 | AWS_STORAGE_BUCKET_NAME = config('AWS_STORAGE_BUCKET_NAME', default='')
126 |
127 | if not (AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY and AWS_STORAGE_BUCKET_NAME):
128 | MEDIA_ROOT = os.path.join(BASE_DIR, 'public', 'media')
129 |
130 | else:
131 | INSTALLED_APPS.extend(['storages', ])
132 |
133 | if config('AWS_S3_CUSTOM_DOMAIN', default=None):
134 | AWS_S3_URL_PROTOCOL = 'https:'
135 | AWS_S3_CUSTOM_DOMAIN = config('AWS_S3_CUSTOM_DOMAIN', default='')
136 |
137 | AWS_DEFAULT_ACL = 'private'
138 | AWS_QUERYSTRING_AUTH = True
139 | AWS_QUERYSTRING_EXPIRE = 60 * 60 * 24
140 | AWS_S3_FILE_OVERWRITE = False
141 | AWS_LOCATION = 'media'
142 |
143 | DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
144 |
145 |
146 | # SSL
147 |
148 | SECURE_SSL_REDIRECT = config('SECURE_SSL_REDIRECT', default=True, cast=bool)
149 | SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
150 | SESSION_COOKIE_SECURE = SECURE_SSL_REDIRECT
151 | CSRF_COOKIE_SECURE = SECURE_SSL_REDIRECT
152 |
153 |
154 | # logging
155 | # levels: https://docs.python.org/3/library/logging.html#logging-levels
156 |
157 | DJANGO_LOG_LEVEL = config('DJANGO_LOG_LEVEL', default='ERROR').upper()
158 | DJANGO_LOG_FORMAT = config('DJANGO_LOG_FORMAT', default='simple').lower()
159 |
160 | LOGGING = {
161 | 'version': 1,
162 | 'disable_existing_loggers': False,
163 | 'formatters': {
164 | 'simple': {
165 | 'format': '[%(asctime)s] %(levelname)s "%(message)s"',
166 | 'datefmt': '%d/%b/%Y %H:%M:%S',
167 | },
168 | 'verbose': {
169 | 'format': '[%(asctime)s] %(levelname)s %(name)s.%(funcName)s:%(lineno)d "%(message)s"', # noqa
170 | 'datefmt': '%d/%b/%Y %H:%M:%S',
171 | }
172 | },
173 | 'datefmt': '%d/%b/%Y %H:%M:%S',
174 | 'handlers': {
175 | 'console': {
176 | 'level': DJANGO_LOG_LEVEL,
177 | 'class': 'logging.StreamHandler',
178 | 'formatter': DJANGO_LOG_FORMAT,
179 | },
180 | },
181 | 'loggers': {
182 | 'django.request': {
183 | 'handlers': ['console'],
184 | 'level': 'DEBUG',
185 | },
186 | '{{ cookiecutter.package_name }}': {
187 | 'handlers': ['console'],
188 | 'level': 'DEBUG',
189 | },
190 | },
191 | }
192 |
193 |
194 | # Redis
195 |
196 | SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
197 | CACHES = {
198 | 'default': {
199 | 'BACKEND': 'django_redis.cache.RedisCache',
200 | 'LOCATION': config('REDIS_URL', default='redis://localhost:6379/0'),
201 | 'OPTIONS': {
202 | 'CLIENT_CLASS': 'django_redis.client.DefaultClient',
203 | }
204 | },
205 | }
206 |
207 |
208 | # rq
209 |
210 | RQ_QUEUES = {
211 | 'default': {
212 | 'URL': config('REDIS_URL', default='redis://localhost:6379/0'),
213 | },
214 | 'high': {
215 | 'URL': config('REDIS_URL', default='redis://localhost:6379/0'),
216 | },
217 | 'low': {
218 | 'URL': config('REDIS_URL', default='redis://localhost:6379/0'),
219 | },
220 | }
221 |
222 | RQ_SHOW_ADMIN_LINK = True
223 |
224 |
225 | # Sentry
226 |
227 | SENTRY_DSN = config('SENTRY_DSN', default=None)
228 |
229 | if SENTRY_DSN:
230 | INSTALLED_APPS.extend([
231 | 'raven.contrib.django.raven_compat',
232 | ])
233 | RAVEN_CONFIG = {
234 | 'dsn': SENTRY_DSN,
235 | }
236 |
--------------------------------------------------------------------------------
/{{cookiecutter.repo_name}}/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "{{ cookiecutter.repo_name }}",
3 | "version": "0.1.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@webassemblyjs/ast": {
8 | "version": "1.7.11",
9 | "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz",
10 | "integrity": "sha512-ZEzy4vjvTzScC+SH8RBssQUawpaInUdMTYwYYLh54/s8TuT0gBLuyUnppKsVyZEi876VmmStKsUs28UxPgdvrA==",
11 | "requires": {
12 | "@webassemblyjs/helper-module-context": "1.7.11",
13 | "@webassemblyjs/helper-wasm-bytecode": "1.7.11",
14 | "@webassemblyjs/wast-parser": "1.7.11"
15 | }
16 | },
17 | "@webassemblyjs/floating-point-hex-parser": {
18 | "version": "1.7.11",
19 | "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.11.tgz",
20 | "integrity": "sha512-zY8dSNyYcgzNRNT666/zOoAyImshm3ycKdoLsyDw/Bwo6+/uktb7p4xyApuef1dwEBo/U/SYQzbGBvV+nru2Xg=="
21 | },
22 | "@webassemblyjs/helper-api-error": {
23 | "version": "1.7.11",
24 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.11.tgz",
25 | "integrity": "sha512-7r1qXLmiglC+wPNkGuXCvkmalyEstKVwcueZRP2GNC2PAvxbLYwLLPr14rcdJaE4UtHxQKfFkuDFuv91ipqvXg=="
26 | },
27 | "@webassemblyjs/helper-code-frame": {
28 | "version": "1.7.11",
29 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.11.tgz",
30 | "integrity": "sha512-T8ESC9KMXFTXA5urJcyor5cn6qWeZ4/zLPyWeEXZ03hj/x9weSokGNkVCdnhSabKGYWxElSdgJ+sFa9G/RdHNw==",
31 | "requires": {
32 | "@webassemblyjs/wast-printer": "1.7.11"
33 | }
34 | },
35 | "@webassemblyjs/helper-fsm": {
36 | "version": "1.7.11",
37 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.11.tgz",
38 | "integrity": "sha512-nsAQWNP1+8Z6tkzdYlXT0kxfa2Z1tRTARd8wYnc/e3Zv3VydVVnaeePgqUzFrpkGUyhUUxOl5ML7f1NuT+gC0A=="
39 | },
40 | "@webassemblyjs/helper-module-context": {
41 | "version": "1.7.11",
42 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.11.tgz",
43 | "integrity": "sha512-JxfD5DX8Ygq4PvXDucq0M+sbUFA7BJAv/GGl9ITovqE+idGX+J3QSzJYz+LwQmL7fC3Rs+utvWoJxDb6pmC0qg=="
44 | },
45 | "@webassemblyjs/helper-wasm-bytecode": {
46 | "version": "1.7.11",
47 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.11.tgz",
48 | "integrity": "sha512-cMXeVS9rhoXsI9LLL4tJxBgVD/KMOKXuFqYb5oCJ/opScWpkCMEz9EJtkonaNcnLv2R3K5jIeS4TRj/drde1JQ=="
49 | },
50 | "@webassemblyjs/wast-parser": {
51 | "version": "1.7.11",
52 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.7.11.tgz",
53 | "integrity": "sha512-lEyVCg2np15tS+dm7+JJTNhNWq9yTZvi3qEhAIIOaofcYlUp0UR5/tVqOwa/gXYr3gjwSZqw+/lS9dscyLelbQ==",
54 | "requires": {
55 | "@webassemblyjs/ast": "1.7.11",
56 | "@webassemblyjs/floating-point-hex-parser": "1.7.11",
57 | "@webassemblyjs/helper-api-error": "1.7.11",
58 | "@webassemblyjs/helper-code-frame": "1.7.11",
59 | "@webassemblyjs/helper-fsm": "1.7.11",
60 | "@xtuc/long": "4.2.1"
61 | }
62 | },
63 | "@webassemblyjs/wast-printer": {
64 | "version": "1.7.11",
65 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.7.11.tgz",
66 | "integrity": "sha512-m5vkAsuJ32QpkdkDOUPGSltrg8Cuk3KBx4YrmAGQwCZPRdUHXxG4phIOuuycLemHFr74sWL9Wthqss4fzdzSwg==",
67 | "requires": {
68 | "@webassemblyjs/ast": "1.7.11",
69 | "@webassemblyjs/wast-parser": "1.7.11",
70 | "@xtuc/long": "4.2.1"
71 | }
72 | },
73 | "@xtuc/long": {
74 | "version": "4.2.1",
75 | "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.1.tgz",
76 | "integrity": "sha512-FZdkNBDqBRHKQ2MEbSC17xnPFOhZxeJ2YGSfr2BKf3sujG49Qe3bB+rGCwQfIaA7WHnGeGkSijX4FuBCdrzW/g=="
77 | },
78 | "d": {
79 | "version": "1.0.0",
80 | "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
81 | "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
82 | "requires": {
83 | "es5-ext": "0.10.46"
84 | }
85 | },
86 | "es5-ext": {
87 | "version": "0.10.46",
88 | "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz",
89 | "integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==",
90 | "requires": {
91 | "es6-iterator": "2.0.3",
92 | "es6-symbol": "3.1.1",
93 | "next-tick": "1.0.0"
94 | }
95 | },
96 | "es6-iterator": {
97 | "version": "2.0.3",
98 | "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
99 | "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
100 | "requires": {
101 | "d": "1.0.0",
102 | "es5-ext": "0.10.46",
103 | "es6-symbol": "3.1.1"
104 | }
105 | },
106 | "es6-symbol": {
107 | "version": "3.1.1",
108 | "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
109 | "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
110 | "requires": {
111 | "d": "1.0.0",
112 | "es5-ext": "0.10.46"
113 | }
114 | },
115 | "expand-tilde": {
116 | "version": "2.0.2",
117 | "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
118 | "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
119 | "requires": {
120 | "homedir-polyfill": "1.0.1"
121 | }
122 | },
123 | "global-modules": {
124 | "version": "1.0.0",
125 | "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
126 | "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
127 | "requires": {
128 | "global-prefix": "1.0.2",
129 | "is-windows": "1.0.2",
130 | "resolve-dir": "1.0.1"
131 | }
132 | },
133 | "global-prefix": {
134 | "version": "1.0.2",
135 | "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
136 | "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
137 | "requires": {
138 | "expand-tilde": "2.0.2",
139 | "homedir-polyfill": "1.0.1",
140 | "ini": "1.3.5",
141 | "is-windows": "1.0.2",
142 | "which": "1.3.1"
143 | }
144 | },
145 | "homedir-polyfill": {
146 | "version": "1.0.1",
147 | "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz",
148 | "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=",
149 | "requires": {
150 | "parse-passwd": "1.0.0"
151 | }
152 | },
153 | "ini": {
154 | "version": "1.3.5",
155 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
156 | "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
157 | },
158 | "is-windows": {
159 | "version": "1.0.2",
160 | "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
161 | "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="
162 | },
163 | "isexe": {
164 | "version": "2.0.0",
165 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
166 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
167 | },
168 | "next-tick": {
169 | "version": "1.0.0",
170 | "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
171 | "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
172 | },
173 | "parse-passwd": {
174 | "version": "1.0.0",
175 | "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
176 | "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY="
177 | },
178 | "resolve-dir": {
179 | "version": "1.0.1",
180 | "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
181 | "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=",
182 | "requires": {
183 | "expand-tilde": "2.0.2",
184 | "global-modules": "1.0.0"
185 | }
186 | },
187 | "which": {
188 | "version": "1.3.1",
189 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
190 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
191 | "requires": {
192 | "isexe": "2.0.0"
193 | }
194 | }
195 | }
196 | }
197 |
--------------------------------------------------------------------------------