├── .gitignore ├── LICENSE ├── Pipfile ├── README.md ├── circle.yml ├── cookiecutter.json ├── hooks └── post_gen_project.py ├── tests └── test_mo_project.py ├── tox.ini └── {{cookiecutter.repo_name}} ├── .babelrc ├── .editorconfig ├── .gitignore ├── .nvmrc ├── LICENSE ├── Pipfile ├── Procfile ├── Procfile.dev ├── README.md ├── app.json ├── banner.txt ├── bin └── post_compile ├── env.example ├── gulp ├── build.js ├── production.js └── utils.js ├── gulpfile.babel.js ├── manage.py ├── mo.yml ├── package-lock.json ├── package.json ├── public ├── favicon-152.png ├── favicon.ico ├── humans.txt └── robots.txt ├── runtime.txt ├── tox.ini ├── webpack.config.js └── {{cookiecutter.package_name}} ├── __init__.py ├── apps.py ├── settings.py ├── static └── README.md ├── static_src ├── js │ └── app.js └── sass │ ├── app.scss │ └── settings.scss ├── storage.py ├── templates ├── 404.html ├── README.md ├── base.html └── home.html ├── urls.py ├── users ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto_20190123_1458.py │ └── __init__.py ├── models.py ├── tests.py └── views.py ├── views.py └── wsgi.py /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | override: 3 | - pip install tox tox-pyenv 4 | - pyenv local 2.7.10 3.4.3 5 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /hooks/post_gen_project.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islco/mo-django/d24da67fcee7c50ab1dd9cc01858cf772b7ef0f4/hooks/post_gen_project.py -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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}}/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } 4 | -------------------------------------------------------------------------------- /{{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}}/.nvmrc: -------------------------------------------------------------------------------- 1 | 8.11 2 | -------------------------------------------------------------------------------- /{{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}}/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}}/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}}/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}}/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}}/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}}/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}}/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}}/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}}/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 | -------------------------------------------------------------------------------- /{{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}}/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}}/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}}/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}}/mo.yml: -------------------------------------------------------------------------------- 1 | domain: {{ cookiecutter.domain_name }} 2 | heroku: 3 | staging: {{ cookiecutter.repo_name }}-staging 4 | production: {{ cookiecutter.repo_name }} 5 | -------------------------------------------------------------------------------- /{{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 | -------------------------------------------------------------------------------- /{{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}}/public/favicon-152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islco/mo-django/d24da67fcee7c50ab1dd9cc01858cf772b7ef0f4/{{cookiecutter.repo_name}}/public/favicon-152.png -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islco/mo-django/d24da67fcee7c50ab1dd9cc01858cf772b7ef0f4/{{cookiecutter.repo_name}}/public/favicon.ico -------------------------------------------------------------------------------- /{{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}}/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.7.0 2 | -------------------------------------------------------------------------------- /{{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}}/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}}/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islco/mo-django/d24da67fcee7c50ab1dd9cc01858cf772b7ef0f4/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/__init__.py -------------------------------------------------------------------------------- /{{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 | -------------------------------------------------------------------------------- /{{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}}/{{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}}/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}}/{{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}}/static_src/sass/settings.scss: -------------------------------------------------------------------------------- 1 | $color: #000000; 2 | -------------------------------------------------------------------------------- /{{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}}/{{cookiecutter.package_name}}/templates/404.html: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 |
15 | 16 | 17 | 18 |