对Flask感兴趣的,可以看下这个视频教程:http://study.163.com/course/courseLearn.htm?courseId=1004091002

1. WTForms 表单使用

WTForms 是一个支持多 web 框架的一个插件,主要功能有两个:第一个是做表单的验证,验证用户提交上来的信息是否合法,第二个是模板渲染。

1.1 WTForms 表单验证的基本使用

使用 WTForms 进行表单验证,会更好的管理我们的代码和项目结构,还可以大大提高开发项目时的效率。WTForms 功能强大,将表单定义成一个类,可以实现对表单字段的丰富限制。

使用 WTForms 实现表单验证的功能,主要有以下步骤:

  1. wtforms 中导入 Form 这个类,以及相关字段的数据类型

     from wtforms import From,StringField,IntegerField,FileField
    
     # Form 是一个基类,StringField 用来验证 String 类型的数据
  2. wrforms.validators 导入一些限制对象(如长度限制)

     from wrforms.validators import Length,EqualTo
    
     # # wrforms.vaildators 是一个验证器,包含 Length 在内的多种验证限制,Length 则专门对参数的长度进行验证,EqualTo 指定必须要和某个值相等
  3. 创建表单类并继承自 Form,定义相关字段

     class RegistForm(Form):		# 该类用来验证表单中传递的参数,属性名和参数名必须一致
    username = StringField(validators=[Length(min=3,max=10,message='用户名长度必须在3到10位之间')])
    # StringField 必须传入关键字参数 validators,且 validators 是一个 List 类型(此处仅对长度作验证)
    password = StringField(validators=[Length(min=6,max=16)])
    password_repeat = StringField(validators=[Length(min=6,max=16),EqualTo('password')]) # 验证长度和相等
  4. 在视图函数中使用该 RegistForm

     form = RegistForm(request.form)		# request.form 会拿到所有提交的表单信息
    if form.validate(): # form.validate() 方法会匹配表单信息并返回 True 或 False
    return '注册成功!'
    else:
    return '注册失败!'

完整代码如下:

# regist.html
<form action="" method="post">
<table>
<tbody>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td>确认密码:</td>
<td><input type="password" name="password_repeat"></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="点击提交"></td>
</tr>
</tbody>
</table>
</form> # 后端程序
from wtforms import Form,StringField
from wtforms.validators import Length,EqualTo class RegistForm(Form):
username = StringField(validators=[Length(min=3,max=10,message='输入的用户名不符合长度规范')])
password = StringField(validators=[Length(min=6,max=16)])
password_repeat = StringField(validators=[Length(min=6,max=16),EqualTo('password')]) @app.route('/regist/',methods=['GET','POST'])
def regist():
if request.method == 'GET':
return render_template('regist.html')
else:
form = RegistForm(request.form)
if form.validate():
return '注册成功'
else:
print(form.errors)
for message in form.errors:
return '注册成功'

1.2 WTForms 的相关验证器

除了上面使用到的两个验证器(StringFieldEqualTo)外,WTForms 中还有很多常用的验证器:

  1. Email:验证上传的数据是否为邮箱(格式)

     email = StringField(validators=[email()])
  2. EqualTo:验证上传的数据是否与另一个字段相等,常用在注册时的两次密码输入上

     password_repeat = StringField(validators=[Length(min=6,max=16),EqualTo('password')])
  3. InputRequired:该字段必须输入参数,且只要输入了,那么该字段就是 True。如果不是特数据情况,应该使用 InputRequired

     password = StringField(validators=[InputRequired()])	# 不管你的值是什么,只要输入了就是 True
  4. Length:长度限制,由 minmax 两个值进行限制

     password = StringField(validators=[Length(6,16)])
  5. NumberRange:数字的区间,由 minmax 两个值进行限制(包括 minmax)

     age = IntegerField(validators=[NumberRange(12,100)])
  6. Regexp:自定义正则表达式,比如手机号码的匹配

     phone = StringField(validators=[Regexp(r'1[34578]\d{9}')])
  7. URL:必须要是 URL 的形式

     homepage = StringField(validators=[URL()])
  8. UUID:验证 UUID

     uuid = StringField(validators=[UUID()])

注意在使用验证器的时候,后面要加上 ()

1.3 自定义验证器

如果以上介绍的验证器不满足项目当中的需求,那么还可以根据需求自定义相关的验证器。如果想要对表单中的某个字段进行更加细致的验证,那么可以根据需求对该字段定进行单独的验证,步骤如下:

  1. 在表单验证类中定义一个方法,方法的命名规则为:validate_字段名(self,field)
  2. 在方法中使用 field.data 获取到用户上传到这个字段上的值。
  3. 对于验证的判断:若验证成功,可以什么都不做;若验证失败,则必须跑出 wtforms.validators.ValidationError 异常,并填入验证失败的原因。

示例代码如下所示:

from wtforms import Form,StringField
from wtforms.validators import Length,ValidationError class LoginForm(Form):
captcha = StringField(validators=[Length(4,4)])
def validate_captcha(self,field): # 用 validate_captcha 来指定该验证器是针对 captcha 字段的
if field.data != 'aw7e':
raise ValidationError('验证码输入错误!')

1.4 WTForms 渲染模板

这个功能可以让我们的前端代码少写一点点,但是实际上用处不大。主要使用方法如下:

  1. forms 文件中定义一个表单类:

     class SettingsForms(Form):
    username = StringField(validators=[Length(4,10)])
  2. 在视图函数中返回模板时传递相关参数:

     @app.route('/settings/',methods=['GET','POST'])
    def Settings():
    if request.method == 'GET':
    form = SettingsForms()
    return render_template('settings.html',my_form=form)
    else:
    pass
  3. 在前端模板中调用

     <form action="" method="post">
    <table>
    <tbody>
    <tr>
    <td>{{ my_form.username.label }}</td>
    <td>{{ my_form.username() }}</td>
    </tr>
    <tr>
    <td></td>
    <td><input type="submit" value="提交"></td>
    </tr>
    </tbody>
    </table>
    </form>

    其中,第五第六两行相当于:

     <td>用户名:</td>
    <td><input type="text" name='username'></td>

实际上,这个功能在生产环境中几乎没有任何作用,很鸡肋。

2. 文件上传和访问

2.1 文件上传

上传文件时需要注意以下几点:

  1. 在模板中,form 表单内,要指定 encotype='multipart/form-data' 才能实现文件的上传:

     <form action="" method="post" enctype="multipart/form-data">
    ...
    </form>
  2. 在后台获取文件,需要使用 request.files.get('标签名') 才能获取到上传的文件:

     avatar = request.files.get('avatar')
  3. 保存文件使用 avatar.save(路径) 实现,推荐在保存文件时先对文件进行安全封装:

     from werkzueg.utils import secure_filename
    import os UPLOAD_PATH = os.path.join(os.path.dirname(__file__),'images') # UPLOAD_PATH = 当前路径/images avatar.save(UPLOAD_PATH,secure_filename(avatar.filename))
  4. 后台完整代码如下:

     from werkzeug.utils import secure_filename
    import os UPLOAD_PATH = os.path.join(os.path.dirname(__file__),'images') # 定义文件保存路径:UPLOAD_PATH = 当前路径/images @app.route('/upload/',methods=['GET','POST'])
    def upload():
    if request.method == 'GET':
    return render_template('upload.html')
    else:
    avatar = request.files.get('avatar')
    filename = secure_filename(avatar.filename) # 对文件名进行安全过滤
    avatar.save(os.path.join(UPLOAD_PATH,filename))
    desc = request.form.get('desc')
    print(desc)
    return '上传成功!'

2.2 文件访问

实现了文件上传,那么用户肯定会需要对文件进行访问。在 Flask 中,实现文件的访问必须要定义一个单独的 url 与视图函数的映射,并且要借助 send_from_directory 方法返回文件给客户端。

  1. flask 导入 send_from_directory

     from flask import send_from_directory
  2. 定义视图函数并映射到文件的 url

     UPLOAD_PATH = os.path.join(os.path.dirname(__file__),'images')
    
     @app.route('/getfile/<filename>/')
    def getfile(filename):
    return send_from_directory(UPLOAD_PATH,filename) # send_from_directory 要传入路径和文件名 # 用户可以访问 http://domainname/filename 对文件进行访问

2.3 使用验证器对验证上传的文件

在验证文件的时候,同样要定义一个验证的类,然后用该验证类去验证上传的文件。主要分为以下几个步骤:

  1. 导入 FileField 和文件验证器:FileRequiredFileAllowed

     from forms import FileField
    from flask_wtf.file import FileRequired,FileAllowed # 注意这两个针对文件的验证器是从 flask_wtf_file 中导入的,而不是从之前的 wtforms.validators 中导入
  2. 定义表单类并继承自 Form,然后定义相关字段

     class UpLoadForm(Form):
    avatar = FileField(validators=[FileRequired(),FileAllowed(['jpg','png','gif'])]) # FileRequired() 要求必须传入文件,FileAllowed() 则指定了允许的文件类型
    desc = StringField(validators=[InputRequired()])
  3. 在主 app 文件中引用

     from werkzeug.datastructures import CombinedMultiDict	# CombinedMultiDict 用来合并两个不可变的 dict
    form =UpLoadForm(CombinedMultiDict([request.form,request.files])) # 传入用户提交的信息,其中 request.form 是表单中的信息,request.files 是上传的文件
  4. 完整代码如下:

     # forms.py 文件
    from wtforms import Form,StringField,FileField
    from flask_wtf.file import FileRequired,FileAllowed class UpLoadForm(Form):
    avatar = FileField(validators=[FileRequired(),FileAllowed(['jpg','png','gif'])])
    desc = StringField(validators=[InputRequired()]) # 主 app 文件
    from forms import UpLoadForm
    from werkzeug.utils import secure_filename
    from werkzeug.datastructures import CombinedMultiDict
    import os UPLOAD_PATH = os.path.join(os.path.dirname(__file__),'images') @app.route('/upload/',methods=['GET','POST'])
    def upload():
    if request.method == 'GET':
    return render_template('upload.html')
    else:
    form =UpLoadForm(CombinedMultiDict([request.form,request.files]))
    if form.validate():
    avatar = request.files.get('avatar')
    filename = secure_filename(avatar.filename)
    avatar.save(os.path.join(UPLOAD_PATH,filename))
    desc = request.form.get('desc')
    print(desc)
    return '上传成功!'
    else:
    return '上传失败!'

3. Cookie 的使用

3.1 设置 Cookie

设置 CookieResponse 类中有的方法,用法是:在视图函数中

resp = Response('MYYD')		# 创建一个 Response 对象,传入的字符串会被显示在网页中
resp.set_cookie('username','myyd')
return resp

其中,set_cookie() 中的参数有:

key			键
value 值
max_age IE8 以下不支持,优先级比 expires 高
expires 几乎所有浏览器都支持,必须传入 datetime 的数据类型,并且默认加 8 个小时(因为我们是东八区)
path 生效的 URL,'/' 代表该域名下所有 URL 都生效,一般默认就好
domian 域名,若没设置,则只能在当前域名下使用
secure 默认 False,若改为 True 则只能在 https 协议下使用
httponly 默认 False,若改为 True 则只能被浏览器所读取,不能被 JavaScript 读取(JavaScript可以在前端处理一些简单逻辑)

使用时依次传入即可,如果有些选项要跳过则需要指定一下参数名。

完整代码如下所示:

from flask import Flask,Response

app = Flask(__name__)

@app.route('/')
def hello_world():
resp = Response('首页')
resp.set_cookie('username','MYYD')
return resp if __name__ == '__main__':
app.run()

3.2 删除 Cookie

删除 Cookie 时需要另外指定一条 URL 和视图函数,也是使用 Response 来创建一个类,并使用 resp.delete_cookie() 来完成这个需求。代码如下所示:

from flask import Flask,Response

app = Flask(__name__)

@app.route('/delCookie/')
def delete_cookie():
resp = Response('删除Cookie')
resp.delete_cookie('username')
return resp if __name__ == '__main__':
app.run()

3.3 设置 Cookie 的有效期

设置 Cookie 的有效期,可以有两种方法:使用 max_ageexpires

  1. 使用 max_age

    使用 max-age 时要注意,max-age 不支持 IE8 及以下版本的浏览器,并且只能相对于现在的时间往后进行推迟(单位是秒s),而不能指定具体的失效时间。使用方法如下代码所示:

     resp.set_cookie('username','myyd',max_age=60)	# 设置该 cookie 60s 之后失效。
  2. 使用 expires

    使用 expires 时要注意,必须要使用格林尼治时间,因为最后会自动加上 8 小时(中国是东八区)。expires 的兼容性要比 max_age 要好,尽管在新版的 http 协议中指明了 expires 要被废弃,但现在几乎所有的浏览器都支持 expires

    expire 设置失效时间,可以针对当前时间往后推移,也可以指定某一个具体的失效时间。具体如下所示:

    1. 针对当前时间推移

       from datetime import datetime,timedelta
      
       expires = datetime.now() + timedelta(days=30,hours=16)	# 当下时间往后推移 31 天失效,注意这里给的参数是减了 8 小时的
      resp.set_cookie('username','MYYD',expires=expires)
    2. 指定具体日期

       from datetime import datetime
      
       resp = Response('首页')
      expires = datetime(year=2018,month=12,day=30,hour=10,minute=0,second=0) # 实际上的失效时间是 2018-12-30-18:0:0
      resp.set_cookie('username','MYYD',expires=expires)
      return resp
  3. 其他注意事项

    此外,还要注意几点:

    1. 当同时使用 max_ageexpires 的时候,会优先使用 max_age 指定的失效时间
    2. 若同时不使用 max_ageexpires 的时候,默认的 cookie 失效时间为浏览器关闭的时间(而不是窗口关闭的时间)
    3. expires 要设置为格林尼治时间,同时导入 datetime.datetimedatetime.timedelta

4. RFCS

防范 CSRF 攻击的措施:

实现:在返回一些危险操作的页面时,同时返回一个 csrf_tokencookie 信息,并且在返回的页面表单中也返回一个带有 csrf_token 值的 input 标签。

原理:当用户提交该表单时,若表单中 input 标签的 csrf_token 值存在并且和 cookie 中的 csrf_token 值相等则允许操作;若不满足该条件,则操作不被允许。

原因:因为 csrf_token 这个值是在返回危险操作页面时随机生成的,黑客是无法伪造出相同的 csrf_token 值的,因为黑客不能操作非自己域名下的 cookie,即不知道 cookie 中的 csrf_token 值的内容。

具体实现:

主app文件:

  1. from flask_wtf import CSRFProtect
  2. CSRFProtect(app)

模板文件(表单中):

<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
注意这里是要在所有危险操作页面的表单内都需要加入。

浏览器:F12 -> Network -> Disable Cache

// 整个文档加载完毕后才会执行这个函数
$(function () {
$('#submit').click(function (event) {
// 阻止默认的表单提交行为
// event.preventDefault();
var email = $('input[name=email]').val();
var password = $('input[name=password]').val();
var csrftoken = $('input[name=csrf_token]').val(); // $.post() 方法用来提交表单
$.post({
'url':'/login/',
'data':{
'email': email,
'password': password,
'csrftoken': csrftoken
},
'success':function (data) {
console.log(data);
},
'fail':function (error) {
console.log(error);
}
});
})
});

5. Flask Restful

5.1 Restful API 介绍

Restful API 是用于在前端与后台进行通信时使用的一套传输规范,这些规范可以使后台开发变得更加轻松。

其采用的协议httphttps

传输数据格式采用 json 而不是 xml。使用 json 传输数据会变得更加简单高效,而不是像 xml 那样伴随有众多的固定代码(类似于 html 的格式),即每次传输时 xml 占的资源更多。

并且其url 链接中,不能包含动词,只能包含名词;并且对于名词,若出现复数,则必须加上 s

HTTP 的请求方法主要有以下 5 种,但实际上 getpost 就够用了。

  1. get:获取服务器上的一个资源
  2. post:在服务器上创建一个紫爱云
  3. put:在服务器上更新资源(客户端需要提交更新后的所有数据)
  4. patch:在服务器上更新资源(客户端只需要提交所更新的数据)
  5. delete:在服务器上删除一个资源

5.2 Flask-Restful 插件

  1. 安装

    Flask-Restful 需要在 Flask 0.8 以上版本运行,在 python 2.6 以上版本运行,通过 pip install flask-restful 即可安装。

  2. 使用

    使用之前必须从 flask_restful 中导入 ApiResource;然后用 Api 将初始化的 app 绑定起来;再定义一个类视图,定义类视图必须继承自 Resource;最后用 add_resource 方法将接口(URL)与视图绑定起来。完整代码如下:

     from flask import Flask
    from flask_restful import Api,Resource # Api 用来绑定 app,Resource 用来创建类视图 app = Flask(__name__)
    api = Api(app) class LoginView(Resource):
    def post(self): # 定义了什么样的方法,才能用什么样的请求
    return {'username':'MYYD'} # 可以直接返回字典类型的数据(因为字典数据已经自动转换成Json格式了) api.add_resource(LoginView,'/login/',endpoint='login') # 映射类视图和接口,endpoint 用来指定 url_for 反转到类视图时的关键字 if __name__ == '__main__':
    app.run()
  3. 注意事项:

    1. 映射类视图和接口时不指定 endpoint,则进行 url_for 反转时默认使用视图名称的小写,即上例中的 loginview
    1. add_resource 方法的第二个参数,用来指定访问这个类视图的接口,与之前不同的是,这个地方可以传入多个接口。

5.3 Flask-Restful 参数验证

  1. 基本使用

    Flask-Restful 插件为我们提供了类似之前的 WTForm 表单验证的包,可以用来验证提交的数据是否合法,叫做 reqparse。基本用法如下(3步骤):

     parser = reqparse.RequestParser()	# 初始化一个 RequestParser 对象
    parser.add_argument('password',type=int,help='password input error') # 指定验证的参数名称,类型以及验证不通过时的提示信息
    args = parser.parse_args() # 执行验证

    完整代码如下:

     from flask_restful import Api,Resource,reqparse
    
     class LoginView(Resource):
    def post(self): # post 方法提交数据时传入的 username 和 password,这里不需要定义
    parser = reqparse.RequestParser()
    parser.add_argument('username',type=str,help='用户名格式错误') # 如果提交数据时没传入,默认为 None
    parser.add_argument('age',type=int,help='密码错误')
    args = parser.parse_args()
    print(args)
    return {'username':'MYYD'}
  2. add_argument 解析

    在使用 add_argument 对上传的数据进行验证时,可以根据需求使用不同的选项进行验证,常用的选项有:

    1. default:默认值,如果没有传入该参数,则使用 default 为该参数指定默认的值。
    2. required:置为 True 时(默认为 False),该参数必须传入值,否则抛出异常。
    3. type:指定该参数的类型,并进行强制转换,若强制转换失败则抛出异常。
    4. choices:相当于枚举类型,即该传入的参数只能为 choices 列表中指定的值。
    5. help:当验证失败时抛出的异常信息。
    6. trim:置为 True 时对上传的数据进行去空格处理(只去掉字符串前后的空格,不去掉字符串之间的空格)。

    其中,type 选项除了可以指定 python 自带的一些数据类型外,还可以指定 flask_restful.inputs 下的一些特定类型来进行强制转换。常用的类型如下:

    1. url:会判断上传的这个参数是不是一个 url,若不是则抛出异常。
    2. regex:会判断上传的这个参数是否符合正则表达式中的格式,若不符合则抛出异常。
    3. date:将上传的这个参数强制转换成 datetime.date 类型,若转换不成功则抛出异常。

    在使用 type 指定 flask_restful.inputs 数据类型时的用法如下:

     parser.add_argument('birthday',type=inputs.date,help='日期输入错误')

5.4 Flask-Restful 类视图返回内容

返回数据时候可以使用最原始的方法,返回一个字典。但是 Restful 推荐我们使用 Restful 方法,如下:

  1. 先定义一个字典,该字典定义所有要返回的参数

  2. 再使用 marshal_with(字典名) 传入字典名称

  3. 最后返回数据就行了,如下:

     from flask_restful import Api,Resource,fields,marshal_with
    api = Api(app)
    class Article(object):
    def __init__(self,title,content):
    self.title = title
    self.content = content
    artilce = Article('MYYD','wuba luba dub dub')
    class LoginView(Resource):
    resource_field = {
    'title': fields.String,
    'content': fields.String
    }
    @marshal_with(resource_field)
    def get(self):
    return artilce # 可以直接返回 Article 的实例,会拿到 article 对象的两个属性并返回
    api.add_resource(LoginView,'/login/',endpoint='login')

这样做的好处是:

  1. 可以少写代码
  2. 可以规范输出,即如果 article 对象只有 title 属性而没有 content 属性,也会返回 content 的值,只不过该值被置为 None

5.5 Flask-Restful 标准返回

5.5.1 复杂结构

对于一个类视图,可以指定好一些数据字段用于返回。指定的这些数据字段,在此后使用 ORM 模型或者自定义模型时,会自动获取模型中的相应字段,生成 Json 数据,并返回给客户端。对于拥有子属性的字段而言,若想成功获取其属性并返回给客户端,需要引用 fields.Nested 并在其中定义子属性的字段。整个例子如下:

  1. 模型关系

     class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer,primary_key=True)
    username = db.Column(db.String(50),nullable=False)
    email = db.Column(db.String(50),nullable=False) article_tag_table = db.Table(
    'article_tag',
    db.Column('article_id',db.Integer,db.ForeignKey("article.id"),primary_key=True),
    db.Column('tag_id',db.Integer,db.ForeignKey("tag.id"),primary_key=True)
    ) class Article(db.Model):
    __tablename__ = 'article'
    id = db.Column(db.Integer,primary_key=True)
    title = db.Column(db.String(50),nullable=False)
    content = db.Column(db.Text)
    author_id = db.Column(db.Integer,db.ForeignKey('user.id')) author = db.relationship('User',backref='articles') tags = db.relationship('Tag',secondary=article_tag_table,backref='articles') class Tag(db.Model):
    __tablename__ = 'tag'
    id = db.Column(db.Integer,primary_key=True)
    name = db.Column(db.String(50),nullable=False)
  2. 返回时定义的数据字段

    注意这里有三点必须实现:

    1. 导入相关包并初始化 app
    2. 定义返回数据的字段
    3. 使用装饰器 marshal_with 传入定义的数据字段
     from flask_restful import Api,Resource,fields,marshal_with
    api = Api(app)
    class ArticleView(Resource): article_detail = {
    'article_title': fields.String(attribute='title'),
    'content': fields.String,
    'author': fields.Nested({ # 返回有子属性的字段时要用 fields.Nested()
    'username': fields.String,
    'email': fields.String,
    'age': fields.Integer(default=1)
    }),
    'tags': fields.Nested({ # 返回有子属性的字段时要用 fields.Nested()
    'name': fields.String
    })
    }
    @marshal_with(article_detail)
    def get(self,article_id):
    article = Article.query.filter_by(id=article_id).first()
    return article
5.5.2 重命名属性

重命名属性很简单,就是返回的时候使用不同于模型本身的字段名称,此操作需要借助 attribute 选项。如下所示代码:

article_detail = {
'article_title': fields.String(attribute='title')
}

Article 模型中的属性原本是 title,但是要返回的字段想要命名为 article_title。如果不使用 attribute 选项,则在返回时会去 Article 模型中找 article_title 属性,很明显是找不到的,这样以来要返回的 article_title 字段会被置为 Null。使用 attribute 选项后,当返回 article_title 字段时,会去 Article 模型中找 attribute 选项指定的 title 属性,这样就可以成功返回了。

5.5.3 默认值

当要返回的字段没有值时,会被置为 Null,如果不想置为 Null,则需要指定一个默认的值,此操作需要借助 default 选项。如下代码所示:

article_detail = {
'article_title': fields.String(attribute='title')
'readed_number': fields.Integer(default=0)
}

当想要返回一篇文章的阅读量时,若没有从模型中获取到该字段的值,若不使用 default 选项则该字段会被置为 Null;若使用了该选项,则该字段会被置为 0

5.6 Flask-restful 细节

实际上,flask-restful 还可以嵌套在蓝图中使用,也能返回一个 html 模板文件。

  1. 嵌套蓝图使用

    搭配蓝图使用时,在注册 api 时就不需要使用 app 了,而是使用蓝图的名称,如下:

     article_bp = Blueprint('article',__name__,url_prefix='/article')
    api = Api(article_bp)

    其他的和之前一样,不过要在主 app 文件中注册一下蓝图。

  2. 渲染模板

    如果想使用 flask-restful 返回 html 模板,则必须使用 api.representation() 装饰器来转换返回数据的类型,并根据该装饰器定义一个函数,用于返回该模板,如下:

     from flask import render_template,make_response
    
     @api.representation('text/html')
    def outPrintListForArticle(data,code,headers): # 这里要传入这三个参数
    resp = make_response(data) # 其中,data 就是模板的 html 代码
    return resp class ListView(Resource):
    def get(self):
    return render_template('list.html')
    api.add_resource(ListView,'/list/',endpoint='list')

Flask-论坛开发-4-知识点补充的更多相关文章

  1. 前端开发面试知识点大纲--摘自jackyWHJ

    前端开发面试知识点大纲:HTML&CSS:    对Web标准的理解.浏览器内核差异.兼容性.hack.CSS基本功:布局.盒子模型.选择器优先级及使用.HTML5.CSS3.移动端适应 Ja ...

  2. 《Flask Web开发实战:入门、进阶与原理解析(李辉著 )》PDF+源代码

    一句话评价: 这可能是市面上(包括国外出版的)你能找到最好的讲Flask的书了 下载:链接: https://pan.baidu.com/s/1ioEfLc7Hc15jFpC-DmEYBA 提取码: ...

  3. 从零开始的全栈工程师——html篇1.8(知识点补充与浏览器兼容性)

    知识点补充 一.浏览器的兼容问题(关于浏览器的兼容问题 有很多大佬已经解释的很清楚了 这个得自己百度去多花点时间去了解 这里咱们只说一下前面的漏点) 浏览器兼容性问题又被称为网页兼容性或网站兼容性问题 ...

  4. python day4 元组/字典/集合类知识点补充

    目录 python day4 元组/字典/集合类知识点补充 1. 元组tuple知识点补充 2. 字典dict的知识点补充 3. 基本数据类型set 4. 三元运算,又叫三目运算 5. 深复制浅复制 ...

  5. python 知识点补充

    python 知识点补充 简明 python 教程 r 或 R 来指定一个 原始(Raw) 字符串 Python 是强(Strongly)面向对象的,因为所有的一切都是对象, 包括数字.字符串与 函数 ...

  6. disruptor笔记之八:知识点补充(终篇)

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  7. Jaeger知识点补充

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  8. Flask web开发 请求拦截和预处理

    我们在开发WEB应用时,往往会需要对所有的url请求进行拦截,做些预处理,比如权限处理.日志等统一处理. 本文介绍一下Flask中的处理机制.我们通过一个简单的例子来说明. 1.编写一个简单应用 ru ...

  9. Flask web开发 处理Session

    本文我们在上篇文章<Flask web开发  处理POST请求(登录案例)>的基础上,来讲述Flask对session的支持. 在上面案例上,我们需要修改和新增如下功能 1.登录成功后的 ...

  10. 《Flask Web开发——基于Python的Web应用开发实践》一字一句上机实践(上)

    目录 前言 第1章 安装 第2章 程序的基本结构 第3章 模板 第4章 Web表单 第5章 数据库 第6章 电子邮件 第7章 大型程序的结构   前言 学习Python也有一个半月时间了,学到现在感觉 ...

随机推荐

  1. Django view 视图

    request.method 判断请求方式 8种 GET : 获取一个页面 POST: 提交数据 PUT : 上传 HEAD: 不用上传就获取数据 DELETE: 删除 Request-URL 标识的 ...

  2. 2018-2019-2 网络对抗技术 20165318 Exp6 信息搜集与漏洞扫描

    2018-2019-2 网络对抗技术 20165318 Exp6 信息搜集与漏洞扫描 原理与实践说明 实践原理 实践内容概述 基础问题回答 实践过程记录 各种搜索技巧的应用 DNS IP注册信息的查询 ...

  3. OCX ACTIVEX程序打包个人精典案例(OCX)

  4. Centos 6.5 pptpd服务端搭建过程

    首先检测有没有启用ppp和tun cat /dev/ppp cat /dev/net/tun 如果显示是这样的 cat: /dev/ppp: No such device or address cat ...

  5. 2017-2018-2 20155314《网络对抗技术》Exp4 恶意代码分析

    2017-2018-2 20155314<网络对抗技术>Exp4 恶意代码分析 目录 实验要求 实验内容 实验环境 基础问题回答 预备知识 实验步骤 1 静态分析 1.1 使用virsca ...

  6. ADB安装及使用

    环境安装: 下载.安装和配置ADB     https://jingyan.baidu.com/article/22fe7cedf67e353002617f25.html 安装驱动adbdriver  ...

  7. linux终端神器kmux

    文章链接 https://www.cnblogs.com/rond/p/4466599.html http://cenalulu.github.io/linux/tmux/ https://www.c ...

  8. [转]QT4.8.5+qt-vs-addin-1.1.11+VS2010安装配置和QT工程的新建和加载

    1.下载windows下的QT库 QT4.8.5 for vs2010: http://download.qt-project.org/official_releases/qt/4.8/4.8.5/q ...

  9. PAT A1104 Sum of Number Segments (20 分)——数学规律,long long

    Given a sequence of positive numbers, a segment is defined to be a consecutive subsequence. For exam ...

  10. AI mac安装TensorFlow

    1.安装pip sudo easy_install pip 2.安装virtualenv sudo pip install --upgrade virtualenv 3.在指定目录创建virtuale ...