一个简单地flask应用

/flaskr

  /static

  /template

用户可以通过HTTP访问static文件夹中的文件,这里也是css和javascript文件存放位置,flask将会在templates文件夹中寻找Jinja2模板,

要支持SQLite,将下列文件放入schema.sql

drop table if exists entries;

create table entries(

id interger primary key autoincrement,

title string not null,

text string not null);

在flaskr.py中,

import sqlite3

from flask import Flask,request,session,g,redirect,url_for,abort,render_template,flash

#configuration

DATABASE = '/tmp/flaskr.db'

DEBUG = True

SECRET_KEY = 'development key'

USERNAME = 'admin'

PASSWORD = 'default'

创建应用,在同一文件中配置初始化:

app = Flask(__name__)

app.config.from_object(__name__)

from_object()将会寻找给定的对象(如果他是一个字符串),搜寻里面定义的全部大写的变量,

从配置文件加载配置是from_envvar()所做的,用于替换from_object()

app.config.from_envvar('FLASKr_SETTINGS',silent=True)

这种设置方法我们可以设置一个名为FLASKR_SETTINGS环境变量来设定一个配置文件载入之后是否覆盖默认值,静默开关告诉FLask不去关心这个环境变量键值是否存在

secret_key是需要为了保持客户端的会话安全,

添加一个轻松连接到指定数据库的方法,这个方法用于请求时打开一个连接.

def connect_db():

  return sqlite3.connect(app.config['DATABASE'])

如果想要把那个文件当做独立应用来运行,只需要加上:

if  __name__ == '__main__':

app.run()

顺利开始运行这个应用.使用如下命令:

python flaskr.py 

创建数据库

Flaskr是一个使用关系型数据库的应用程序,这样的系统需要一个模式告诉他们如何存储信息,因此在首次启动服务器之前,创建数据库模式,可以通过管道把schema.sql作为sqlite3命令的输入来创建这个模式,命令如下:

sqlite3 / tmp/flaskr.db <schema.sql>

这种方法的缺点是需要安装sqlite3命令,而并不是每个系统都有安装,必须提供数据库的路径,否则将报错,

可以添加一个函数来初始化数据库

首先从contextlib包中导入contextlib.closing()函数,并且在flaskr.py文件中添加如下的内容;

from contextlib import closing

接着可以创建一个称为init_db函数,该函数用来初始化数据库,我们可以使用之前定义的connect_db函数,添加这样的函数:

def init_db():

  with closing(connect_db()) as db:

    with app.open_resource('schema.sql') as f:

      db.cursor().executescript(f.read())

    db.commit()

closing()助手函数允许我们在with块中保持数据库连接可用,应用对象的open_resource()方法在其方框外也支持这个功能,因此可以在with块中直接使用,这个函数从资源位置(你的flaskr文件夹)中打开一个文件,并且允许读取

当我们连接到数据库时会得到一个数据库连接对象,这个对象提供给我们一个数据库指针,

请求数据库连接:

在函数中需要数据库连接,在请求之前初始化他们,请求结束后自动关闭

Flask允许我们使用before_request(),after_request()和teardown_request()装饰器来实现这个功能:

@app.before_request

def before_request():

g.db = connect_db()

@app.teardown_request

def teardown_request(exception):

g.db.close()

使用before_request()装饰器的函数会在请求之前被调用而且不带参数.使用after_request()装饰器的函数会在请求之后被调用且传入将要发给客户端的响应.他们必须返回那个响应对象或是不同的响应对象,但当异常被抛出时,他们不一定会被执行,并不允许修改请求,返回的值会被忽略.如果在请求已经被处理的时候抛出异常,他会被传递到每个函数,否则会传入一个None

我们把当前的数据库连接保存在Flask提供的g特殊对象中,这个对象只能保存一次请求的信息,并且在每个函数里都可用,不要用其他对象来保存信息,因为在多线程环境下将不再可行,特殊的对象g在后台有一些神奇的机制来保证他在做正确的事情.

视图函数

显示条目:

这个视图显示所有存储在数据库的条目,他监听着应用的根地址以及会从数据库中查询标题和内容,id值最大的条目将在前面,从游标返回行是按select语句中声明的列组织的元组,视图函数将会把条目作为字典传入show_entries.html模板及返回渲染结果

@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',enries=entries)

添加新条目

这个视图允许登录的用户添加新的条目,他只回应POST请求,实际的表单是显示在show_entries页面,如果一些工作正常,我们用flash()向下一个请求闪现一条信息并且跳转回show_entries页:

@app.route('/add',methods=['POS'])

def add_entry():

if not session.get('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 successful posted')

return redirect (url_for('show-entries'))

登录和注销

这些函数是用于用户登录以及注销,依据在配置中的值登录时检查用户名和密码并且在会话中设置logged_in键值,如果用户登录成功,logged_in键值被设置成True,并跳转回show_entries页,此外,会有消息闪现来提示用户登入成功,如果发生错误,模板会通知并提示重新登录:

@app.route('/login',methods=['GET','POST'])

def login():

error = None

if request.method == 'POST':

  if request.form['username'] != app.config['USERNAMe']:

    error = 'Invalid username'

  elif request.form['password'] != app.config['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)

另一方面注销函数会从会话中移除了logged_in键值,这里我们使用一个大绝招,如果你使用字典的pop()方法并传入第二个参数(默认),这个方法会从字典中删除这个键,如果这个键不存在则什么都不做,我们不需要检查用户是否登录

@aa.route('/lgout')

def logout():

  session.pop('logged_in',None)

  flash('You were logged out')

  return redirect(url_for('show_entries'))

模板

模板使用Jinja2语言以及默认开启自动转义,意味着除非使用Markup标记或在模板中使用|safe过滤器,否则jinja2会确保特殊字符比如<或>被转义成等价的XML实体,session字典在模板中同样可用的,注意在Jinja中你可以访问不存在的对象/字典属性或成员,及时键不存在仍然可以正常工作

<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 session.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>

show_entries.html

这个模板继承了上面的layout.html模板来显示信息,注意for遍历了所有用render_template()函数传入的信息,同样告诉表单提交到add_entry函数且使用HTTP的POST方法:

{% extends "layout.html" %}
{% block body %} {% if session.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 %}

猜你喜欢

转载自www.cnblogs.com/suncunxu/p/11281817.html