Skip to content
Snippets Groups Projects
Unverified Commit d4175ad7 authored by Kirill Malev's avatar Kirill Malev Committed by GitHub
Browse files

Merge branch 'master' into bootstrap4

parents a1757b28 685067e1
No related branches found
No related tags found
No related merge requests found
Showing
with 177 additions and 111 deletions
......@@ -25,6 +25,7 @@ Features
- Bootstrap 3 and Font Awesome 4 with starter templates
- Flask-SQLAlchemy with basic User model
- Easy database migrations with Flask-Migrate
- Configuration in environment variables, as per `The Twelve-Factor App <https://12factor.net/config>`_
- Flask-WTForms with login and registration forms
- Flask-Login for authentication
- Flask-Bcrypt for password hashing
......
......@@ -4,5 +4,6 @@
"github_username": "sloria",
"project_name": "My Flask App",
"app_name": "myflaskapp",
"project_short_description": "A flasky app."
"project_short_description": "A flasky app.",
"use_pipenv": ["no", "yes"]
}
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Post gen hook to ensure that the generated project
hase only one package managment, either pipenv or pip."""
import os
import shutil
import sys
def clean_extra_package_managment_files():
"""Removes either requirements files and folderor the Pipfile."""
use_pipenv = '{{cookiecutter.use_pipenv}}'
to_delete = []
if use_pipenv == 'yes':
to_delete = to_delete + ['requirements.txt', 'requirements']
else:
to_delete.append('Pipfile')
try:
for file_or_dir in to_delete:
if os.path.isfile(file_or_dir):
os.remove(file_or_dir)
else:
shutil.rmtree(file_or_dir)
sys.exit(0)
except OSError as e:
sys.stdout.write(
'While attempting to remove file(s) an error occurred'
)
sys.stdout.write('Error: {}'.format(e))
if __name__ == '__main__':
clean_extra_package_managment_files()
......@@ -13,7 +13,6 @@ with open(os.path.join(HERE, 'cookiecutter.json'), 'r') as fp:
COOKIECUTTER_SETTINGS = json.load(fp)
# Match default value of app_name from cookiecutter.json
COOKIE = os.path.join(HERE, COOKIECUTTER_SETTINGS['app_name'])
AUTOAPP = os.path.join(COOKIE, 'autoapp.py')
REQUIREMENTS = os.path.join(COOKIE, 'requirements', 'dev.txt')
......@@ -42,7 +41,8 @@ def clean(ctx):
def _run_flask_command(ctx, command):
ctx.run('FLASK_APP={0} flask {1}'.format(AUTOAPP, command), echo=True)
os.chdir(COOKIE)
ctx.run('flask {0}'.format(command), echo=True)
@task(pre=[clean, build])
......@@ -52,12 +52,14 @@ def test(ctx):
echo=True)
_run_npm_command(ctx, 'run lint')
os.chdir(COOKIE)
shutil.copyfile(os.path.join(COOKIE, '.env.example'),
os.path.join(COOKIE, '.env'))
_run_flask_command(ctx, 'lint')
_run_flask_command(ctx, 'test')
@task
def readme(ctx, browse=False):
ctx.run("rst2html.py README.rst > README.html")
ctx.run('rst2html.py README.rst > README.html')
if browse:
webbrowser.open_new_tab('README.html')
# Environment variable overrides for local development
FLASK_APP=autoapp.py
FLASK_ENV=development
DATABASE_URL="sqlite:////tmp/dev.db"
SECRET_KEY="not-so-secret"
......@@ -51,3 +51,9 @@ env/
# webpack-built files
/{{cookiecutter.app_name}}/static/build/
/{{cookiecutter.app_name}}/webpack/manifest.json
# Configuration
.env
# Development database
*.db
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[packages]
# Flask
Flask = "==1.0.2"
MarkupSafe = "==1.0"
Werkzeug = "==0.14.1"
Jinja2 = "==2.10"
itsdangerous = "==0.24"
click = ">=5.0"
# Database
Flask-SQLAlchemy = "==2.3.2"
psycopg2 = "==2.7.5"
SQLAlchemy = "==1.2.8"
# Migrations
Flask-Migrate = "==2.2.0"
# Forms
Flask-WTF = "==0.14.2"
WTForms = "==2.2.1"
# Deployment
gunicorn = ">=19.1.1"
# Webpack
flask-webpack = "==0.1.0"
# Auth
Flask-Login = "==0.4.1"
Flask-Bcrypt = "==0.7.1"
# Caching
Flask-Caching = ">=1.0.0"
# Debug toolbar
Flask-DebugToolbar = "==0.10.1"
# Environment variable parsing
environs = "==4.0.0"
[dev-packages]
# Testing
pytest = "==3.6.1"
WebTest = "==2.0.30"
factory-boy = "==2.11.*"
# Lint and code style
flake8 = "==3.5.0"
flake8-blind-except = "==0.1.1"
flake8-debugger = "==3.1.0"
flake8-docstrings = "==1.3.0"
flake8-isort = "==2.5"
flake8-quotes = "==1.0.0"
isort = "==4.3.4"
pep8-naming = "==0.7.0"
......@@ -8,29 +8,21 @@
Quickstart
----------
First, set your app's secret key as an environment variable. For example,
add the following to ``.bashrc`` or ``.bash_profile``.
.. code-block:: bash
export {{cookiecutter.app_name | upper}}_SECRET='something-really-secret'
Run the following commands to bootstrap your environment ::
git clone https://github.com/{{cookiecutter.github_username}}/{{cookiecutter.app_name}}
cd {{cookiecutter.app_name}}
{%- if cookiecutter.use_pipenv == "yes" %}
pipenv install --dev
{%- else %}
pip install -r requirements/dev.txt
{%- endif %}
cp .env.example .env
npm install
npm start # run the webpack dev server and flask server using concurrently
You will see a pretty welcome screen.
In general, before running shell commands, set the ``FLASK_APP`` and
``FLASK_DEBUG`` environment variables ::
export FLASK_APP=autoapp.py
export FLASK_DEBUG=1
Once you have installed your DBMS, run the following to create your app's
database tables and perform the initial migration ::
......@@ -45,12 +37,14 @@ Deployment
To deploy::
export FLASK_ENV=production
export FLASK_DEBUG=0
export DATABASE_URL="<YOUR DATABASE URL>"
npm run build # build assets with webpack
flask run # start the flask server
In your production environment, make sure the ``FLASK_DEBUG`` environment
variable is unset or is set to ``0``, so that ``ProdConfig`` is used.
variable is unset or is set to ``0``.
Shell
......
# -*- coding: utf-8 -*-
"""Create an application instance."""
from flask.helpers import get_debug_flag
from {{cookiecutter.app_name}}.app import create_app
from {{cookiecutter.app_name}}.settings import DevConfig, ProdConfig
CONFIG = DevConfig if get_debug_flag() else ProdConfig
app = create_app(CONFIG)
app = create_app()
......@@ -6,7 +6,7 @@
"build": "NODE_ENV=production webpack --progress --colors -p",
"start": "concurrently -n \"WEBPACK,FLASK\" -c \"bgBlue.bold,bgMagenta.bold\" \"npm run webpack-dev-server\" \"npm run flask-server\"",
"webpack-dev-server": "NODE_ENV=debug webpack-dev-server --port 2992 --hot --inline",
"flask-server": "FLASK_APP=$PWD/autoapp.py FLASK_DEBUG=1 flask run",
"flask-server": "flask run",
"lint": "eslint \"assets/js/*.js\""
},
"repository": {
......@@ -28,25 +28,25 @@
},
"devDependencies": {
"babel-core": "^6.25.0",
"babel-eslint": "^7.2.3",
"babel-eslint": "^9.0.0",
"babel-loader": "^7.0.0",
"babel-preset-env": "^1.6.0",
"concurrently": "^3.5.0",
"css-loader": "^0.28.4",
"eslint": "^3.19.0",
"eslint-config-airbnb-base": "^11.2.0",
"concurrently": "^4.0.1",
"css-loader": "^1.0.0",
"eslint": "^5.3.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-plugin-import": "^2.3.0",
"extract-text-webpack-plugin": "^2.1.2",
"file-loader": "^0.11.2",
"file-loader": "^2.0.0",
"font-awesome-webpack": "0.0.5-beta.2",
"less": "^2.7.2",
"less": "^3.8.0",
"less-loader": "^4.0.4",
"manifest-revision-webpack-plugin": "^0.4.0",
"raw-loader": "^0.5.1",
"style-loader": "^0.18.2",
"url-loader": "^0.5.9",
"style-loader": "^0.23.0",
"url-loader": "^1.0.1",
"webpack": "^2.6.1",
"webpack-dev-server": "^2.4.5",
"webpack-dev-server": "^3.1.5",
"sync-exec": "^0.6.2"
}
}
......@@ -2,8 +2,8 @@
-r prod.txt
# Testing
pytest==3.6.1
WebTest==2.0.29
pytest==3.7.4
WebTest==2.0.30
factory-boy==2.11.1
# Lint and code style
......
......@@ -11,10 +11,10 @@ click>=5.0
# Database
Flask-SQLAlchemy==2.3.2
psycopg2==2.7.5
SQLAlchemy==1.2.8
SQLAlchemy==1.2.11
# Migrations
Flask-Migrate==2.2.0
Flask-Migrate==2.2.1
# Forms
Flask-WTF==0.14.2
......@@ -35,3 +35,6 @@ Flask-Caching>=1.0.0
# Debug toolbar
Flask-DebugToolbar==0.10.1
# Environment variable parsing
environs==4.0.0
......@@ -6,7 +6,6 @@ from webtest import TestApp
from {{cookiecutter.app_name}}.app import create_app
from {{cookiecutter.app_name}}.database import db as _db
from {{cookiecutter.app_name}}.settings import TestConfig
from .factories import UserFactory
......@@ -14,7 +13,7 @@ from .factories import UserFactory
@pytest.fixture
def app():
"""An application for the tests."""
_app = create_app(TestConfig)
_app = create_app('tests.settings')
ctx = _app.test_request_context()
ctx.push()
......
"""Settings module for test app."""
ENV = 'development'
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite://'
SECRET_KEY = 'not-so-secret-in-tests'
BCRYPT_LOG_ROUNDS = 4 # For faster tests; needs at least 4 to avoid "ValueError: Invalid rounds"
DEBUG_TB_ENABLED = False
CACHE_TYPE = 'simple' # Can be "memcached", "redis", etc.
SQLALCHEMY_TRACK_MODIFICATIONS = False
WEBPACK_MANIFEST_PATH = 'webpack/manifest.json'
WTF_CSRF_ENABLED = False # Allows form testing
# -*- coding: utf-8 -*-
"""Test configs."""
from {{cookiecutter.app_name}}.app import create_app
from {{cookiecutter.app_name}}.settings import DevConfig, ProdConfig
def test_production_config():
"""Production config."""
app = create_app(ProdConfig)
assert app.config['ENV'] == 'prod'
assert app.config['DEBUG'] is False
assert app.config['DEBUG_TB_ENABLED'] is False
def test_dev_config():
"""Development config."""
app = create_app(DevConfig)
assert app.config['ENV'] == 'dev'
assert app.config['DEBUG'] is True
......@@ -4,10 +4,9 @@ from flask import Flask, render_template
from {{cookiecutter.app_name}} import commands, public, user
from {{cookiecutter.app_name}}.extensions import bcrypt, cache, csrf_protect, db, debug_toolbar, login_manager, migrate, webpack
from {{cookiecutter.app_name}}.settings import ProdConfig
def create_app(config_object=ProdConfig):
def create_app(config_object='{{cookiecutter.app_name}}.settings'):
"""An application factory, as explained here: http://flask.pocoo.org/docs/patterns/appfactories/.
:param config_object: The configuration object to use.
......
# -*- coding: utf-8 -*-
"""Application configuration."""
import os
class Config(object):
"""Base configuration."""
SECRET_KEY = os.environ.get('{{cookiecutter.app_name | upper}}_SECRET', 'secret-key') # TODO: Change me
APP_DIR = os.path.abspath(os.path.dirname(__file__)) # This directory
PROJECT_ROOT = os.path.abspath(os.path.join(APP_DIR, os.pardir))
BCRYPT_LOG_ROUNDS = 13
DEBUG_TB_ENABLED = False # Disable Debug toolbar
DEBUG_TB_INTERCEPT_REDIRECTS = False
CACHE_TYPE = 'simple' # Can be "memcached", "redis", etc.
SQLALCHEMY_TRACK_MODIFICATIONS = False
WEBPACK_MANIFEST_PATH = 'webpack/manifest.json'
class ProdConfig(Config):
"""Production configuration."""
ENV = 'prod'
DEBUG = False
SQLALCHEMY_DATABASE_URI = 'postgresql://localhost/example' # TODO: Change me
DEBUG_TB_ENABLED = False # Disable Debug toolbar
class DevConfig(Config):
"""Development configuration."""
ENV = 'dev'
DEBUG = True
DB_NAME = 'dev.db'
# Put the db file in project root
DB_PATH = os.path.join(Config.PROJECT_ROOT, DB_NAME)
SQLALCHEMY_DATABASE_URI = 'sqlite:///{0}'.format(DB_PATH)
DEBUG_TB_ENABLED = True
CACHE_TYPE = 'simple' # Can be "memcached", "redis", etc.
class TestConfig(Config):
"""Test configuration."""
TESTING = True
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'sqlite://'
BCRYPT_LOG_ROUNDS = 4 # For faster tests; needs at least 4 to avoid "ValueError: Invalid rounds"
WTF_CSRF_ENABLED = False # Allows form testing
"""Application configuration.
Most configuration is set via environment variables.
For local development, use a .env file to set
environment variables.
"""
from environs import Env
env = Env()
env.read_env()
ENV = env.str('FLASK_ENV', default='production')
DEBUG = ENV == 'development'
SQLALCHEMY_DATABASE_URI = env.str('DATABASE_URL')
SECRET_KEY = env.str('SECRET_KEY')
BCRYPT_LOG_ROUNDS = env.int('BCRYPT_LOG_ROUNDS', default=13)
DEBUG_TB_ENABLED = DEBUG
DEBUG_TB_INTERCEPT_REDIRECTS = False
CACHE_TYPE = 'simple' # Can be "memcached", "redis", etc.
SQLALCHEMY_TRACK_MODIFICATIONS = False
WEBPACK_MANIFEST_PATH = 'webpack/manifest.json'
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment