diff --git a/{{cookiecutter.app_name}}/manage.py b/{{cookiecutter.app_name}}/manage.py index 2f2fe20f53f89665e633da9e4deae661c4d66cda..6abed9e538e1c1604b8ae92363979cb14859f822 100644 --- a/{{cookiecutter.app_name}}/manage.py +++ b/{{cookiecutter.app_name}}/manage.py @@ -1,8 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- import os -import sys -import subprocess from flask_script import Manager, Shell, Server from flask_migrate import MigrateCommand @@ -21,12 +19,14 @@ TEST_PATH = os.path.join(HERE, 'tests') manager = Manager(app) + def _make_context(): """Return context dict for a shell session so you can access app, db, and the User model by default. """ return {'app': app, 'db': db, 'User': User} + @manager.command def test(): """Run the tests.""" @@ -34,6 +34,7 @@ def test(): exit_code = pytest.main([TEST_PATH, '--verbose']) return exit_code + manager.add_command('server', Server()) manager.add_command('shell', Shell(make_context=_make_context)) manager.add_command('db', MigrateCommand) diff --git a/{{cookiecutter.app_name}}/tests/conftest.py b/{{cookiecutter.app_name}}/tests/conftest.py index 1bfe5707cfe0d59f48335e017b8192e6c11bcb3d..31118e75ffd552fab193f7b3faec718d6fabbfe8 100644 --- a/{{cookiecutter.app_name}}/tests/conftest.py +++ b/{{cookiecutter.app_name}}/tests/conftest.py @@ -11,6 +11,7 @@ from {{cookiecutter.app_name}}.database import db as _db from .factories import UserFactory + @pytest.yield_fixture(scope='function') def app(): _app = create_app(TestConfig) @@ -21,11 +22,13 @@ def app(): ctx.pop() + @pytest.fixture(scope='function') def testapp(app): """A Webtest app.""" return TestApp(app) + @pytest.yield_fixture(scope='function') def db(app): _db.app = app diff --git a/{{cookiecutter.app_name}}/tests/factories.py b/{{cookiecutter.app_name}}/tests/factories.py index db06fd4ca14853613714a650def241826a3e6e8f..10b98abde5852a3f6de5ce128c16f9ce46aa9717 100644 --- a/{{cookiecutter.app_name}}/tests/factories.py +++ b/{{cookiecutter.app_name}}/tests/factories.py @@ -5,6 +5,7 @@ from factory.alchemy import SQLAlchemyModelFactory from {{cookiecutter.app_name}}.user.models import User from {{cookiecutter.app_name}}.database import db + class BaseFactory(SQLAlchemyModelFactory): class Meta: @@ -20,4 +21,3 @@ class UserFactory(BaseFactory): class Meta: model = User - diff --git a/{{cookiecutter.app_name}}/tests/test_config.py b/{{cookiecutter.app_name}}/tests/test_config.py index 691f5f6419b5d00285a9ef5d4a04fb02bec15a1c..c945dc7a173666aad60a8cccea5ea063fb861252 100644 --- a/{{cookiecutter.app_name}}/tests/test_config.py +++ b/{{cookiecutter.app_name}}/tests/test_config.py @@ -2,6 +2,7 @@ from {{cookiecutter.app_name}}.app import create_app from {{cookiecutter.app_name}}.settings import ProdConfig, DevConfig + def test_production_config(): app = create_app(ProdConfig) assert app.config['ENV'] == 'prod' diff --git a/{{cookiecutter.app_name}}/tests/test_forms.py b/{{cookiecutter.app_name}}/tests/test_forms.py index 4a37d03cbe64d40b1279c70c2915b5be25b3020e..53e5bd0263300a554f6fefa89ea3d019f8b5cf3e 100644 --- a/{{cookiecutter.app_name}}/tests/test_forms.py +++ b/{{cookiecutter.app_name}}/tests/test_forms.py @@ -5,6 +5,7 @@ from {{cookiecutter.app_name}}.public.forms import LoginForm from {{cookiecutter.app_name}}.user.forms import RegisterForm from .factories import UserFactory + class TestRegisterForm: def test_validate_user_already_registered(self, user): diff --git a/{{cookiecutter.app_name}}/tests/test_models.py b/{{cookiecutter.app_name}}/tests/test_models.py index f8fde0c8940327948133d80c0165cf36955e8404..feb70783b913d9fba780e6db2100ab3d8e489ff9 100644 --- a/{{cookiecutter.app_name}}/tests/test_models.py +++ b/{{cookiecutter.app_name}}/tests/test_models.py @@ -7,6 +7,7 @@ import pytest from {{ cookiecutter.app_name }}.user.models import User, Role from .factories import UserFactory + @pytest.mark.usefixtures('db') class TestUser: diff --git a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/app.py b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/app.py index 47901f5dce6e74f85cd19f9ded866b6942887690..c945411ba81b2ee85c1586c24d238627dc9a24d0 100644 --- a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/app.py +++ b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/app.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -'''The app module, containing the app factory function.''' +"""The app module, containing the app factory function.""" from flask import Flask, render_template from {{cookiecutter.app_name}}.settings import ProdConfig @@ -16,11 +16,11 @@ from {{cookiecutter.app_name}} import public, user def create_app(config_object=ProdConfig): - '''An application factory, as explained here: + """An application factory, as explained here: http://flask.pocoo.org/docs/patterns/appfactories/ :param config_object: The configuration object to use. - ''' + """ app = Flask(__name__) app.config.from_object(config_object) register_extensions(app) diff --git a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/database.py b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/database.py index 31207661b3996a49efafde08b1587c1d86fc108d..09f7f390f30100fb1d886c4ab5814637f2df7753 100644 --- a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/database.py +++ b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/database.py @@ -11,6 +11,7 @@ from .compat import basestring Column = db.Column relationship = relationship + class CRUDMixin(object): """Mixin that adds convenience methods for CRUD (create, read, update, delete) operations. @@ -44,6 +45,7 @@ class Model(CRUDMixin, db.Model): """Base model class that includes CRUD convenience methods.""" __abstract__ = True + # From Mike Bayer's "Building the app" talk # https://speakerdeck.com/zzzeek/building-the-app class SurrogatePK(object): diff --git a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/public/__init__.py b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/public/__init__.py index c6fdbc809c278b484b81192e0635aa06c6128063..4260a4308e29f41546c01156cfc1e2dfb930281c 100644 --- a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/public/__init__.py +++ b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/public/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -'''The public module, including the homepage and user auth.''' +"""The public module, including the homepage and user auth.""" from . import views diff --git a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/public/forms.py b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/public/forms.py index eb834d07b2802cbf19b75778b299914815f4e062..43da1de4c6e8fd68b0009dc61109ad92337a9ffb 100644 --- a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/public/forms.py +++ b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/public/forms.py @@ -1,9 +1,11 @@ +# -*- coding: utf-8 -*- from flask_wtf import Form from wtforms import TextField, PasswordField from wtforms.validators import DataRequired from {{cookiecutter.app_name}}.user.models import User + class LoginForm(Form): username = TextField('Username', validators=[DataRequired()]) password = PasswordField('Password', validators=[DataRequired()]) diff --git a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/public/views.py b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/public/views.py index 36dce070a40f95a7414f4f9833c588b06686dd66..455fdb973d7504b4e9f88dcd0873cb854aa9f239 100644 --- a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/public/views.py +++ b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/public/views.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -'''Public section, including homepage and signup.''' +"""Public section, including homepage and signup.""" from flask import (Blueprint, request, render_template, flash, url_for, redirect, session) from flask.ext.login import login_user, login_required, logout_user @@ -13,6 +13,7 @@ from {{cookiecutter.app_name}}.database import db blueprint = Blueprint('public', __name__, static_folder="../static") + @login_manager.user_loader def load_user(id): return User.get_by_id(int(id)) @@ -32,6 +33,7 @@ def home(): flash_errors(form) return render_template("public/home.html", form=form) + @blueprint.route('/logout/') @login_required def logout(): @@ -39,6 +41,7 @@ def logout(): flash('You are logged out.', 'info') return redirect(url_for('public.home')) + @blueprint.route("/register/", methods=['GET', 'POST']) def register(): form = RegisterForm(request.form, csrf_enabled=False) @@ -53,6 +56,7 @@ def register(): flash_errors(form) return render_template('public/register.html', form=form) + @blueprint.route("/about/") def about(): form = LoginForm(request.form) diff --git a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/settings.py b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/settings.py index 51e6b12d2f4517f359acc41c16feefe56ea9a802..683dc52399f6e12041ea24a4b2ca5d18e4d1833a 100644 --- a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/settings.py +++ b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/settings.py @@ -3,6 +3,7 @@ import os os_env = os.environ + class Config(object): SECRET_KEY = os_env.get('{{cookiecutter.app_name | upper}}_SECRET', 'secret-key') # TODO: Change me APP_DIR = os.path.abspath(os.path.dirname(__file__)) # This directory diff --git a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/__init__.py b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/__init__.py index fe9e0ac026457ed56def2a85942ac7d29193ecbc..a655bf11bdf0ebbc1ec82dc5cb45bde47aea7455 100644 --- a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/__init__.py +++ b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/__init__.py @@ -1,3 +1,3 @@ -'''The user module.''' - +# -*- coding: utf-8 -*- +"""The user module.""" from . import views diff --git a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/forms.py b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/forms.py index 165e81f87088523107240add225a8dc305332420..f4ba798c5421e0fbc5bff46ee7600274f2d11c8b 100644 --- a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/forms.py +++ b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/forms.py @@ -1,9 +1,11 @@ +# -*- coding: utf-8 -*- from flask_wtf import Form from wtforms import TextField, PasswordField from wtforms.validators import DataRequired, Email, EqualTo, Length from .models import User + class RegisterForm(Form): username = TextField('Username', validators=[DataRequired(), Length(min=3, max=25)]) diff --git a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/models.py b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/models.py index 0d9c413eba69d64ecb69989e061f6ca3eee92d88..06ad15a95b2e25202767df775c405a40b7fcd51e 100644 --- a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/models.py +++ b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/models.py @@ -26,6 +26,7 @@ class Role(SurrogatePK, Model): def __repr__(self): return '<Role({name})>'.format(name=self.name) + class User(UserMixin, SurrogatePK, Model): __tablename__ = 'users' diff --git a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/views.py b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/views.py index 9d6fd2e74c991d443379383b88d38c54ad601802..e8c41ef7011de0ee8e47226dd43a01e9f6103526 100644 --- a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/views.py +++ b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/views.py @@ -3,7 +3,7 @@ from flask import Blueprint, render_template from flask.ext.login import login_required blueprint = Blueprint("user", __name__, url_prefix='/users', - static_folder="../static") + static_folder="../static") @blueprint.route("/") diff --git a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/utils.py b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/utils.py index bfaf4fe027fd7cf01ec71c6202458324e04f53de..2def30c8a9f194a173a49a38dd952544af48acf9 100644 --- a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/utils.py +++ b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/utils.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -'''Helper utilities and decorators.''' +"""Helper utilities and decorators.""" from flask import flash + def flash_errors(form, category="warning"): - '''Flash all errors for a form.''' + """Flash all errors for a form.""" for field, errors in form.errors.items(): for error in errors: flash("{0} - {1}" - .format(getattr(form, field).label.text, error), category) + .format(getattr(form, field).label.text, error), category)