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

1. WTForms 表单使用

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

1.1 WTForms 表单验证的基本使用

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

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

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

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

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

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

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

完整代码如下:

  1. # regist.html
  2. <form action="" method="post">
  3. <table>
  4. <tbody>
  5. <tr>
  6. <td>用户名:</td>
  7. <td><input type="text" name="username"></td>
  8. </tr>
  9. <tr>
  10. <td>密码:</td>
  11. <td><input type="password" name="password"></td>
  12. </tr>
  13. <tr>
  14. <td>确认密码:</td>
  15. <td><input type="password" name="password_repeat"></td>
  16. </tr>
  17. <tr>
  18. <td></td>
  19. <td><input type="submit" value="点击提交"></td>
  20. </tr>
  21. </tbody>
  22. </table>
  23. </form>
  24. # 后端程序
  25. from wtforms import Form,StringField
  26. from wtforms.validators import Length,EqualTo
  27. class RegistForm(Form):
  28. username = StringField(validators=[Length(min=3,max=10,message='输入的用户名不符合长度规范')])
  29. password = StringField(validators=[Length(min=6,max=16)])
  30. password_repeat = StringField(validators=[Length(min=6,max=16),EqualTo('password')])
  31. @app.route('/regist/',methods=['GET','POST'])
  32. def regist():
  33. if request.method == 'GET':
  34. return render_template('regist.html')
  35. else:
  36. form = RegistForm(request.form)
  37. if form.validate():
  38. return '注册成功'
  39. else:
  40. print(form.errors)
  41. for message in form.errors:
  42. return '注册成功'

1.2 WTForms 的相关验证器

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

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

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

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

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

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

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

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

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

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

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

1.3 自定义验证器

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

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

示例代码如下所示:

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

1.4 WTForms 渲染模板

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

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

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

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

    1. <form action="" method="post">
    2. <table>
    3. <tbody>
    4. <tr>
    5. <td>{{ my_form.username.label }}</td>
    6. <td>{{ my_form.username() }}</td>
    7. </tr>
    8. <tr>
    9. <td></td>
    10. <td><input type="submit" value="提交"></td>
    11. </tr>
    12. </tbody>
    13. </table>
    14. </form>

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

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

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

2. 文件上传和访问

2.1 文件上传

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

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

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

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

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

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

2.2 文件访问

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

  1. flask 导入 send_from_directory

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

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

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

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

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

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

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

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

    1. # forms.py 文件
    2. from wtforms import Form,StringField,FileField
    3. from flask_wtf.file import FileRequired,FileAllowed
    4. class UpLoadForm(Form):
    5. avatar = FileField(validators=[FileRequired(),FileAllowed(['jpg','png','gif'])])
    6. desc = StringField(validators=[InputRequired()])
    7. # 主 app 文件
    8. from forms import UpLoadForm
    9. from werkzeug.utils import secure_filename
    10. from werkzeug.datastructures import CombinedMultiDict
    11. import os
    12. UPLOAD_PATH = os.path.join(os.path.dirname(__file__),'images')
    13. @app.route('/upload/',methods=['GET','POST'])
    14. def upload():
    15. if request.method == 'GET':
    16. return render_template('upload.html')
    17. else:
    18. form =UpLoadForm(CombinedMultiDict([request.form,request.files]))
    19. if form.validate():
    20. avatar = request.files.get('avatar')
    21. filename = secure_filename(avatar.filename)
    22. avatar.save(os.path.join(UPLOAD_PATH,filename))
    23. desc = request.form.get('desc')
    24. print(desc)
    25. return '上传成功!'
    26. else:
    27. return '上传失败!'

3. Cookie 的使用

3.1 设置 Cookie

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

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

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

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

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

完整代码如下所示:

  1. from flask import Flask,Response
  2. app = Flask(__name__)
  3. @app.route('/')
  4. def hello_world():
  5. resp = Response('首页')
  6. resp.set_cookie('username','MYYD')
  7. return resp
  8. if __name__ == '__main__':
  9. app.run()

3.2 删除 Cookie

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

  1. from flask import Flask,Response
  2. app = Flask(__name__)
  3. @app.route('/delCookie/')
  4. def delete_cookie():
  5. resp = Response('删除Cookie')
  6. resp.delete_cookie('username')
  7. return resp
  8. if __name__ == '__main__':
  9. app.run()

3.3 设置 Cookie 的有效期

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

  1. 使用 max_age

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

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

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

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

    1. 针对当前时间推移

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

      1. from datetime import datetime
      2. resp = Response('首页')
      3. expires = datetime(year=2018,month=12,day=30,hour=10,minute=0,second=0) # 实际上的失效时间是 2018-12-30-18:0:0
      4. resp.set_cookie('username','MYYD',expires=expires)
      5. 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)

模板文件(表单中):

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

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

  1. // 整个文档加载完毕后才会执行这个函数
  2. $(function () {
  3. $('#submit').click(function (event) {
  4. // 阻止默认的表单提交行为
  5. // event.preventDefault();
  6. var email = $('input[name=email]').val();
  7. var password = $('input[name=password]').val();
  8. var csrftoken = $('input[name=csrf_token]').val();
  9. // $.post() 方法用来提交表单
  10. $.post({
  11. 'url':'/login/',
  12. 'data':{
  13. 'email': email,
  14. 'password': password,
  15. 'csrftoken': csrftoken
  16. },
  17. 'success':function (data) {
  18. console.log(data);
  19. },
  20. 'fail':function (error) {
  21. console.log(error);
  22. }
  23. });
  24. })
  25. });

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)与视图绑定起来。完整代码如下:

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

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

5.3 Flask-Restful 参数验证

  1. 基本使用

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

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

    完整代码如下:

    1. from flask_restful import Api,Resource,reqparse
    2. class LoginView(Resource):
    3. def post(self): # post 方法提交数据时传入的 username 和 password,这里不需要定义
    4. parser = reqparse.RequestParser()
    5. parser.add_argument('username',type=str,help='用户名格式错误') # 如果提交数据时没传入,默认为 None
    6. parser.add_argument('age',type=int,help='密码错误')
    7. args = parser.parse_args()
    8. print(args)
    9. 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 数据类型时的用法如下:

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

5.4 Flask-Restful 类视图返回内容

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

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

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

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

    1. from flask_restful import Api,Resource,fields,marshal_with
    2. api = Api(app)
    3. class Article(object):
    4. def __init__(self,title,content):
    5. self.title = title
    6. self.content = content
    7. artilce = Article('MYYD','wuba luba dub dub')
    8. class LoginView(Resource):
    9. resource_field = {
    10. 'title': fields.String,
    11. 'content': fields.String
    12. }
    13. @marshal_with(resource_field)
    14. def get(self):
    15. return artilce # 可以直接返回 Article 的实例,会拿到 article 对象的两个属性并返回
    16. 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. 模型关系

    1. class User(db.Model):
    2. __tablename__ = 'user'
    3. id = db.Column(db.Integer,primary_key=True)
    4. username = db.Column(db.String(50),nullable=False)
    5. email = db.Column(db.String(50),nullable=False)
    6. article_tag_table = db.Table(
    7. 'article_tag',
    8. db.Column('article_id',db.Integer,db.ForeignKey("article.id"),primary_key=True),
    9. db.Column('tag_id',db.Integer,db.ForeignKey("tag.id"),primary_key=True)
    10. )
    11. class Article(db.Model):
    12. __tablename__ = 'article'
    13. id = db.Column(db.Integer,primary_key=True)
    14. title = db.Column(db.String(50),nullable=False)
    15. content = db.Column(db.Text)
    16. author_id = db.Column(db.Integer,db.ForeignKey('user.id'))
    17. author = db.relationship('User',backref='articles')
    18. tags = db.relationship('Tag',secondary=article_tag_table,backref='articles')
    19. class Tag(db.Model):
    20. __tablename__ = 'tag'
    21. id = db.Column(db.Integer,primary_key=True)
    22. name = db.Column(db.String(50),nullable=False)
  2. 返回时定义的数据字段

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

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

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

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

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

5.5.3 默认值

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

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

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

5.6 Flask-restful 细节

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

  1. 嵌套蓝图使用

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

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

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

  2. 渲染模板

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

    1. from flask import render_template,make_response
    2. @api.representation('text/html')
    3. def outPrintListForArticle(data,code,headers): # 这里要传入这三个参数
    4. resp = make_response(data) # 其中,data 就是模板的 html 代码
    5. return resp
    6. class ListView(Resource):
    7. def get(self):
    8. return render_template('list.html')
    9. 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. 解决python中 .to_csv() 的乱码问题

    解决方法:添加参数 encoding='utf_8_sig' df.to_csv('users.csv', encoding='utf_8_sig')

  2. Python程序的编写方式

    直接在Python的交互式环境编写代码 现在,了解了如何启动和退出Python的交互式环境,我们就可以正式开始编写Python代码了. 在写代码之前,请千万不要用“复制”-“粘贴”把代码从页面粘贴到你 ...

  3. VCS双机+oracle 11gR2+ASM主机名修改

    ----------------------------------------------------------------------------VCS修改主机名---------------- ...

  4. linux 的常用命令---------第十一阶段

    软件管理rpm.yum 在 windows 与 linux 之间 实现小文件传输(仅支持在 X shell 中完成文件传输,虚拟机中不可实现): # yum install  lrzsz  -y    ...

  5. Web应用程序使用说明

    目录 Web应用程序使用说明 1.组织权限概述 a)概述 b)组织权限设置 2.门户的使用 门户-栏目门户配置 3.安全策略功能使用说明 一.概述 二.安全策略设置 Web应用程序使用说明 1.    ...

  6. (转)解决k8s集群提示docker login问题(同样适用于Rancher)

    文章转自 https://blog.liv1020.com/ 参考文档:https://kubernetes.io/docs/concepts/containers/images/#configuri ...

  7. [转]Qt 之 QFileSystemWatcher

    简述 QFileSystemWatcher类用于提供监视文件和目录修改的接口. QFileSystemWatcher通过监控指定路径的列表,监视文件系统中文件和目录的变更. 调用addPath()函数 ...

  8. 六大主流开源SQL引擎

    导读 本文涵盖了6个开源领导者:Hive.Impala.Spark SQL.Drill.HAWQ 以及Presto,还加上Calcite.Kylin.Phoenix.Tajo 和Trafodion.以 ...

  9. 关于java中BufferedReader的read()及readLine()方法的使用心得

    BufferedReader的readLine()方法是阻塞式的, 如果到达流末尾, 就返回null, 但如果client的socket末经关闭就销毁, 则会产生IO异常. 正常的方法就是使用sock ...

  10. AI 正则化

    正则化,是减少泛化误差的技术.