使用MongoEngine文档类方法进行自定义验证和预保存钩子

14 投票
3 回答
16173 浏览
提问于 2025-04-16 18:11

我现在正在研究MongoEngine这个“对象文档映射器”的功能。现在我不太清楚的是,我能在文档对象里放多少验证和对象创建的逻辑。

我感觉这应该没问题,但我找不到很多关于以下问题的例子、注意事项或最佳实践:

  • 自定义的验证函数,这些函数会在调用save()时自动检查字段内容是否有效;
  • 在保存时,基于某个字段内容的哈希值自动生成标识符;

我觉得我需要重写save()方法,这样我才能调用我的自定义逻辑,但由于缺少例子,我开始怀疑这是不是个正确的方法……

如果有任何例子,或者使用mongoEngine的高质量代码库的参考资料,我都非常欢迎。

3 个回答

7

你也可以在Document上重写validate方法,但你需要处理掉父类Document的错误,这样才能把你的错误加进去。

不过,这样做不太好,因为它依赖于MongoEngine内部的实现细节,谁知道将来会不会出问题。

class MyDoc(Document):
    def validate(self):
        errors = {}
        try:
            super(MyDoc, self).validate()
        except ValidationError as e:
            errors = e.errors

        # Your custom validation here...
        # Unfortunately this might swallow any other errors on 'myfield'
        if self.something_is_wrong():
            errors['myfield'] = ValidationError("this field is wrong!", field_name='myfield')

        if errors:
            raise ValidationError('ValidationError', errors=errors)

另外,MongoEngine现在有了合适的信号支持,可以处理其他类型的钩子(比如你在问题中提到的标识符生成)。

http://mongoengine.readthedocs.io/en/latest/guide/signals.html

25

现在,自定义验证应该通过在模型上实现 clean() 方法 来完成。

class Essay(Document):
    status = StringField(choices=('Published', 'Draft'), required=True)
    pub_date = DateTimeField()

    def clean(self):
        """
        Ensures that only published essays have a `pub_date` and
        automatically sets the pub_date if published and not set.
        """
        if self.status == 'Draft' and self.pub_date is not None:
            msg = 'Draft entries should not have a publication date.'
            raise ValidationError(msg)

        # Set the pub_date for published items if not set.
        if self.status == 'Published' and self.pub_date is None:
            self.pub_date = datetime.now()

补充: 不过,使用 clean() 时要小心,因为它是在根据你模型定义中的规则进行验证之前,从 validate() 中调用的。

15

你可以重写 save() 方法,但要记得调用父类的方法,这一点很重要。

如果你发现自己想要在所有模型中添加验证功能,可以考虑创建一个自定义的子类,基于 Document,比如这样:

class MyDocument(mongoengine.Document):

    def save(self, *args, **kwargs):
        for hook in self._pre_save_hooks:
            # the callable can raise an exception if
            # it determines that it is inappropriate
            # to save this instance; or it can modify
            # the instance before it is saved
            hook(self):

        super(MyDocument, self).save(*args, **kwargs)

然后你可以以一种比较自然的方式为特定的模型类定义验证功能:

class SomeModel(MyDocument):
    # fields...

    _pre_save_hooks = [
        some_callable,
        another_callable
    ]

撰写回答