python - Django DRY 模型/表单/序列化程序验证

标签 python django validation serialization django-rest-framework

我在确定在 Django 中引入验证逻辑的最佳(阅读:DRY & maintainable)位置时遇到了一些问题,即在模型、表单和 DRF 序列化程序之间。

我使用 Django 多年,一直遵循处理模型、表单和 REST API 端点验证的各种约定。我已经尝试了很多变体来确保整体数据完整性,但最近我遇到了一些绊脚石。以下是我在浏览许多文章、SO 帖子和票证后尝试过的内容的简要列表:

  1. 模型级别的验证;即,通过覆盖 myModel.clean()(以及特定于字段和唯一的方法),确保在调用 myModel.save() 之前匹配我的所有自定义约束.为此,我确保在 myForm.clean() 中调用了 myModel.full_clean()(对于表单——管理面板实际上已经这样做了)和 mySerializer.validate()(用于 DRF 序列化程序)方法。

  2. 在表单和序列化程序级别进行验证,为可维护的 DRY 代码调用共享方法。

  3. 在表单和序列化程序级别进行验证,分别使用不同的方法来确保最大的灵 active (即当表单和端点具有不同的约束时)。

当表单和序列化程序具有相同的约束时,方法一对我来说似乎是最直观的,但在实践中有点困惑;首先,数据由表单或序列化程序自动清理和验证,然后实例化模型实体,再次运行更多验证——这有点复杂,可能会变得复杂。

方法三是 Django Rest Framework 从 3.0 版本开始推荐的方法;他们消除了很多 model.save() Hook ,而更愿意将验证留给应用程序面向用户的方面。这对我来说有些意义,因为 Django 的基础 model.save() 实现无论如何都不会调用 model.full_clean()

所以,方法二对我来说似乎是最好的总体概括结果;验证存在于一个不同的地方——在模型被触及之前——并且由于共享验证逻辑,代码库不那么困惑/更干。

不幸的是,我遇到的大部分麻烦都是让 Django Rest Framework 的序列化程序协作。这三种方法都适用于表单,而且实际上适用于大多数 HTTP 方法(最明显的是在创建实体时使用 POST 方法)——但在更新现有实体(PUT、PATCH)时似乎没有一个效果很好。

长话短说,当输入数据不完整时(但在其他方面有效——PATCH 通常是这种情况),事实证明验证输入数据相当困难。请求数据可能只包含一些字段——那些包含不同/新信息的字段——并且为所有其他字段维护模型实例的现有信息。事实上,DRF issue #4306完美地总结了这一特殊挑战。

我还考虑过在 View 集级别运行自定义模型验证(在填充 serializer.validated_data 并且 serializer.instance 存在之后,但在调用 serializer.save() 之前),但我仍在努力想出由于处理更新的复杂性,一种干净、通用的方法。

TL;DR Django Rest Framework 使得在明显的地方编写干净、可维护的验证逻辑有点困难,尤其是对于依赖于现有模型数据和传入请求数据混合的部分更新.

我很乐意让一些 Django 专家对他们的工作进行权衡,因为我没有看到任何方便的解决方案。

谢谢。

最佳答案

刚刚意识到我从来没有将我的解决方案发回给这个问题。我最终写了一个模型 mixin 以在保存之前始终运行验证;这有点不方便,因为从技术上讲,验证将在 Django 的表单中运行两次(即在管理面板中),但它让我保证运行验证——无论什么触发模型保存。我一般不使用Django的表单,所以这对我的应用影响不大。

这里有一个简单的片段可以解决这个问题:

class ValidatesOnSaveModelMixin:
    """ ValidatesOnSaveModelMixin
    A mixin that ensures valid model state prior to saving.
    """
    def save(self, **kwargs):
        self.full_clean()
        super(ValidatesOnSaveModelMixin, self).save(**kwargs)

以下是您将如何使用它:

class ImportantModel(ValidatesOnSaveModelMixin, models.Model):
    """ Will always ensure its fields pass validation prior to saving. """

有一个重要的警告:Django 的任何直接到数据库的操作(即 ImportantModel.objects.update())不要调用模型的 save() 方法,因此不会被验证。对此没什么可做的,因为这些方法实际上是通过跳过一堆数据库调用来优化性能——所以如果您使用它们,请注意它们的影响。

关于python - Django DRY 模型/表单/序列化程序验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40697648/

相关文章:

security - 你什么时候信任数据/变量

java - 验证多个 edittext 并显示 edittext.setError ("String");

python - igraph:为什么 add_edge 函数与 add_edges 相比这么慢?

在 Windows 10 中以编程方式运行 python map 驱动器

python - 类型错误 : get() got multiple values for argument 'task_id'

python - Django admin datetime `now` 按钮选择正确的本地时间,但服务器时间错误

django - 解读Django服务器请求日志

Python - 无法安装包 : TypeError: unorderable types: NoneType() >= str()

Python - 返回具有多个整数列表匹配的整数范围的元组

postgresql - 存储月份的数据类型或约束