mkdir LaiKe
cd LaiKe/
# server(server端目录)
mkdir -p project/server
touch project/server/models.py
touch project/server/settings.py
# client(client端目录)
mkdir -p project/client
mkdir -p project/client/static
mkdir -p project/client/templates
t
##### main 模块 ########
# server -> main
mkdir -p project/server/main
touch project/server/main/__init__.py
touch project/server/main/views.py
# client > main
mkdir -p project/client/templates/main
touch -p project/client/templastes/main/home.html
####### User 模块 #######
# server -> user
mkdir -p project/server/user
mkdir -p project/server/user/__init__.py
mkdir -p project/server/user/forms.py
mkdir -p project/server/user/views.py
# client -> user
mkdir -p project/client/templates/user
touch -p project/client/templastes/user/login.html
touch -p project/client/templastes/user/register.html
touch -p project/client/templastes/user/members.html
######## entry模块 ##########
#server -> entry (词条)
mkdir -p project/server/entry/
touch project/server/entry/__init__.py
touch project/server/entry/forms.py
touch project/server/entry/views.py
# client -> entry
mkdir -p project/client/templates/entry
touch -p project/client/templastes/entry/login.html
touch -p project/client/templastes/entry/register.html
touch -p project/client/templastes/entry/members.html
app的创建:初始化的时,可以通过template_folder指定模板位置,通过static_folder指定静态资源位置
蓝图的构建:通过Blueprint为每个模块定义一个蓝图对象,然后通过app.register_blueprint将各个模块通过蓝图对象集成到app中;
插件的应用:一个插件通常就是一个类,插件其实就是对flask创建的app的包装,即把app当做构造方法的参数。例如:
toolbar = DebugToolbarExtension(app)
bootstrap = Bootstrap(app)
login_manager = LoginManager(app)
db = SQLAlchemy(app) # ORM插件
Model的设计:ORM
Views的设计:Flask中的Views包含路由和视图函数。设计风格:RESTful 风格、非RESTful风格、前后端分离接口风格、模板风格。
import datetime
from project.server import db, bcrypt, app
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
email = db.Column(db.String(255), unique=True, nullable=False)
password = db.Column(db.String(255), nullable=False)
registered_on = db.Column(db.DateTime, nullable=False)
admin = db.Column(db.Boolean, nullable=False, default=False)
entries = db.relationship('Entry', backref='creator')
def __init__(self, email, password, admin=False):
self.email = email
self.password = bcrypt.generate_password_hash(
password, app.config.get('BCRYPT_LOG_ROUNDS')
)
self.registered_on = datetime.datetime.now()
self.admin = admin
def is_authenticated(self):
return True
def is_active(self):
return True
def get_id(self):
return self.id
def __repr__(self):
return '' .format(self.email)
class Entry(db.Model):
__tablename__ = 'entry'
id = db.Column(db.Integer, primary_key=True, autoincrement=True,nullable=True)
title = db.Column(db.String(100), unique=True, nullable=False)
content = db.Column(db.String(500), nullable=False)
author_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
Views中同时包含路由和负责处理请求的视图函数。每个模块都仅有一个views.py,用来定义该模块涉及的所有用来处理请求的视图函数,每个视图函数都和一个url绑定。
传统风格:
# project/server/entry/views.py
from flask import render_template, Blueprint
entry_blueprint = Blueprint('entry', __name__, url_prefix='/entry')
@entry_blueprint.route('/list/')
def entries():
return render_template('entry/entries.html')
@entry_blueprint.route('/add/')
def add_entry():
return render_template('entry/add.html')
@entry_blueprint.route('/delete//')
def delete_entry(entry_id):
return render_template('entry/delete.html')
@entry_blueprint.route('/modify//')
def modify(entry_id):
return render_template('entry/modify.html')
RESTful风格:
entry_blueprint = Blueprint('entry', __name__)
# 1. 对所有资源的操作
@entry_blueprint.route('/entries/',methods=['GET','POST'])
def entries():
pass
# 2. 对某个资源的操作
@entry_blueprint.route('/entry//',methods=['GET','PUT','DELETE'])
def entry():
pass
模板的设计综合使用继承和组合的思想,最大限度地减少重复代码。
继承:{% extends ‘_base.html’ %}
组装:{% include ‘header.html’ %}、{% include ‘footer.html’ %}
Form类:把对form表单的操作对象化
ORM类:把对关系表的操作对象化
HTTP 方法(也经常被叫做“谓词”)告知服务器,客户端想对请求的页面 做 些什么。下面的都是非常常见的方法:
GET
浏览器告知服务器:只 获取 页面上的信息并发给我。这是最常用的方法。
HEAD
浏览器告诉服务器:欲获取信息,但是只关心 消息头 。应用应像处理 GET 请求一样来处理它,但是不分发实际内容。在 Flask 中你完全无需 人工 干预,底层的 Werkzeug 库已经替你打点好了。
POST
浏览器告诉服务器:想在 URL 上 发布 新信息。并且,服务器必须确保 数据已存储且仅存储一次。这是 HTML 表单通常发送数据到服务器的方法。
PUT
类似 POST 但是服务器可能触发了存储过程多次,多次覆盖掉旧值。你可 能会问这有什么用,当然这是有原因的。考虑到传输中连接可能会丢失,在 这种 情况下浏览器和服务器之间的系统可能安全地第二次接收请求,而 不破坏其它东西。因为 POST它只触发一次,所以用 POST 是不可能的。
DELETE
删除给定位置的信息。
OPTIONS
给客户端提供一个敏捷的途径来弄清这个 URL 支持哪些 HTTP 方法。 从 Flask 0.6 开始,实现了自动处理。
有趣的是,在 HTML4 和 XHTML1 中,表单只能以 GET 和 POST 方法提交到服务器。但是 JavaScript 和未来的 HTML 标准允许你使用其它所有的方法。此外,HTTP 最近变得相当流行,浏览器不再是唯一的 HTTP 客户端。
https://flask-login.readthedocs.io/en/latest
Flask-Login为Flask提供了会话管理,如:登录、退出登录、7天内自动登录等。
Flask-Login provides user session management for Flask. It handles the common tasks of logging in, logging out, and remembering your users’ sessions over extended periods of time.
主要功能:
Store the active user’s ID in the session, and let you log them in and out easily.
把 用户ID保存在session中,使登录和退出登录非常简单。(通过从session中保存用户ID或删除用户ID达到登录和退出登录的效果)
Let you restrict views to logged-in (or logged-out) users.
为views的执行添加限制,eg:通过login_required装饰器来限制某个视图函数必须是登录状态下才能访问。
Handle the normally-tricky “remember me” functionality.
提供【记住我】功能。
Help protect your users’ sessions from being stolen by cookie thieves.
保护用户会话信息,防止被盗。
Possibly integrate with Flask-Principal or other authorization extensions later on.
将来可能会和Flask-Princial等认证插件集成。
pip install flask-login
login_manager = LoginManager()
login_manager.init_app(app)
@login_manager.user_loader
def load_user(user_id):
"""
user_id如果不正确,该user_id将从session移除,return None
"""
return User.get(user_id)
用户模型必须实现下面的属性和方法:
The class that you use to represent users needs to implement these properties and methods:
is_authenticated
功能:判断用户是否已经验证通过,如果是认证通过的,is_authenticated的返回值是True。
This property should return True
if the user is authenticated, i.e. they have provided valid credentials. (Only authenticated users will fulfill the criteria of login_required
.)
is_active
如果是一个活跃的user,将返回True。
This property should return True
if this is an active user - in addition to being authenticated, they also have activated their account, not been suspended, or any condition your application has for rejecting an account. Inactive accounts may not log in (without being forced of course).
is_anonymous
如果是一个匿名用户,将返回True。
This property should return True
if this is an anonymous user. (Actual users should return False
instead.)
get_id()
获得user的id
This method must return a unicode
that uniquely identifies this user, and can be used to load the user from the user_loader
callback. Note that this must be a unicode
- if the ID is natively an int
or some other type, you will need to convert it to unicode
.
To make implementing a user class easier, you can inherit from UserMixin
, which provides default implementations for all of these properties and methods. (It’s not required, though.)
@app.route('/login', methods=['GET', 'POST'])
def login():
# Here we use a class of some kind to represent and validate our
# client-side form data. For example, WTForms is a library that will
# handle this for us, and we use a custom LoginForm to validate.
form = LoginForm()
if form.validate_on_submit():
# Login and validate the user.
# user should be an instance of your `User` class
login_user(user)
flask.flash('Logged in successfully.')
next = flask.request.args.get('next')
# is_safe_url should check if the url is safe for redirects.
# See http://flask.pocoo.org/snippets/62/ for an example.
if not is_safe_url(next):
return flask.abort(400)
return flask.redirect(next or flask.url_for('index'))
return flask.render_template('login.html', form=form)
Views that require your users to be logged in can be decorated with the login_required
decorator:
@app.route("/settings")
@login_required
def settings():
pass
一个密码加密的插件
pw_hash = bcrypt.generate_password_hash('hunter2')
bcrypt.check_password_hash(pw_hash, 'hunter2') # returns True
和bootstrap集成的插件。
参考文档:http://www.pythondoc.com/flask-sqlalchemy/quickstart.html
常见情况下对于只有一个 Flask 应用,所有您需要做的事情就是创建 Flask 应用,选择加载配置接着创建 SQLAlchemy
对象时候把 Flask 应用传递给它作为参数。
一旦创建,这个对象就包含 sqlalchemy
和 sqlalchemy.orm
中的所有函数和助手。此外它还提供一个名为 Model 的类,用于作为声明模型时的 delarative 基类:
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
def __init__(self, username, email):
self.username = username
self.email = email
def __repr__(self):
return '' % self.username
为了创建初始数据库,只需要从交互式 Python shell 中导入 db 对象并且调用 SQLAlchemy.create_all()
方法来创建表和数据库:
>>> from yourapplication import db
>>> db.create_all()
Boom, 您的数据库已经生成。现在来创建一些用户:
>>> from yourapplication import User
>>> admin = User('admin', '[email protected]')
>>> guest = User('guest', '[email protected]')
但是它们还没有真正地写入到数据库中,因此让我们来确保它们已经写入到数据库中:
>>> db.session.add(admin)
>>> db.session.add(guest)
>>> db.session.commit()
访问数据库中的数据也是十分简单的:
>>> users = User.query.all()
[, ]
>>> admin = User.query.filter_by(username='admin').first()
SQLAlchemy 连接到关系型数据库,关系型数据最擅长的东西就是关系。因此,我们将创建一个使用两张相互关联的表的应用作为例子:
from datetime import datetime
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(80))
body = db.Column(db.Text)
pub_date = db.Column(db.DateTime)
category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
category = db.relationship('Category',
backref=db.backref('posts', lazy='dynamic'))
def __init__(self, title, body, category, pub_date=None):
self.title = title
self.body = body
if pub_date is None:
pub_date = datetime.utcnow()
self.pub_date = pub_date
self.category = category
def __repr__(self):
return '' % self.title
class Category(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
def __init__(self, name):
self.name = name
def __repr__(self):
return '' % self.name
首先让我们创建一些对象:
>>> py = Category('Python')
>>> p = Post('Hello Python!', 'Python is pretty cool', py)
>>> db.session.add(py)
>>> db.session.add(p)
现在因为我们在 backref 中声明了 posts 作为动态关系,查询显示为:
>>> py.posts
它的行为像一个普通的查询对象,因此我们可以查询与我们测试的 “Python” 分类相关的所有文章(posts):
>>> py.posts.all()
[]
简单地使用 pip 来安装:
$ pip install flask-debugtoolbar
设置调试工具栏是简单的:
from flask import Flask
from flask_debugtoolbar import DebugToolbarExtension
app = Flask(__name__)
# the toolbar is only enabled in debug mode:
app.debug = True
# set a 'SECRET_KEY' to enable the Flask session cookies
app.config['SECRET_KEY'] = ''
toolbar = DebugToolbarExtension(app)
当调试模式开启的时候,工具栏会自动地给添加到 Jinja 模板中。在生产环境中,设置 app.debug = False
将会禁用工具栏。
该扩展也支持 Flask 应用的工厂模式,先单独地创建工具栏接着后面为应用初始化它:
toolbar = DebugToolbarExtension()
# Then later on.
app = create_app('the-config.cfg')
toolbar.init_app(app)
工具栏支持多个配置选项:
名称 | 描述 | 默认值 |
---|---|---|
DEBUG_TB_ENABLED |
启用工具栏? | app.debug |
DEBUG_TB_HOSTS |
显示工具栏的 hosts 白名单 | 任意 host |
DEBUG_TB_INTERCEPT_REDIRECTS |
要拦截重定向? | True |
DEBUG_TB_PANELS |
面板的模板/类名的清单 | 允许所有内置的面板 |
DEBUG_TB_PROFILER_ENABLED |
启用所有请求的分析工具 | False , 用户自行开启 |
DEBUG_TB_TEMPLATE_EDITOR_ENABLED |
启用模板编辑器 | False |
要更改配置选项之一,在 Flask 应用程序配置中像这样设置它:
app.config['DEBUG_TB_INTERCEPT_REDIRECTS'] = False
The Flask-Script extension provides support for writing external scripts in Flask. This includes running a development server, a customised Python shell, scripts to set up your database, cronjobs, and other command-line tasks that belong outside the web application itself.
Flask-Script works in a similar way to Flask itself. You define and add commands that can be called from the command line to a Manager
instance:
# manage.py
from flask_script import Manager
from myapp import app
manager = Manager(app)
@manager.command
def hello():
print "hello"
if __name__ == "__main__":
manager.run()
Once you define your script commands, you can then run them on the command line:
python manage.py hello
> hello
官方文档:https://flask-migrate.readthedocs.io/en/late
初始化资源(create a migration repository)
$ flask db init
生成migration:(Generate an initial migration)
$ flask db migrate # 相当于Django中的 makemigrations
把migration迁移到数据库(apply the migration to the database)
$ flask db upgrade
注意:每次修改model后,要重新依次执行migrate和upgrade命令。(Then each time the database models change repeat the migrate
and upgrade
commands.)
查看所有可以使用的命令:
flask db --help
把db命令集成到flask-script中:
# manage.py
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)
集成之后,执行命令的方式如下:
$ python manage.py db init
$ python manage.py db migrate
$ python manage.py db upgrade
$ python manage.py db --help
参考文档:http://www.pythondoc.com/flask-wtf/
Flask-WTF 提供了对 WTForms 的集成。例如:
from flask_wtf import Form
from wtforms import StringField
from wtforms.validators import DataRequired
class MyForm(Form):
name = StringField('name', validators=[DataRequired()])
Note
从 0.9.0 版本开始,Flask-WTF 将不会从 wtforms 中导入任何的内容,用户必须自己从 wtforms 中导入字段。
另外,隐藏的 CSRF 令牌会被自动地创建。你可以在模板这样地渲染它:
<form method="POST" action="/">
{{ form.csrf_token }}
{{ form.name.label }} {{ form.name(size=20) }}
<input type="submit" value="Go">
form>
尽管如此,为了创建有效的 XHTML/HTML, Form 类有一个 hidden_tag 方法, 它在一个隐藏的 DIV 标签中渲染任何隐藏的字段,包括 CSRF 字段:
<form method="POST" action="/">
{{ form.hidden_tag() }}
{{ form.name.label }} {{ form.name(size=20) }}
<input type="submit" value="Go">
form>
在你的视图处理程序中验证请求:
@app.route('/submit', methods=('GET', 'POST'))
def submit():
form = MyForm()
if form.validate_on_submit():
return redirect('/success')
return render_template('submit.html', form=form)
注意你不需要把 request.form
传给 Flask-WTF;Flask-WTF 会自动加载。便捷的方法 validate_on_submit
将会检查是否是一个 POST 请求并且请求是否有效。