Browse Source

Merge pull request #2228 from wgwz/improve-flaskr

Add blueprint and the app factory pattern into the flaskr example
pull/2338/head
Kenneth Reitz 8 years ago committed by GitHub
parent
commit
a63a4cb034
  1. 8
      examples/flaskr/README
  2. 1
      examples/flaskr/flaskr/__init__.py
  3. 15
      examples/flaskr/flaskr/_cliapp.py
  4. 0
      examples/flaskr/flaskr/blueprints/__init__.py
  5. 55
      examples/flaskr/flaskr/blueprints/flaskr.py
  6. 64
      examples/flaskr/flaskr/factory.py
  7. 4
      examples/flaskr/flaskr/templates/layout.html
  8. 2
      examples/flaskr/flaskr/templates/login.html
  9. 2
      examples/flaskr/flaskr/templates/show_entries.html
  10. 4
      examples/flaskr/setup.cfg
  11. 15
      examples/flaskr/setup.py
  12. 59
      examples/flaskr/tests/test_flaskr.py

8
examples/flaskr/README

@ -9,9 +9,11 @@
~ How do I use it? ~ How do I use it?
1. edit the configuration in the flaskr.py file or 1. edit the configuration in the factory.py file or
export an FLASKR_SETTINGS environment variable export an FLASKR_SETTINGS environment variable
pointing to a configuration file. pointing to a configuration file or pass in a
dictionary with config values using the create_app
function.
2. install the app from the root of the project directory 2. install the app from the root of the project directory
@ -19,7 +21,7 @@
3. Instruct flask to use the right application 3. Instruct flask to use the right application
export FLASK_APP=flaskr export FLASK_APP=flaskr._cliapp
4. initialize the database with this command: 4. initialize the database with this command:

1
examples/flaskr/flaskr/__init__.py

@ -1 +0,0 @@
from .flaskr import app

15
examples/flaskr/flaskr/_cliapp.py

@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
"""
Flaskr
~~~~~~
A microblog example application written as Flask tutorial with
Flask and sqlite3.
:copyright: (c) 2015 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
from flaskr.factory import create_app
app = create_app()

0
examples/flaskr/flaskr/blueprints/__init__.py

55
examples/flaskr/flaskr/flaskr.py → examples/flaskr/flaskr/blueprints/flaskr.py

@ -10,29 +10,18 @@
:license: BSD, see LICENSE for more details. :license: BSD, see LICENSE for more details.
""" """
import os
from sqlite3 import dbapi2 as sqlite3 from sqlite3 import dbapi2 as sqlite3
from flask import Flask, request, session, g, redirect, url_for, abort, \ from flask import Blueprint, request, session, g, redirect, url_for, abort, \
render_template, flash render_template, flash, current_app
# create our little application :) # create our blueprint :)
app = Flask(__name__) bp = Blueprint('flaskr', __name__)
# Load default config and override config from an environment variable
app.config.update(dict(
DATABASE=os.path.join(app.root_path, 'flaskr.db'),
DEBUG=True,
SECRET_KEY='development key',
USERNAME='admin',
PASSWORD='default'
))
app.config.from_envvar('FLASKR_SETTINGS', silent=True)
def connect_db(): def connect_db():
"""Connects to the specific database.""" """Connects to the specific database."""
rv = sqlite3.connect(app.config['DATABASE']) rv = sqlite3.connect(current_app.config['DATABASE'])
rv.row_factory = sqlite3.Row rv.row_factory = sqlite3.Row
return rv return rv
@ -40,18 +29,11 @@ def connect_db():
def init_db(): def init_db():
"""Initializes the database.""" """Initializes the database."""
db = get_db() db = get_db()
with app.open_resource('schema.sql', mode='r') as f: with current_app.open_resource('schema.sql', mode='r') as f:
db.cursor().executescript(f.read()) db.cursor().executescript(f.read())
db.commit() db.commit()
@app.cli.command('initdb')
def initdb_command():
"""Creates the database tables."""
init_db()
print('Initialized the database.')
def get_db(): def get_db():
"""Opens a new database connection if there is none yet for the """Opens a new database connection if there is none yet for the
current application context. current application context.
@ -61,14 +43,7 @@ def get_db():
return g.sqlite_db return g.sqlite_db
@app.teardown_appcontext @bp.route('/')
def close_db(error):
"""Closes the database again at the end of the request."""
if hasattr(g, 'sqlite_db'):
g.sqlite_db.close()
@app.route('/')
def show_entries(): def show_entries():
db = get_db() db = get_db()
cur = db.execute('select title, text from entries order by id desc') cur = db.execute('select title, text from entries order by id desc')
@ -76,7 +51,7 @@ def show_entries():
return render_template('show_entries.html', entries=entries) return render_template('show_entries.html', entries=entries)
@app.route('/add', methods=['POST']) @bp.route('/add', methods=['POST'])
def add_entry(): def add_entry():
if not session.get('logged_in'): if not session.get('logged_in'):
abort(401) abort(401)
@ -85,26 +60,26 @@ def add_entry():
[request.form['title'], request.form['text']]) [request.form['title'], request.form['text']])
db.commit() db.commit()
flash('New entry was successfully posted') flash('New entry was successfully posted')
return redirect(url_for('show_entries')) return redirect(url_for('flaskr.show_entries'))
@app.route('/login', methods=['GET', 'POST']) @bp.route('/login', methods=['GET', 'POST'])
def login(): def login():
error = None error = None
if request.method == 'POST': if request.method == 'POST':
if request.form['username'] != app.config['USERNAME']: if request.form['username'] != current_app.config['USERNAME']:
error = 'Invalid username' error = 'Invalid username'
elif request.form['password'] != app.config['PASSWORD']: elif request.form['password'] != current_app.config['PASSWORD']:
error = 'Invalid password' error = 'Invalid password'
else: else:
session['logged_in'] = True session['logged_in'] = True
flash('You were logged in') flash('You were logged in')
return redirect(url_for('show_entries')) return redirect(url_for('flaskr.show_entries'))
return render_template('login.html', error=error) return render_template('login.html', error=error)
@app.route('/logout') @bp.route('/logout')
def logout(): def logout():
session.pop('logged_in', None) session.pop('logged_in', None)
flash('You were logged out') flash('You were logged out')
return redirect(url_for('show_entries')) return redirect(url_for('flaskr.show_entries'))

64
examples/flaskr/flaskr/factory.py

@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
"""
Flaskr
~~~~~~
A microblog example application written as Flask tutorial with
Flask and sqlite3.
:copyright: (c) 2015 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
import os
from flask import Flask, g
from werkzeug.utils import find_modules, import_string
from flaskr.blueprints.flaskr import init_db
def create_app(config=None):
app = Flask(__name__)
app.config.update(dict(
DATABASE=os.path.join(app.root_path, 'flaskr.db'),
DEBUG=True,
SECRET_KEY='development key',
USERNAME='admin',
PASSWORD='default'
))
app.config.update(config or {})
app.config.from_envvar('FLASKR_SETTINGS', silent=True)
register_blueprints(app)
register_cli(app)
register_teardowns(app)
return app
def register_blueprints(app):
"""Register all blueprint modules
Reference: Armin Ronacher, "Flask for Fun and for Profit" PyBay 2016.
"""
for name in find_modules('flaskr.blueprints'):
mod = import_string(name)
if hasattr(mod, 'bp'):
app.register_blueprint(mod.bp)
return None
def register_cli(app):
@app.cli.command('initdb')
def initdb_command():
"""Creates the database tables."""
init_db()
print('Initialized the database.')
def register_teardowns(app):
@app.teardown_appcontext
def close_db(error):
"""Closes the database again at the end of the request."""
if hasattr(g, 'sqlite_db'):
g.sqlite_db.close()

4
examples/flaskr/flaskr/templates/layout.html

@ -5,9 +5,9 @@
<h1>Flaskr</h1> <h1>Flaskr</h1>
<div class="metanav"> <div class="metanav">
{% if not session.logged_in %} {% if not session.logged_in %}
<a href="{{ url_for('login') }}">log in</a> <a href="{{ url_for('flaskr.login') }}">log in</a>
{% else %} {% else %}
<a href="{{ url_for('logout') }}">log out</a> <a href="{{ url_for('flaskr.logout') }}">log out</a>
{% endif %} {% endif %}
</div> </div>
{% for message in get_flashed_messages() %} {% for message in get_flashed_messages() %}

2
examples/flaskr/flaskr/templates/login.html

@ -2,7 +2,7 @@
{% block body %} {% block body %}
<h2>Login</h2> <h2>Login</h2>
{% if error %}<p class="error"><strong>Error:</strong> {{ error }}{% endif %} {% if error %}<p class="error"><strong>Error:</strong> {{ error }}{% endif %}
<form action="{{ url_for('login') }}" method="post"> <form action="{{ url_for('flaskr.login') }}" method="post">
<dl> <dl>
<dt>Username: <dt>Username:
<dd><input type="text" name="username"> <dd><input type="text" name="username">

2
examples/flaskr/flaskr/templates/show_entries.html

@ -1,7 +1,7 @@
{% extends "layout.html" %} {% extends "layout.html" %}
{% block body %} {% block body %}
{% if session.logged_in %} {% if session.logged_in %}
<form action="{{ url_for('add_entry') }}" method="post" class="add-entry"> <form action="{{ url_for('flaskr.add_entry') }}" method="post" class="add-entry">
<dl> <dl>
<dt>Title: <dt>Title:
<dd><input type="text" size="30" name="title"> <dd><input type="text" size="30" name="title">

4
examples/flaskr/setup.cfg

@ -1,2 +1,2 @@
[tool:pytest] [aliases]
test=pytest test=pytest

15
examples/flaskr/setup.py

@ -1,8 +1,19 @@
from setuptools import setup # -*- coding: utf-8 -*-
"""
Flaskr Tests
~~~~~~~~~~~~
Tests the Flaskr application.
:copyright: (c) 2015 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
from setuptools import setup, find_packages
setup( setup(
name='flaskr', name='flaskr',
packages=['flaskr'], packages=find_packages(),
include_package_data=True, include_package_data=True,
install_requires=[ install_requires=[
'flask', 'flask',

59
examples/flaskr/tests/test_flaskr.py

@ -12,19 +12,38 @@
import os import os
import tempfile import tempfile
import pytest import pytest
from flaskr import flaskr from flaskr.factory import create_app
from flaskr.blueprints.flaskr import init_db
@pytest.fixture @pytest.fixture
def client(): def app(request):
db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
flaskr.app.config['TESTING'] = True db_fd, temp_db_location = tempfile.mkstemp()
client = flaskr.app.test_client() config = {
with flaskr.app.app_context(): 'DATABASE': temp_db_location,
flaskr.init_db() 'TESTING': True,
yield client 'DB_FD': db_fd
os.close(db_fd) }
os.unlink(flaskr.app.config['DATABASE'])
app = create_app(config=config)
with app.app_context():
init_db()
yield app
@pytest.fixture
def client(request, app):
client = app.test_client()
def teardown():
os.close(app.config['DB_FD'])
os.unlink(app.config['DATABASE'])
request.addfinalizer(teardown)
return client
def login(client, username, password): def login(client, username, password):
@ -44,25 +63,25 @@ def test_empty_db(client):
assert b'No entries here so far' in rv.data assert b'No entries here so far' in rv.data
def test_login_logout(client): def test_login_logout(client, app):
"""Make sure login and logout works""" """Make sure login and logout works"""
rv = login(client, flaskr.app.config['USERNAME'], rv = login(client, app.config['USERNAME'],
flaskr.app.config['PASSWORD']) app.config['PASSWORD'])
assert b'You were logged in' in rv.data assert b'You were logged in' in rv.data
rv = logout(client) rv = logout(client)
assert b'You were logged out' in rv.data assert b'You were logged out' in rv.data
rv = login(client, flaskr.app.config['USERNAME'] + 'x', rv = login(client,app.config['USERNAME'] + 'x',
flaskr.app.config['PASSWORD']) app.config['PASSWORD'])
assert b'Invalid username' in rv.data assert b'Invalid username' in rv.data
rv = login(client, flaskr.app.config['USERNAME'], rv = login(client, app.config['USERNAME'],
flaskr.app.config['PASSWORD'] + 'x') app.config['PASSWORD'] + 'x')
assert b'Invalid password' in rv.data assert b'Invalid password' in rv.data
def test_messages(client): def test_messages(client, app):
"""Test that messages work""" """Test that messages work"""
login(client, flaskr.app.config['USERNAME'], login(client, app.config['USERNAME'],
flaskr.app.config['PASSWORD']) app.config['PASSWORD'])
rv = client.post('/add', data=dict( rv = client.post('/add', data=dict(
title='<Hello>', title='<Hello>',
text='<strong>HTML</strong> allowed here' text='<strong>HTML</strong> allowed here'

Loading…
Cancel
Save