使用auto_now对字段进行模型日期时间字段验证

时间:2015-01-21 07:17:07

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

我对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. 只需在视图中手动执行
  4. 即可

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

    编辑:我将仅使用django进行rest_framework,而不是其他任何东西,因为我们只想构建RESTful API。

3 个答案:

答案 0 :(得分:4)

用于调用Model.clean的Django REST框架,以前是推荐需要在Django表单和DRF序列化程序中使用的验证逻辑的地方。 自DRF 3.0起,this is no longer the case Model.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进行验证,并希望以后不会发生任何奇怪的事。
  3.   

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

    我冒昧地说,提出错误会比保存的数据更有可能变得无效。一旦开始允许无效数据,您必须在使用数据的任何地方进行检查来修复它。如果您不允许它进入无效状态,则不会遇到该问题。

      

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

    您应该可以假设,如果要保存某个对象,那么这些字段已经填充了它们的值。

答案 1 :(得分:2)

如果你想使用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()函数的代码生成一个新函数,并根据参数django.core.exceptions.ValidationError(或其他东西)吐出rest_framework.exceptions.ValidationErrorsource到功能。然后我会从模型和序列化器中调用它。但这对我来说似乎不太好。

答案 2 :(得分:0)

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

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

验证应该永远不会在视图中发生,因为它们不应该过于膨胀,真正的业务逻辑应该封装在模型或表单中。