关于python:为什么Django模型上这个输入错误的字段名称会抛出异常?

Why won't this mistyped field name on a Django model throw an exception?

我有一个型号叫Foo。我可以创建这个模型的新实例,并将其保存到数据库中,即使我分配了一个不存在的字段名。例如:

1
2
3
4
foobar = Foo.objects.create()
foobar.abcdefg = True # The field abcdefg does not exist!
foobar.full_clean()
foobar.save()

当分配给不存在的字段时,Django为什么不抛出异常?我怎样才能让它抛出一个异常,至少在full_clean期间如此?


python允许您在任何实例上几乎存储任何名称的属性。可以阻止这种情况(用C语言编写类)。

它工作的原因是大多数实例将其属性存储在字典中。字典存储在一个名为"dict"的实例属性中。事实上,有些人说"类只是字典的句法糖分。"也就是说,你可以用字典做你能做的任何事情;类只是让它更容易。

在静态语言中,必须在编译时定义所有属性。在Python中,类定义是执行的,而不是编译的;类和其他任何类一样是对象;添加属性和向字典添加项一样容易。这是按设计的。

事实上,唯一的事实是,没有所谓的声明。也就是说,您永远不会声明"这个类有一个方法foo"或"这个类的实例有一个属性栏",更不用说声明要存储在那里的对象的类型了。您只需定义一个方法、属性、类等并添加它。


foobar只是一个类的实例。与任何实例一样,您可以随时添加属性:

1
2
3
4
5
6
7
8
9
>>> class Test(object):
...     myval = 7
...
>>> t = Test()
>>> t.myval
7
>>> t.otherval = 'another'
>>> t.otherval
'another'

(正如flimm在注释中指出的,您可以通过向类中添加__slots__属性来防止这种情况发生)

您可以在full_clean方法中使用self.__dict__来检查外部属性,但您需要能够确定应该存在哪些属性。注意,模型实例有一个_state属性,需要单独使用。

您可以使用此列表理解获得模型上的字段名列表(来自此答案):

1
[f.name for f in self._meta.get_fields()]

因此,只需迭代self.__dict__,如果发现模型字段列表中没有任何键,并且没有以下划线开头,则引发异常。