mirror of https://github.com/mitsuhiko/flask.git
Armin Ronacher
15 years ago
7 changed files with 241 additions and 0 deletions
@ -0,0 +1,103 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
""" |
||||||
|
Flaskr |
||||||
|
~~~~~~ |
||||||
|
|
||||||
|
A microblog example application written as Flask tutorial with |
||||||
|
Flask and sqlite3. |
||||||
|
|
||||||
|
:copyright: (c) 2010 by Armin Ronacher. |
||||||
|
:license: BSD, see LICENSE for more details. |
||||||
|
""" |
||||||
|
from __future__ import with_statement |
||||||
|
import time |
||||||
|
import sqlite3 |
||||||
|
from contextlib import closing |
||||||
|
from flask import Flask, request, session, g, redirect, url_for, abort, \ |
||||||
|
render_template, flash |
||||||
|
from werkzeug import secure_filename |
||||||
|
|
||||||
|
# configuration |
||||||
|
DATABASE = '/tmp/flaskr.db' |
||||||
|
DEBUG = True |
||||||
|
SECRET_KEY = 'development key' |
||||||
|
USERNAME = 'admin' |
||||||
|
PASSWORD = 'default' |
||||||
|
|
||||||
|
# create our little application :) |
||||||
|
app = Flask(__name__) |
||||||
|
app.secret_key = SECRET_KEY |
||||||
|
app.debug = DEBUG |
||||||
|
|
||||||
|
|
||||||
|
def connect_db(): |
||||||
|
"""Returns a new connection to the database.""" |
||||||
|
return sqlite3.connect(DATABASE) |
||||||
|
|
||||||
|
|
||||||
|
def init_db(): |
||||||
|
"""Creates the database tables.""" |
||||||
|
with closing(connect_db()) as db: |
||||||
|
with app.open_resource('schema.sql') as f: |
||||||
|
db.cursor().executescript(f.read()) |
||||||
|
db.commit() |
||||||
|
|
||||||
|
|
||||||
|
@app.request_init |
||||||
|
def before_request(): |
||||||
|
"""Make sure we are connected to the database each request. Also |
||||||
|
set `g.logged_in` to `True` if we are logged in. |
||||||
|
""" |
||||||
|
g.db = connect_db() |
||||||
|
g.logged_in = session.get('logged_in', False) |
||||||
|
|
||||||
|
|
||||||
|
@app.request_shutdown |
||||||
|
def after_request(response): |
||||||
|
"""Closes the database again at the end of the request.""" |
||||||
|
g.db.close() |
||||||
|
return response |
||||||
|
|
||||||
|
|
||||||
|
@app.route('/') |
||||||
|
def show_entries(): |
||||||
|
cur = g.db.execute('select title, text from entries order by id desc') |
||||||
|
entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()] |
||||||
|
return render_template('show_entries.html', entries=entries) |
||||||
|
|
||||||
|
|
||||||
|
@app.route('/add', methods=['POST']) |
||||||
|
def add_entry(): |
||||||
|
if not g.logged_in: |
||||||
|
abort(401) |
||||||
|
g.db.execute('insert into entries (title, text) values (?, ?)', |
||||||
|
[request.form['title'], request.form['text']]) |
||||||
|
g.db.commit() |
||||||
|
flash('New entry was successfully posted') |
||||||
|
return redirect(url_for('show_entries')) |
||||||
|
|
||||||
|
|
||||||
|
@app.route('/login', methods=['GET', 'POST']) |
||||||
|
def login(): |
||||||
|
error = None |
||||||
|
if request.method == 'POST': |
||||||
|
if request.form['username'] != USERNAME: |
||||||
|
error = 'Invalid username' |
||||||
|
elif request.form['password'] != PASSWORD: |
||||||
|
error = 'Invalid password' |
||||||
|
else: |
||||||
|
session['logged_in'] = True |
||||||
|
flash('You were logged in') |
||||||
|
return redirect(url_for('show_entries')) |
||||||
|
return render_template('login.html', error=error) |
||||||
|
|
||||||
|
|
||||||
|
@app.route('/logout') |
||||||
|
def logout(): |
||||||
|
session.pop('logged_in', None) |
||||||
|
flash('You were logged out') |
||||||
|
return redirect(url_for('show_entries')) |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
app.run() |
@ -0,0 +1,64 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
""" |
||||||
|
Flaskr Tests |
||||||
|
~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Tests the Flaskr application. |
||||||
|
|
||||||
|
:copyright: (c) 2010 by Armin Ronacher. |
||||||
|
:license: BSD, see LICENSE for more details. |
||||||
|
""" |
||||||
|
import flaskr |
||||||
|
import unittest |
||||||
|
import tempfile |
||||||
|
|
||||||
|
|
||||||
|
class FlaskrTestCase(unittest.TestCase): |
||||||
|
|
||||||
|
def setUp(self): |
||||||
|
"""Before each test, set up a blank database""" |
||||||
|
self.db = tempfile.NamedTemporaryFile() |
||||||
|
self.app = flaskr.app.test_client() |
||||||
|
flaskr.DATABASE = self.db.name |
||||||
|
flaskr.init_db() |
||||||
|
|
||||||
|
def login(self, username, password): |
||||||
|
return self.app.post('/login', data=dict( |
||||||
|
username=username, |
||||||
|
password=password |
||||||
|
), follow_redirects=True) |
||||||
|
|
||||||
|
def logout(self): |
||||||
|
return self.app.get('/logout', follow_redirects=True) |
||||||
|
|
||||||
|
# testing functions |
||||||
|
|
||||||
|
def test_login_logout(self): |
||||||
|
"""Make sure login and logout works""" |
||||||
|
rv = self.login(flaskr.USERNAME, flaskr.PASSWORD) |
||||||
|
assert 'You were logged in' in rv.data |
||||||
|
rv = self.logout() |
||||||
|
assert 'You were logged out' in rv.data |
||||||
|
rv = self.login(flaskr.USERNAME + 'x', flaskr.PASSWORD) |
||||||
|
assert 'Invalid username' in rv.data |
||||||
|
rv = self.login(flaskr.USERNAME, flaskr.PASSWORD + 'x') |
||||||
|
assert 'Invalid password' in rv.data |
||||||
|
|
||||||
|
def test_messages(self): |
||||||
|
"""Test that messages work""" |
||||||
|
# start with a blank state |
||||||
|
rv = self.app.get('/') |
||||||
|
assert 'No entries here so far' in rv.data |
||||||
|
self.login(flaskr.USERNAME, flaskr.PASSWORD) |
||||||
|
rv = self.app.post('/add', data=dict( |
||||||
|
title='<Hello>', |
||||||
|
text='<strong>HTML</strong> allowed here' |
||||||
|
), follow_redirects=True) |
||||||
|
assert 'No entries here so far' not in rv.data |
||||||
|
self.login(flaskr.USERNAME, flaskr.PASSWORD) |
||||||
|
assert '<Hello>' in rv.data |
||||||
|
assert '<strong>HTML</strong> allowed here' in rv.data |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
unittest.main() |
@ -0,0 +1,6 @@ |
|||||||
|
drop table if exists entries; |
||||||
|
create table entries ( |
||||||
|
id integer primary key autoincrement, |
||||||
|
title string not null, |
||||||
|
text string not null |
||||||
|
); |
@ -0,0 +1,16 @@ |
|||||||
|
body { font-family: sans-serif; background: #eee; } |
||||||
|
a, h1, h2 { color: #377BA8; } |
||||||
|
h1, h2 { font-family: 'Georgia', serif; margin: 0; } |
||||||
|
h1 { border-bottom: 2px solid #eee; } |
||||||
|
h2 { font-size: 1.2em; } |
||||||
|
div.metanav { text-align: right; font-size: 0.8em; background: #fafafa; |
||||||
|
padding: 0.3em; margin-bottom: 1em; } |
||||||
|
ul.entries { list-style: none; margin: 0; padding: 0; } |
||||||
|
ul.entries li { margin: 0.8em 1.2em; } |
||||||
|
ul.entries li h2 { margin-left: -1em; } |
||||||
|
div.page { margin: 2em auto; width: 35em; border: 5px solid #ccc; |
||||||
|
padding: 0.8em; background: white; } |
||||||
|
form.add-entry { font-size: 0.9em; border-bottom: 1px solid #ccc; } |
||||||
|
form.add-entry dl { font-weight: bold; } |
||||||
|
div.flash { background: #CEE5F5; padding: 0.5em; border: 1px solid #AACBE2; } |
||||||
|
p.error { background: #F0D6D6; padding: 0.5em; } |
@ -0,0 +1,17 @@ |
|||||||
|
<!doctype html> |
||||||
|
<title>Flaskr</title> |
||||||
|
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}"> |
||||||
|
<div class=page> |
||||||
|
<h1>Flaskr</h1> |
||||||
|
<div class=metanav> |
||||||
|
{% if not g.logged_in %} |
||||||
|
<a href="{{ url_for('login') }}">log in</a> |
||||||
|
{% else %} |
||||||
|
<a href="{{ url_for('logout') }}">log out</a> |
||||||
|
{% endif %} |
||||||
|
</div> |
||||||
|
{% for message in get_flashed_messages() %} |
||||||
|
<div class=flash>{{ message }}</div> |
||||||
|
{% endfor %} |
||||||
|
{% block body %}{% endblock %} |
||||||
|
</div> |
@ -0,0 +1,14 @@ |
|||||||
|
{% extends "layout.html" %} |
||||||
|
{% block body %} |
||||||
|
<h2>Login</h2> |
||||||
|
{% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %} |
||||||
|
<form action="{{ url_for('login') }}" method=post> |
||||||
|
<dl> |
||||||
|
<dt>Username: |
||||||
|
<dd><input type=text name=username> |
||||||
|
<dt>Password: |
||||||
|
<dd><input type=passowrd name=password> |
||||||
|
<dd><input type=submit value=Login> |
||||||
|
</dl> |
||||||
|
</form> |
||||||
|
{% endblock %} |
@ -0,0 +1,21 @@ |
|||||||
|
{% extends "layout.html" %} |
||||||
|
{% block body %} |
||||||
|
{% if g.logged_in %} |
||||||
|
<form action="{{ url_for('add_entry') }}" method=post class=add-entry> |
||||||
|
<dl> |
||||||
|
<dt>Title: |
||||||
|
<dd><input type=text size=30 name=title> |
||||||
|
<dt>Text: |
||||||
|
<dd><textarea name=text rows=5 cols=40></textarea> |
||||||
|
<dd><input type=submit value=Share> |
||||||
|
</dl> |
||||||
|
</form> |
||||||
|
{% endif %} |
||||||
|
<ul class=entries> |
||||||
|
{% for entry in entries %} |
||||||
|
<li><h2>{{ entry.title }}</h2>{{ entry.text|safe }} |
||||||
|
{% else %} |
||||||
|
<li><em>Unbelievable. No entries here so far</em> |
||||||
|
{% endfor %} |
||||||
|
</ul> |
||||||
|
{% endblock %} |
Loading…
Reference in new issue