最近在实验楼学习的python,笔记实际上都是上面的教程练习加上自己的理解。
在上次学习flask的基础上,这次用flask写了一个小博客,数据库是SQLite 3, 因为它足够应付这种规模的应用。对更大的应用使用 SQLAlchemy 比较好。
实现登录验证及博文展示(使用SQLite3)
这次使用的是PyChrm,不然每次都要在Shell中敲命令行(关键是不同应用切来切去太麻烦了)。
使用PyChrm创建project时有flask 的选项,创建好后自动创建好了static,templates文件夹以及.py文件,我的是这样的:
Web小应用的功能大概是这样:
- 创建数据库以及表。
- 连接数据库进入应用。
- 登录成功后可以直接写博文,保存在数据库中并显示到当前页面。
首先在project根目录下新建sql脚本文件
/schema.sql
drop table if exists entries;
create table entries
(
id integer primary key autoincrement,
title string not null,
text string not null
);
这个脚本将在python中调用运行,新建一个表entries,包括博文id(主键,以1为基数自增),标题title,正文text。
开始flaskr.py
/flask.py
#!/usr/bin/python
# -*- coding: utf8 -*-
from __future__ import with_statement
from contextlib import closing
# 导入所有的模块
import sqlite3
import sys
from flask import Flask, request, session, g, redirect, url_for, abort, render_template, flash
reload(sys)
# 当网页中有中文时,就需要进行utf8编码,以便网页内容能够被浏览器解码正常显示
sys.setdefaultencoding('utf8')
# 配置文件
# SECRET_KEY是需要为了保持客户端的会话安全。最好是尽可能复杂。
# 也可使用密码随机生成器
# >>> import os
# >>> os.urandom(24)
DATABASE = '/tmp/flaskr.db'
DEBUG = True
SECRET_KEY = 'i like python'
USERNAME = 'admin'
PASSWORD = 'default'
# 创建应用
app = Flask(__name__)
# from_object()将会寻找给定的对象(如果它是一个字符串,则会导入它),搜寻里面定义的全部大写的变量。
# 在我们的这种情况中,配置文件就是我们上面写的几行代码。你也可以将他们分别存储到多个文件。
# 从配置文件中加载配置,用from_envvar(),替换from_object()
# app.config.from_envvar('FLASKR_SETTINGS', silent=True)
# 这种方法我们可以设置一个名为FLASKR_SETTINGS的环境变量来设定一个配置文件载入后是否覆盖默认值。
#静默开关silent=True告诉 Flask 不去关心这个环境变量键值是否存在。
app.config.from_object(__name__)
# 连接到指定数据库
def connect_db():
return sqlite3.connect(app.config['DATABASE'])
# 初始化数据库
# 在应用运行之前需要在python shell中调用该函数创建数据库
# >>> from flaskr import init_db
# >>> init_db()
def init_db():
# closing()函数允许我们在with块中保持数据库连接可用
# 数据库连接对象‘db’提供一个数据库指针
with closing(connect_db()) as db:
with app.open_resource('schema.sql') as f:
# db.cursor()是获得python执行sql命令的方法,也就是操作游标
# executescript函数一次可以执行多条sql
# db.cursor().executescript(f.read())为:python读取并执行全部SQL语句
db.cursor().executescript(f.read())
# commit()为提交修改
db.commit()
# 使用before_request()装饰器的函数会在数据库连接请求之前被调用而且不带参数
# 使用after_request()装饰器的函数会在请求之后被调用且传入将要发给客户端的响应。
# 使用teardown_request()装饰器。它装饰的函数将在响应构造后执行,并不允许修改请求,返回的值会被忽略。
# 如果在请求已经被处理的时候抛出异常,它会被传递到每个函数,否则会传入一个None。
# 我们把当前的数据库连接保存在 Flask 提供的 g 特殊对象中。这个对象只能保存一次请求的信息,
# 并且在每个函数里都可用。不要用其它对象来保存信息,因为在多线程环境下将不可行。
# 特殊的对象 g 在后台有一些神奇的机制来保证它在做正确的事情。
@app.before_request
def before_request():
g.db = connect_db()
@app.teardown_request
def teardown_request(exception):
g.db.close()
@app.route('/')
def show_entries():
# 按照id列倒序形式取出数据库中的数据存入cur
cur = g.db.execute('select title, text from entries order by id desc')
# fetchall()方法返回查询到的所有记录
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 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()方法来闪现一个消息,使用get_flashed_messages()能够获取消息,get_flashed_messages()也能用于模版中。
# flash()闪现的消息在layout.html中通过get_flashed_message()中显示出来。
flash('New entry was succesfully 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'] != 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)
@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()
模板和静态文件
/templates/layout.html
<!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 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>
/templates/login.html
{% 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 class="myForm1">
<dl>
<dt>Username/账户:
<dd><input type=text name=username>
<dt>Password/密码:
<dd><input type=password name=password>
<dd><input type=submit value=Login/登录>
</dl>
</form>
{% endblock %}
/templates/show_entries.html
{% 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>
<ul class=entries>
{% if not entries %}
<li><em>Unbelievable.No entries here so far/您当前<strong>没有博客</strong>哟~</em>
{% else %}
{% for entry in entries %}
<!--在模板中使用|safe过滤器, 否则 Jinja2 会确保特殊字符比如<或>被转义成等价的XML实体。-->
<li><h2>{{ entry.title }}</h2>{{ entry.text|safe }}
{% endfor %}
{% endif %}
{% else %}
<li><em>Unbelievable.No Users so far/您当前<strong>没有登录</strong>哟~</em>
{% endif %}
</ul>
{% endblock %}
/static/style.css
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; }
.myForm1 input[type=submit]
{
background-color: green;
border-radius: 10px;
border: none;
margin-top: 10px;
padding: 5px 10px 5px 10px;
color: white;
}
.page { margin: 2em auto; width: 35em; border: 5px solid #ccc;
padding: 0.8em; background: white; }
.entries { list-style: none; margin: 0; padding: 0; }
.entries li { margin: 0.8em 1.2em; }
.entries li h2 { margin-left: -1em; }
.add-entry { font-size: 0.9em; border-bottom: 1px solid #ccc; }
.add-entry dl { font-weight: bold; }
.add-entry input[type=submit]
{
background-color: green;
border-radius: 10px;
border: none;
margin-top: 10px;
padding: 5px 10px 5px 10px;
color: white;
}
.metanav { text-align: right; font-size: 0.8em; padding: 0.3em;
margin-bottom: 1em; background: #fafafa; }
.flash { background: #CEE5F5; padding: 0.5em;
border: 1px solid #AACBE2; }
.error { background: #F0D6D6; padding: 0.5em; }
在Python 交互中调用init_db()函数创建数据库后运行Web应用
点击log in
正确输入登录信息,登录成功,初次运行没有文章
键入文章,点击share
注销登录
flask的学习暂时就到这里,个人感觉用flask框架写Web应用很轻便,扩充空间大,非常适合新手进阶。
ps:在flask学习过程中发现有些时候project处于有中文的目录下时,css样式表就无法发挥作用,这里建议目录最好不要有中文。