前言-基础增强与回顾
1、Request对象
Request对象现在该让Flask的请求对象request出场了,这个请求对象封装了从客户端发来的请求报文,我们能从它获取请求报文中的所有数据。 注意请求解析和响应封装实际上大部分是。由Werkzeug完成的,Flask子类化Werkzeug的请求(Request)和响应(Response)对象并添加了和程序相关的特定功能。
我们先从URL说起。假设请求的URL是http://helloflask.com/hello?name=Grey,当Flask接收到请求后,请求对象会提供多个属性来获取URL的各个部分
属性
|
值
|
args
|
一个字典,存储通过 URL 查询字符串传递的所有参数
|
path
|
u'/hello'
|
full_path
|
u'/hello?name=Grey'
|
host
|
u'helloflask.com'
|
host_url
|
u'http://helloflask.com/'
|
base_url
|
u'http://helloflask.com/hello'
|
url
|
u'http://helloflask.com/hello?name=Grey'
|
url_root
|
u'http://helloflask.com/'
|
除了URL,请求报文中的其他信息都可以通过request对象提供的属性和方法获取。参考
https://www.ztloo.com/2018/09/15/flask入门到精通-day1 中的操作请求数据模块。
有时候处理表单的时候,因为flask的默认的函数是get的,所以为了支持post,需要声明,要在后面添加methods 。
@app.route('/paginate/',methods=['GET', 'POST'])
2、钩子
类似于spring里面的aop,类似于单元测试框架里的beforeMethod注解、afterMethod的功能。
flask里有四种钩子
1、before_first_request: 注册一个函数,在处理第一个请求之前运行.
2、before_request : 在处理每次请求之前运行
3、after_request: 如果没有未处理的异常输出,在每次请求之后运行.注册的函数至少需要含有一个参数,这个参数实际上为服务器的响应,且函数中需要返回这个响应参数。
4、teardown_request: ,同样在每次请求之后运行.注册的函数至少需要含有一个参数,这个参数实际上为服务器的响应,且函数中需要返回这个响应参数
5、after_this_request:会在这个请求结束后运行。用法如下。
@app.route('/')
def hello_world():
@after_this_request
def add_header(response):
print(response)
return response
return 'Hello World!'
3、reponse案例
response,响应对象。return‘hello world’就是其响应对象, https://www.ztloo.com/2018/09/15/flask入门到精通-day1/ 中的关于响应。
from flask import Flask, make_response, json ...
@app.route('/foo')
def foo():
data = { 'name':'Grey Li',gender':'male' }
response = make_response(json.dumps(data))
response.mimetype = 'application/json'
return response
方法或属性
|
描述
|
headers
|
表示响应首部
|
status
|
状态码,文本类型
|
status_code
|
状态码,整型
|
data
|
一个调用get_data()和的描述符set_data()。不应该使用它,最终会被弃用。
|
get_json(force=False, silent=False, cache=True)
|
解析并将数据作为JSON返回。如果mimetype不指示JSON(application / json,请参阅 is_json()),则返回None除非force为true。如果解析失败,on_json_loading_failed()则调用并将其返回值用作返回值。
参数:
|
is_json
|
检查mimetype是否指示JSON数据, application / json或application / * + json。
|
max_cookie_size
|
|
mimetype
|
mimetype(没有charset等的内容类型)
|
set_cookie(key, value=”, max_age=None, expires=None, path=’/’, domain=None, secure=False, httponly=False, samesite=None)
|
为响应添加一个cookie
|
4、蓝图
为了在一个或多个应用中,使应用模块化并且支持常用方案, Flask 引入了 蓝图 概念
蓝图的基本概念是:在蓝图被注册到应用之后,所要执行的操作的集合。当分配请求 时, Flask 会把蓝图和视图函数关联起来,并生成两个端点之前的 URL 。
register_blueprint(蓝图,**选项)
参数:
蓝图 - 注册的蓝图。
url_prefix - 蓝图路由将以此为前缀。
5、Flask_login
from flask_login import LoginManager
login_manager = LoginManager()
login_manager.init_app(app)
@login_manager.user_loader
def load_user(user_id):
from models import Admin
user = Admin.query.get(int(user_id))
return user
//定义登陆视图是auth.py下的login方法。
login_manager.login_view = 'auth.login'
# login_manager.login_message = 'Your custom message'
//设置快闪消息,用于提示用户:
login_manager.login_message_category = 'warning'
6、Flask_mail
from flask_mail import Mail
mail = Mail()
mail.init_app(app)
发送邮件
# -*- coding: utf-8 -*-
from flask import Flask, request
from flask_script import Manager, Shell
from flask_mail import Mail, Message
from threading import Thread
app = Flask(__name__)
app.config['MAIL_DEBUG'] = True # 开启debug,便于调试看信息
app.config['MAIL_SUPPRESS_SEND'] = False # 发送邮件,为True则不发送
app.config['MAIL_SERVER'] = 'smtp.qq.com' # 邮箱服务器
app.config['MAIL_PORT'] = 465 # 端口
app.config['MAIL_USE_SSL'] = True # 重要,qq邮箱需要使用SSL
app.config['MAIL_USE_TLS'] = False # 不需要使用TLS
app.config['MAIL_USERNAME'] = 'xxx@qq.com' # 填邮箱
app.config['MAIL_PASSWORD'] = 'xxxxxx' # 填授权码
app.config['MAIL_DEFAULT_SENDER'] = 'xxx@qq.com' # 填邮箱,默认发送者
manager = Manager(app)
mail = Mail(app)
# 异步发送邮件
def send_async_email(app, msg):
with app.app_context():
mail.send(msg)
@app.route('/')
def index():
msg = Message(subject='Hello World',
sender="xxx@qq.com", # 需要使用默认发送者则不用填
recipients=['x1@qq.com', 'x2@qq.com'])
# 邮件内容会以文本和html两种格式呈现,而你能看到哪种格式取决于你的邮件客户端。
msg.body = 'sended by flask-email'
msg.html = '<b>测试Flask发送邮件<b>'
thread = Thread(target=send_async_email, args=[app, msg])
thread.start()
return '<h1>邮件发送成功</h1>'
if __name__ == '__main__':
manager.run()
7、flask_wtf
from flask_wtf import FlaskForm
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(1, 20)])
password = PasswordField('Password', validators=[DataRequired(), Length(1, 128)])
remember = BooleanField('Remember me')
submit = SubmitField('Log in')
@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('blog.index'))
form = LoginForm()
if form.validate_on_submit():
username = form.username.data
password = form.password.data
remember = form.remember.data
admin = Admin.query.first()
if admin:
if username == admin.username and admin.validate_password(password):
login_user(admin, remember)
flash('Welcome back.', 'info')
return redirect_back()
flash('Invalid username or password.', 'warning')
else:
flash('No account.', 'warning')
return render_template('auth/login.html', form=form)
8、flask_sqlalchemy
文章表模型
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(60))
body = db.Column(db.Text)
timestamp = db.Column(db.DateTime, default=datetime.utcnow, index=True)
can_comment = db.Column(db.Boolean, default=True)
category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
category = db.relationship('Category', back_populates='posts')
comments = db.relationship('Comment', back_populates='post', cascade='all, delete-orphan')
首页文章列表展现
@blog_bp.route('/')
def index():
page = request.args.get('page', 1, type=int)
per_page = current_app.config['BLUELOG_POST_PER_PAGE']
pagination = Post.query.order_by(Post.timestamp.desc()).paginate(page, per_page=per_page)
posts = pagination.items
return render_template('blog/index.html', pagination=pagination, posts=posts)
BLUELOG_POST_PER_PAGE 全局变量:为10,分页为10。
items:当前页面的项目
添加文章
@admin_bp.route('/post/new', methods=['GET', 'POST'])
@login_required
def new_post():
form = PostForm()
if form.validate_on_submit():
title = form.title.data
body = form.body.data
category = Category.query.get(form.category.data)
post = Post(title=title, body=body, category=category)
db.session.add(post)
db.session.commit()
flash('Post created.', 'success')
return redirect(url_for('blog.show_post', post_id=post.id))
return render_template('admin/new_post.html', form=form)
修改文章
@admin_bp.route('/post/<int:post_id>/edit', methods=['GET', 'POST'])
@login_required
def edit_post(post_id):
form = PostForm()
post = Post.query.get_or_404(post_id)
if form.validate_on_submit():
post.title = form.title.data
post.body = form.body.data
post.category = Category.query.get(form.category.data)
db.session.commit()
flash('Post updated.', 'success')
return redirect(url_for('blog.show_post', post_id=post.id))
form.title.data = post.title
form.body.data = post.body
form.category.data = post.category_id
return render_template('admin/edit_post.html', form=form)
edit_post视图的工作可以概括为:
首先从数据库中获取指定id的文章。如果是GET请求,使用文章的数据作为表单数据,然后渲染模板。
如果是POST请求,即用户单击了提交按钮,则根据表单的数据更新文章记录的数据。
和保存文章时的做法相反,通过把数据库字段的值分别赋给表单字段的数据,在渲染表单时,这些值会被填充到对应的input标签的value属性中,从而显示在输入框内。
需要注意,因为表单中的分类字段是存储分类记录的id值,所以这里使用post.category_id作为form.category.data的值。
删除文章
@admin_bp.route('/post/<int:post_id>/delete', methods=['POST'])
@login_required
def delete_post(post_id):
post = Post.query.get_or_404(post_id)
db.session.delete(post)
db.session.commit()
flash('Post deleted.', 'success')
return redirect_back()
博客案例参考:Flask Web开发实战入门、进阶与原来解析