为什么克隆FloatField的此子类(带有添加的验证器)会引发异常?

时间:2018-06-20 15:26:59

标签: python django django-models

我是Python和Django的新手。

我希望我的模型具有范围验证的浮点数。我从this answer写到:

class FloatRangeField (FloatField):
    """A FloatField constrained to a given range."""

    def __init__ (self, minimum, maximum, **kwargs):

        minmax = [MinValueValidator (minimum), MaxValueValidator (maximum)]

        print ("\n\t\tFloatRangeField({},{})".format(minimum,maximum)) # (A)
        FloatField.__init__ (self, validators = minmax, **kwargs)
        print ("\t\tFINISHED\n")

这在python3 manage.py migrate中引起错误,我将其范围缩小到clone()调用。演示:

print ("HERE 1")
tmp1 = FloatRangeField (10, 20)
print ("HERE 2")
tmp2 = FloatRangeField (10, 20)
print ("HERE 3")
tmp3 = tmp1.clone ()      # (B)
print ("HERE 4")

它从第# (B)行引发异常。奇怪的是,发生这种情况时, not # (A)上的迹线未打印。输出如下:

HERE 1

                FloatRangeField(10,20)
                FINISHED

HERE 2

                FloatRangeField(10,20)
                FINISHED

HERE 3
Traceback (most recent call last):
  File "manage.py", line 15, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.5/dist-packages/django/core/management/__init__.py", line 371, in execute_from_command_line
    utility.execute()

[...snip...]

    tmp3 = tmp1.clone ()
  File "/usr/local/lib/python3.5/dist-packages/django/db/models/fields/__init__.py", line 470, in clone
    return self.__class__(*args, **kwargs)
TypeError: __init__() missing 2 required positional arguments: 'minimum' and 'maximum'

# (A)上什么都没打印出来,这与我在migrate期间看到的错误相同。如果我取出tmp1=...的东西并运行migrate,则回溯看起来像这样

Traceback (most recent call last):
  File "/usr/local/lib/python3.5/dist-packages/django/db/migrations/state.py", line 411, in from_model
    fields.append((name, field.clone()))
  File "/usr/local/lib/python3.5/dist-packages/django/db/models/fields/__init__.py", line 470, in clone
    return self.__class__(*args, **kwargs)

[...snip...]

    FloatField.__init__ (self, validators = minmax, **kwargs)
TypeError: __init__() got multiple values for keyword argument 'validators'

为什么会这样?为什么我应该实现FloatRangeField以便封装具有最小/最大验证器的子包?

1 个答案:

答案 0 :(得分:2)

像写在documentation on custom model fields中一样:

  

如果添加新的关键字参数,则需要编写代码以自己将其值放入kwargs中(...)

因此,您还应该添加一个解构函数。这是必需的,例如在迁移文件中表示此字段:

class FloatRangeField (FloatField):
    """A FloatField constrained to a given range."""

    def __init__ (self, minimum, maximum, **kwargs):
        self.minimum = minimum
        self.maximum = maximum
        minmax = [MinValueValidator (minimum), MaxValueValidator (maximum)]
        FloatField.__init__ (self, validators = minmax, **kwargs)

    def deconstruct(self):
        result = __, __, __, kwargs = super(FloatRangeField, self).deconstruct()
        kwargs['minimum'] = self.minimum
        kwargs['minimum'] = self.maximum
        del kwargs['validators']
        return result

请注意,最好不要使用validators = minmax作为参数,因为这意味着如果用户将validator的构造函数使用FloatRangeField参数,则会发生参数冲突

例如,可以将我们的minmax验证器追加到已经存在的验证器中,然后在我们想对其进行解构时将它们从验证器中弹出:

class FloatRangeField (FloatField):
    """A FloatField constrained to a given range."""

    def __init__ (self, minimum, maximum, **kwargs):
        self.minimum = minimum
        self.maximum = maximum
        old_validators = kwargs.get('validators', [])
        minmax = [MinValueValidator (minimum), MaxValueValidator (maximum)]
        kwargs['validators'] = minmax + old_validators
        FloatField.__init__ (self, **kwargs)


    def deconstruct(self):
        result = __, __, __, kwargs = super(FloatRangeField, self).deconstruct()
        kwargs['minimum'] = self.minimum
        kwargs['minimum'] = self.maximum
        kwargs['validators'] = kwargs['validators'][2:]
        return result

因此在这里,我们在deconstruct(..)函数中,删除了前两个验证器(在__init__(..)函数中添加的验证器)。