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