这种行为是否记录在Django的外键字段验证器中?

时间:2016-08-02 04:46:45

标签: python django

这是一个示例Python 2代码:

from django.db import models

def my_validator(value):
    assert isinstance(value, (int, long))

class Foo(models.Model):
    name = models.CharField(...) # irrelevant here

class Bar(models.Model):
    name = models.CharField(...) # irrelevant here
    foo = models.ForeignKey(Foo, validators=[my_validator])

如果我创建了一个Foo实例,然后是一个Bar实例(分配foo实例),然后验证,则此代码通过:要验证的FK值不是模型实例而是ID(默认为整数) ):

foo = Foo.objects.create(name='foo')
bar = Bar.objects.create(name='bar', foo=foo)

修改:我忘记加入full_clean()来电。但是是的:麻烦的代码调用full_clean()。事实上,我第一次注意到这种行为是在尝试将验证器中的value可调用时,作为模型实例而不是原始值,在尝试调用实例时触发了int value has no attribute xxx验证器内部的方法。

bar.full_clean()

这在Django 1.9中发生。这是记录和预期的吗?

2 个答案:

答案 0 :(得分:3)

是的 - ForeignKey.to_field的文档中隐含地提到了这一点:

  

关系所涉及的相关对象上的字段。默认情况下,Django使用相关对象的主键。

此外:

  

对于映射到模型实例的ForeignKey等字段,默认值应该是它们引用的字段的值(pk,除非设置了to_field)而不是模型实例。

也就是说,默认情况下,value的{​​{1}}是相关对象的主键 - 即整数。

但是,您可以指定其他ForeignKey,在这种情况下,to_field将采用该字段的类型。

就传递给验证器的值而言,似乎隐含的假设是value(除了将要存储在数据库中的值之外,您还要验证什么?在验证外键时传递模型对象没有多大意义,因为键本身只是指向对象的指针,并没有说明该对象应该是什么。)。

但要回答你的问题 - 似乎没有任何明确的文件说明这一点。

答案 1 :(得分:1)

我不确定@ solarissmoke 答案是否与问题相关。

IMO,objects.create未在ModelForm调用,如果您想在创建模型之前验证模型,则应使用foo = Foo.objects.create(name='foo') bar = Bar(name='bar', foo=foo) try: bar.full_clean() bar.save() except ValidationError as e: # Do something based on the errors contained in e.message_dict. # Display them to a user, or handle them programmatically. pass 或手动调用。

clean_fields

更新

好的,那么究竟发生了什么,当你致电validation时,我们会调用.full_clean()

raw_value = getattr(self, f.attname) if f.blank and raw_value in f.empty_values: continue try: setattr(self, f.attname, f.clean(raw_value, self)) except ValidationError as e: errors[f.name] = e.error_list 内,我们有类似的内容:

raw_value

发生两件事:

  1. 我们获得了field.clean字段
  2. 我们致电field.clean()
  3. validate()我们按此顺序调用了.clean_fields().run_validators()value = self.to_python(value) self.validate(value) self.run_validators(value) return value ,其内容如下:

    int/long

    Django在这里解释:.to_python()

    但是,这不是您在自定义validator中获得ForeignKey的原因。

    原因是因为_id字段Form and field validation位于结尾有f.attname的属性中,等于FKs。因此,在验证int/long的整个过程中,Django使用exists值,而不是使用对象。

    如果您看到store their values方法,则会发现它只会检查具有该ID Disk Space Usage的行。