auto_now 字段的模型日期时间字段验证

Model datetime field validation for fields with auto_now

总的来说,我对 django 和 python 还很陌生,我正在尝试学习 rest_framework 来创建 RESTful API。

所以我有这样的模型:

class Listing(models.Model):
    listingid           = models.BigIntegerField(primary_key=True)
    sellerid            = models.IntegerField()
    createdon           = models.DateTimeField(auto_now_add=True, editable=False)
    expirydate          = models.DateTimeField(null=True)
    validationstatus    = models.SmallIntegerField(default=0)
    listingstatus       = models.SmallIntegerField(
        choices=((0, 'Active'),
            (1, 'Hidden'),
            (2, 'Suspended'),
            (4, 'Expired'),
            (5, 'Deleted'),
            ), 
        default=0)

现在我需要验证 expirydate 总是大于 createdon 日期。

我知道我可以在视图中执行此操作,但我想这不是一个好主意,因为现在验证仅存在于视图中。
所以剩下的就是序列化器和模型了。

我知道我可以覆盖 save 方法来像这样检查:

class MasterListing(models.Model):
    # fields here..

    def save(self, *args, **kwargs):
        if self.expirydate > self.createdon:
            super().save(*args, **kwargs)
        return ValidationError("Expiry date cannot be greater than created date ("++")")

但我不知道这是否是个好主意,因为现在我提出了一个程序员可能忘记捕获的错误。我也不确定当此方法 运行.
时是否会填充字段 我在文档中读到的另一种方法是 clean 方法,我不太了解它。

谁能指导我在使用 rest_framework 时如何处理此类情况?
到目前为止,我已经阅读了一些关于验证的内容:

  1. 序列化器验证
    • 字段级验证
    • 验证者
  2. 模型验证
    • 覆盖clean方法
    • 覆盖save方法
  3. 只需在视图中手动完成

好像有很多选项,我可能还留了几个,我也分不清什么时候用在什么地方。 我很抱歉,如果这有点初学者水平,但我是框架的新手,django 似乎与我在 PHP 中所做的非常不同。欢迎任何建议!

编辑:我将只为 rest_framework 使用 django,因为我们只想构建 RESTful API。

如果您想确保您的数据在最低级别上有效,请使用模型验证(它应该是 运行 由序列化程序 class 以及(模型)形式 classes(例如管理员))。

如果您希望仅在 API/forms 中进行验证,请将其放入 serializer/form class 中。所以放置验证的最佳位置应该是 Model.clean().

验证不应该真正发生在视图中,因为它们不应该变得过于臃肿,真正的业务逻辑应该封装在模型或表单中。

Django REST framework 用于调用 Model.clean,这是以前推荐的放置需要在 Django 表单和 DRF 序列化器中使用的验证逻辑的地方。 从 DRF 3.0 开始,this is no longer the case and Model.clean will no longer be called during the validation cycle。有了这个变化,现在有两个可能的地方可以放置适用于多个字段的自定义验证逻辑。

如果您仅使用 Django REST 框架进行验证,并且您没有任何其他需要手动验证数据的区域(例如 ModelForm ,或在 Django 管理员中),那么你应该查看 Django REST framework's validation framework.

class MySerializer(serializers.ModelSerializer):
    # ...

    def validate(self, data):
        # The keys can be missing in partial updates
        if "expirydate" in data and "createdon" in data:
            if data["expirydate"] < data["createdon"]:
                raise serializers.ValidationError({
                    "expirydata": "Expiry date cannot be greater than created date",
                })

        return super(MySerializer, self).validate(data)

如果您需要将 Django REST 框架与使用模型级验证的 Django 组件结合使用(如 Django 管理员),您有两个选择。

  1. Model.cleanSerializer.validate 中重复你的逻辑,违反了 DRY principle 并为未来的问题敞开心扉。
  2. Model.save 中进行验证,希望以后不会发生任何异常情况。

but I dont know if this would be a good idea, since now I am raising an error which the programmer may forget to catch.

我敢说与其让保存的数据故意变得无效,不如引发错误更好。一旦开始允许无效数据,就必须检查任何使用数据的地方来修复它。如果您不允许它进入无效状态,您就不会 运行 进入该问题。

I am also not sure if the fields would be populated when this method would run.

您应该能够假设,如果要保存一个对象,这些字段已经填充了它们的值。

如果您想使用 Django REST Framework 3.0 进行模型验证和序列化程序验证,您可以强制您的序列化程序像这样使用模型验证(这样您就不会重复自己):

import rest_framework, django
from rest_framework import serializers

class MySerializer(serializers.ModelSerializer):
    def validate(self, data):
        for key, val in data.iteritems():
            setattr(self.instance, key, val)
        try:
            self.instance.clean()
        except django.core.exceptions.ValidationError as e:
            raise rest_framework.exceptions.ValidationError(e.message_dict)

        return data

我考虑过从模型的 clean() 函数代码中生成一个新函数,并根据参数 source(或东西)到功能。然后我会从模型和序列化程序中调用它。但这对我来说似乎并不好。