Modular structure with blueprints

Views are all in located in the modules package, which contains blueprints
Also, use alert-warning instead of alert-error for Bootstrap3
master
Steven Loria 11 years ago
parent 0f142a65e8
commit 3a411b4abe
  1. 6
      README.rst
  2. 11
      {{cookiecutter.repo_name}}/manage.py
  3. 40
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/app.py
  4. 13
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/main.py
  5. 6
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/models.py
  6. 1
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/modules/__init__.py
  7. 14
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/modules/member.py
  8. 60
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/modules/public.py
  9. 3
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/settings.py
  10. 4
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/templates/_layouts/footer.html
  11. 12
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/templates/_layouts/nav.html
  12. 3
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/tests/unit_tests.py
  13. 22
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/utils.py

@ -16,6 +16,7 @@ Features
- A simple ``manage.py`` script.
- CSS and JS minification using Flask-Assets
- Easily switch between development and production environments through the MYFLASKAPP_ENV system variable.
- Utilizes best practices: `Blueprints <http://flask.pocoo.org/docs/blueprints/>`_ and `Application Factory <http://flask.pocoo.org/docs/patterns/appfactories/>`_ patterns
Screenshots
-----------
@ -42,9 +43,10 @@ Inspiration
-----------
- `Building Websites in Python with Flask <http://maximebf.com/blog/2012/10/building-websites-in-python-with-flask/>`_
- `Getting Bigger with Flask <http://maximebf.com/blog/2012/11/getting-bigger-with-flask/>`_
- `Structuring Flask Apps <http://charlesleifer.com/blog/structuring-flask-apps-a-how-to-for-those-coming-from-django/>`_
- `Flask-Foundation <https://github.com/JackStouffer/Flask-Foundation>`_
- `flask-basic-registration <https://github.com/mjhea0/flask-basic-registration>`_
- `Flask-Foundation <https://github.com/JackStouffer/Flask-Foundation>`_ by `@JackStouffer <https://github.com/JackStouffer>`_
- `flask-basic-registration <https://github.com/mjhea0/flask-basic-registration>`_ by `@mjhea0 <https://github.com/mjhea0>`_
- `Flask Official Documentation <http://flask.pocoo.org/docs/>`_

@ -1,9 +1,16 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import subprocess
from flask.ext.script import Manager, Shell, Server
from {{ cookiecutter.repo_name }} import models
from {{ cookiecutter.repo_name }}.main import app, db
from {{cookiecutter.repo_name }} import models
from {{cookiecutter.repo_name }}.app import create_app
from {{cookiecutter.repo_name}}.models import db
env = os.environ.get("{{cookiecutter.repo_name | upper }}_ENV", 'prod')
app = create_app("{{cookiecutter.repo_name}}.settings.{0}Config"
.format(env.capitalize()), env)
manager = Manager(app)
TEST_CMD = "nosetests"

@ -6,19 +6,31 @@ from flask.ext.assets import Environment
from webassets.loaders import PythonLoader
from {{cookiecutter.repo_name}} import assets
from {{cookiecutter.repo_name}}.models import db
app = Flask(__name__)
# The environment variable, either 'prod' or 'dev'
env = os.environ.get("{{cookiecutter.repo_name | upper}}_ENV", "prod")
# Use the appropriate environment-specific settings
app.config.from_object('{{cookiecutter.repo_name}}.settings.{env}Config'
.format(env=env.capitalize()))
app.config['ENV'] = env
db = SQLAlchemy(app)
# Register asset bundles
assets_env = Environment()
assets_env.init_app(app)
assets_loader = PythonLoader(assets)
for name, bundle in assets_loader.load_bundles().iteritems():
assets_env.register(name, bundle)
def create_app(config_object, env):
'''An application factory, as explained here:
http://flask.pocoo.org/docs/patterns/appfactories/
:param config_object: The configuration object to use.
:param env: A string, the current environment. Either "dev" or "prod"
'''
app = Flask(__name__)
app.config.from_object('{{cookiecutter.repo_name}}.settings.{env}Config'
.format(env=env.capitalize()))
app.config['ENV'] = env
# Initialize SQLAlchemy
db.init_app(app)
# Register asset bundles
assets_env.init_app(app)
assets_loader = PythonLoader(assets)
for name, bundle in assets_loader.load_bundles().iteritems():
assets_env.register(name, bundle)
# Register blueprints
from {{cookiecutter.repo_name}}.modules import public, member
app.register_blueprint(public.blueprint)
app.register_blueprint(member.blueprint)
return app

@ -4,11 +4,12 @@
Entry point for all things, to avoid circular imports.
"""
import os
from .app import app, db, assets_env
from .models import *
from .views import *
from .app import create_app
from .models import User, db
import {{cookiecutter.repo_name}}.modules as modules
if __name__ == '__main__':
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
# Get the environment setting from the system environment variable
env = os.environ.get("{{cookiecutter.repo_name | upper}}_ENV", "prod")
app = create_app("{{cookiecutter.repo_name}}.settings.{env}Config"
.format(env=env.capitalize()), env)

@ -3,7 +3,9 @@
"""
{{cookiecutter.project_name}} models.
"""
from .app import db
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
@ -22,5 +24,3 @@ class User(db.Model):
def __repr__(self):
return '<User "{username}">'.format(username=self.username)
db.create_all()

@ -0,0 +1 @@
'''Blueprint modules for {{cookiecutter.repo_name}}.'''

@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
'''Members-only module, typically including the app itself.
'''
from flask import Blueprint, render_template
from {{cookiecutter.repo_name}}.utils import login_required
blueprint = Blueprint('member', __name__,
static_folder="../static",
template_folder="../templates")
@blueprint.route("/members/")
@login_required
def members():
return render_template("members.html")

@ -1,29 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from flask import render_template, session, request, flash, redirect, url_for
from functools import wraps
from .app import app, db
from .models import User
from .forms import RegisterForm, LoginForm
'''Public section, including homepage and signup.'''
from flask import (Blueprint, request, render_template, flash, url_for,
redirect, session)
from sqlalchemy.exc import IntegrityError
def flash_errors(form):
for field, errors in form.errors.items():
for error in errors:
flash("Error in the {0} field - {1}"
.format(getattr(form, field).label.text, error), 'error')
from {{cookiecutter.repo_name}}.models import User
from {{cookiecutter.repo_name}}.forms import RegisterForm, LoginForm
from {{cookiecutter.repo_name}}.utils import flash_errors
from {{cookiecutter.repo_name}}.models import db
blueprint = Blueprint('public', __name__,
static_folder="../static",
template_folder="../templates")
def login_required(test):
@wraps(test)
def wrap(*args, **kwargs):
if 'logged_in' in session:
return test(*args, **kwargs)
else:
flash('You need to log in first.')
return redirect(url_for('home'))
return wrap
@app.route("/", methods=["GET", "POST"])
@blueprint.route("/", methods=["GET", "POST"])
def home():
form = LoginForm(request.form)
if request.method == 'POST':
@ -31,29 +22,22 @@ def home():
password=request.form['password']).first()
if u is None:
error = 'Invalid username or password.'
flash(error, 'error')
flash(error, 'warning')
else:
session['logged_in'] = True
session['username'] = u.username
flash("You are logged in.", 'success')
return redirect(url_for("members"))
return redirect(url_for("member.members"))
return render_template("home.html", form=form)
@app.route("/members/")
@login_required
def members():
return render_template("members.html")
@app.route('/logout/')
@blueprint.route('/logout/')
def logout():
session.pop('logged_in', None)
session.pop('username', None)
flash('You are logged out.', 'info')
return redirect(url_for('home'))
return redirect(url_for('public.home'))
@app.route("/register/", methods=['GET', 'POST'])
@blueprint.route("/register/", methods=['GET', 'POST'])
def register():
form = RegisterForm(request.form, csrf_enabled=False)
if form.validate_on_submit():
@ -62,21 +46,19 @@ def register():
db.session.add(new_user)
db.session.commit()
flash("Thank you for registering. You can now log in.", 'success')
return redirect(url_for('home'))
return redirect(url_for('public.home'))
except IntegrityError as err:
print(err)
flash("That username and/or email already exists. Try again.", 'error')
flash("That username and/or email already exists. Try again.", 'warning')
else:
flash_errors(form)
return render_template('register.html', form=form)
@app.route("/about/")
@blueprint.route("/about/")
def about():
form = LoginForm(request.form)
return render_template("about.html", form=form)
@app.errorhandler(404)
@blueprint.errorhandler(404)
def page_not_found(e):
return render_template("404.html"), 404

@ -18,6 +18,3 @@ class DevConfig(Config):
DB_PATH = os.path.join(Config.PROJECT_ROOT, DB_NAME)
SQLALCHEMY_DATABASE_URI = "sqlite:///{0}".format(DB_PATH)
SQLALCHEMY_ECHO = True

@ -6,9 +6,9 @@
<ul class="footer-nav">
{% raw %}
<li><a href="{{ url_for('about') }}">About</a></li>
<li><a href="{{ url_for('public.about') }}">About</a></li>
{% endraw %}
<li><a href="mailto:{{ cookiecutter.email }}">Contact</a></li>
</ul>
</small>
</footer>
</footer>

@ -8,7 +8,7 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{{ url_for('home') }}">
<a class="navbar-brand" href="{{ url_for('public.home') }}">
{% endraw %}
{{cookiecutter.project_name}}
{% raw %}
@ -17,17 +17,17 @@
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="{{ url_for('home') }}">Home</a></li>
<li><a href="{{ url_for('about') }}">About</a></li>
<li class="active"><a href="{{ url_for('public.home') }}">Home</a></li>
<li><a href="{{ url_for('public.about') }}">About</a></li>
</ul>
{% if session.logged_in %}
<a class="btn btn-default btn-sm navbar-btn navbar-right" href="{{ url_for('logout') }}">Log out</a>
<a class="btn btn-default btn-sm navbar-btn navbar-right" href="{{ url_for('public.logout') }}">Log out</a>
<ul class="nav navbar-nav navbar-right">
<li><a href="{{ url_for('members') }}">Logged in as {{ session.username }}</a></li>
<li><a href="{{ url_for('member.members') }}">Logged in as {{ session.username }}</a></li>
</ul>
{% elif form %}
<ul class="nav navbar-nav navbar-right">
<li><a href="{{ url_for('register') }}">Create account</a></li>
<li><a href="{{ url_for('public.register') }}">Create account</a></li>
</ul>
<form method="POST" class="navbar-form form-inline navbar-right" action="" role="login">
{{ form.hidden_tag() }}

@ -7,11 +7,12 @@ except ImportError:
import sys
print('nose required. Run "pip install nose".')
from {{cookiecutter.repo_name}}.main import app
from {{cookiecutter.repo_name}}.main import create_app
class Test{{cookiecutter.repo_name | capitalize}}(unittest.TestCase):
def setUp(self):
app = create_app("{{cookiecutter.repo_name}}.settings.DevConfig", 'dev')
app.config['TESTING'] = True
self.app = app.test_client()

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
'''Helper utilities and decorators.'''
from flask import session, flash, redirect, url_for
from functools import wraps
def flash_errors(form):
'''Flash all errors for a form.'''
for field, errors in form.errors.items():
for error in errors:
flash("Error in the {0} field - {1}"
.format(getattr(form, field).label.text, error), 'warning')
def login_required(test):
'''Decorator that makes a view require authentication.'''
@wraps(test)
def wrap(*args, **kwargs):
if 'logged_in' in session:
return test(*args, **kwargs)
else:
flash('You need to log in first.')
return redirect(url_for('home'))
return wrap
Loading…
Cancel
Save