在使用webargs作为参数验证时,如何在验证键值时引入其他的键作为验证条件(已终结,谢谢)


#1

1:第十章 待办事项程序,p466中介绍了使用webargs作为大型程序的字段验证
2:在对一个键使用自定义验证规则时如何引入其它键值作为验证条件
3:验证场景在对用户信息进行修改操作时,需要根据用户的id来修改用户名称等信息,其中用户名称是需要做全局唯一的验证,但在验证的时在做全局唯一验证的时候要刨除自己本身,也就是查询语句User.query.filter_by(username=‘test’).filter(User.id!=1).first()
具体伪代码如下

from webargs import fields, validate, ValidationError

def must_not_exist_in_db(val):
    if User.query.filter_by(username=val).first():
        raise ValidationError("User does not exist")

user_update = {
    "id": fields.Int(required=True),
    "username": fields.Str(required=True, validate=must_not_exist_in_db),
    "password": fields.Str(validate=lambda p: len(p) >= 6),
}

@app.route("/update", methods=["POST"])
@use_args(user_update ) 
def update(args):
    .......
    return True

例如在上述代码中如果需要对username做自定义的验证时(must_not_exist_in_db),如何引入其他的键值,或者有什么办法达到以下目的

def must_not_exist_in_db(val):
    if User.query.filter_by(username=val).filter(User.id!=id).first():
        raise ValidationError("User does not exist")

目的是引用验证时其他的键


#2

一般这种情况,我是在视图函数里处理的了。

@app.route("/update", methods=["POST"])
@use_args(user_update ) 
def update(args):
    .......
    # 在这里进行自定义验证。
    return True

webargs 对我来说主要还是提取正确字段,后面的数据验证等,可放到视图函数里,或结合marshmallow使用。

如果发现更好的办法我会补充的。


#3

感谢


#4

看了下官方文档,webargs是可以配置全局验证的。

我根据官方示例写了个Demo,看看是否符合你要求

# coding=utf-8

from flask import Flask
from webargs import fields
from webargs.flaskparser import parser

app = Flask(__name__)


@app.route('/')
def index():
    argmap = {"age": fields.Int(), "years_employed": fields.Int()}
    result = parser.parse(
        argmap,
        # 配置全局验证,返回 False 或抛出 ValidationError 异常表示验证失败
        validate=lambda args: args["years_employed"] < args["age"]
    )
    print(result)
    return ''


if __name__ == "__main__":
    # app.run()
    with app.test_client() as client:
        response = client.get('/?age=1&years_employed=2')
        print(response)  # <Response streamed [422 UNPROCESSABLE ENTITY]>
        assert response.status_code == 422

        response2 = client.get('/?age=2&years_employed=1')
        print(response2)  # <Response streamed [200 OK]>
        assert response2.status_code == 200


#5

首先十分感谢你的回复,还特意查了文档。
其实我之前也看到了这部分文档,但是有一个问题,就是在项目中做参数验时,为了保证代码风格的一致性,肯定要统一编码方式,装饰器的方式是首选,如果再引入

argmap = {"age": fields.Int(), "years_employed": fields.Int()}
result = parser.parse(
        argmap,
        # 配置全局验证,返回 False 或抛出 ValidationError 异常表示验证失败
        validate=lambda args: args["years_employed"] < args["age"]
    )

虽然可以解决需要全局验证的问题,但是又不够优雅。
其实该问题在wtforms的方式就很友好,是你在验证当前字段的同时,也可以使用其他字段

class AccountLoginForm(BaseForm):
    username = StringField(validators=[DataRequired()])
    password = StringField(validators=[DataRequired()])

    def validate_account(self, value):
        user = User.query.filter_by(username=value.data).first()
        if not user:
            raise ParameterException(msg='账户不存在')

自定义验证规则时,既可以验证当前字段,还可以通过self来使用其他字段辅助验证,但wtforms对相对复杂格式的json验证支持不是很友好,总之好像都差那么一点:rofl: