【问题标题】:Model datetime field validation for fields with auto_now使用 auto_now 对字段进行模型日期时间字段验证
【发布时间】:2015-03-19 15:18:33
【问题描述】:

总的来说,我对 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. 只需在视图中手动完成

似乎有很多选择,我什至可能留下了一些,我无法清楚地知道何时使用 where。 如果这有点初级,我很抱歉,但我是框架的新手,django 似乎与我在 PHP 中所做的非常不同。欢迎任何建议!

编辑:我将仅将 django 用于 rest_framework 而没有其他用途,因为我们只想构建 RESTful API。

【问题讨论】:

    标签: django datetime python-3.x django-rest-framework


    【解决方案1】:

    如果您想同时使用 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(或其他东西)将其吐出 django.core.exceptions.ValidationErrorrest_framework.exceptions.ValidationError。然后我会从模型和序列化程序中调用它。但这对我来说几乎没有更好的选择。

    【讨论】:

    • 我不明白...这是答案还是评论?
    • 我想这应该是对 Kevin Brown 的评论,是的。但我没有看到在那里添加代码块的可能性。无论如何,我希望你能原谅我,因为这是我在 stackoverflow 上的第一篇文章 :) 谢谢你的链接安迪。
    • 我认为偶尔用这么多代码的“评论”应该被允许作为“答案”,因为没有其他方法可以发布。但这种情况很少见,而且应该很少见。
    【解决方案2】:

    Django REST framework 曾经调用Model.clean,这是以前推荐的放置需要在 Django 表单和 DRF 序列化程序中使用的验证逻辑的地方。 从 DRF 3.0 开始,this is no longer the caseModel.clean 将不再称为 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 中进行验证,希望以后不会发生任何奇怪的事情。

    但我不知道这是否是个好主意,因为现在我提出了一个程序员可能忘记捕获的错误。

    我敢说最好提出错误,而不是故意让保存的数据变得无效。一旦您开始允许无效数据,您必须在使用数据的任何地方进行检查以修复它。如果您不允许它进入无效状态,则不会遇到该问题。

    我也不确定在运行此方法时是否会填充这些字段。

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

    【讨论】:

    • 所以我想最好将它保存到模型save() 方法中,因为模型将被不同的序列化程序使用,从而保持 DRY?
    【解决方案3】:

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

    如果您希望仅在您的 API/表单中进行验证,请将其放入序列化程序/表单类中。所以验证的最佳位置应该是Model.clean()

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

    【讨论】:

    • 好吧.. 但是如果我的序列化程序没有被其他不在我的模块上工作的人调用怎么办?管理它的最佳方法是什么?..
    • 猜猜唯一的解决方案是确保始终执行验证(同样在直接调用save() 时)是把它放在你提到的save() 方法中......
    • 模型级别验证 (clean/full_clean)仅由 Django 表单和管理界面调用。 Django REST framework 在 3.0 中不再调用它。
    • @KevinBrown 在紧要关头救了我。我正要试验clean,我什至可能都没有检测到这个错误:D
    • 感谢@KevinBrown,仍在使用较旧的 DRF 版本。这是文档中的相关部分:github.com/tomchristie/django-rest-framework/blob/…
    猜你喜欢
    • 2013-10-22
    • 2018-02-24
    • 1970-01-01
    • 2012-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多