Flask-论坛开发-4-知识点补充

Posted myyd

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flask-论坛开发-4-知识点补充相关的知识,希望对你有一定的参考价值。

对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 ‘上传失败!‘

设置 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()

删除 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()

设置 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-知识点补充的主要内容,如果未能解决你的问题,请参考以下文章

python——基础补充(总结廖雪峰论坛知识点)

Flask

Flask企业级论坛实战目录导航

Flask企业级论坛实战目录导航

express开发上手记录(3)知识补充:http请求和响应

Flask项目-新闻网站搭建!