Hooks

Hook 可以更加容易的扩展 Schema 。它最初的目的是为了让 class 类和一些紧密相关的函数(不是指 method)以更加美观的方式组织在一起。

如下例所示,validate_age 是与 User 相关的验证函数,但它却被定义在了全局域上,虽然毫无问题,但是代码结构并不好看。

def validate_age(value):
    if value < 0:
        raise schema.ValidationError('年龄不能小于0。')

class User(schema.Model):
    name = schema.String()
    age = schema.Integer(validators=[validate_age])

使用 hook 后,相关代码变得更加紧密,并且效果一样。

class User(schema.Model):
    name = schema.String()
    age = schema.Integer()

    @schema.as_validator(age)  # 这是一个 hook
    def validate_age(self, value):
        if value < 0:
            raise schema.ValidationError('年龄不能小于0。')

使用用法

hook 是以装饰器的形式定义在 Schema 类的方法上的。

可用的 Hook

Hook 的特点

不可调用

虽然 hook 修饰的是类的方法,但是该方法并不像普通方法一样可以被调用。即无法使用 <class_name>.<method_name> 的方式进行调用。

class MySchema(schema.Schema):

    @schema.as_validator
    def hook_fn(self, value):
        ...
>>> MySchema.hook_fn
Traceback (most recent call last):
    ...
AttributeError: type object 'MySchema' has no attribute 'hook_fn'

这样做的目的是为了避免同名污染,你可以放心地为你的 hook 函数取任何函数名,它不会覆盖掉已有的同名方法。

不可重写

hook 函数虽然是方法,但是它并不能在子类中被重写。

可继承

父类的 hook 会继承给子类。

class Person(schema.Model):
    firstname = schema.String(write_only=True)
    lastname = schema.String(write_only=True)
    fullname = schema.String()

    @schema.as_getter(fullname)
    def get_fullname(self, data):
        return data['firstname'] + data['lastname']

class Actor(Person):
    works = schema.List()
>>> Actor().serialize({'firstname': '赵', 'lastname': '四', 'works': ['乡村爱情']})
{'fullname': '赵四', 'works': ['乡村爱情']}