Django:为什么Django模型字段是类属性?

19 投票
1 回答
3030 浏览
提问于 2025-04-16 17:19

在Django中,一个模型的字段是作为类的属性来定义的。

这是不是意味着模型的所有实例都会共享这些字段的相同值呢?

假设我有一个模型

class Tag(models.Model):
    name = models.CharField(max_length=30)

而且我有一个表单,用户可以在里面提交标签。比如说,一个用户提交了两个标签:“Python”和“Django”。如果我在我的视图中创建两个Tag的实例:

t1 = Tag(name="Python")
t2 = Tag(name="Django")

因为name是一个类属性,所以t1t2name值不应该都是“Django”吗?

但实际上,name的表现更像是实例属性,而不是类属性。你能解释一下这是怎么回事吗?

1 个回答

21

不,这里有个原因:

>>> class Foo(object):
...     bar = 'Foo attribute'
...
>>> f = Foo()
>>> f.bar
'Foo attribute'
>>> Foo.bar
'Foo attribute'
>>> f.bar = 'instance attribute'
>>> f.bar
'instance attribute'
>>> Foo.bar
'Foo attribute'

当你给一个对象添加属性时,如果这个属性和类里已有的属性同名,那么对象的属性会“覆盖”类里的属性。不过,当你查找属性时,如果这个对象没有定义这个属性,就会返回类里的那个属性。

在Django中,这些类属性被ORM层用来生成将要转换成SQL查询和操作的机制(背后有很多复杂的元类魔法在运作)。

编辑: 为了回答你的问题——

要理解这一点,你需要了解一点Python的数据模型。简单来说,类和对象都有各自的命名空间。如果你查看它们的特殊属性__dict__,就能看出来:

>>> print Foo.__dict__
{'__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute
'__weakref__' of 'Foo' objects>, '__module__': '__main__', 'bar': 'Foo attribute
', '__doc__': None}
>>> f = Foo()
>>> print f.__dict__
{}

当对象f第一次被创建时,它的命名空间是空的。当你查找f.bar时,这个命名空间(实际上是一个字典)会被查找。因为在这里找不到'bar'这个属性,所以会去查找f的类Foo。我们在那儿找到了'bar': 'Foo属性'。所以这就是将要返回的内容:

>>> f.bar
'Foo attribute'

现在,当你给对象赋值一个属性,而这个属性名在它的命名空间里还不存在时,就会创建这个属性:

>>> f.bar = 'instance attribute'
>>> print f.__dict__
{'bar': 'instance attribute'}
>>> f.bar
'instance attribute'

现在,你知道下次查找f.bar会发生什么了!f.__dict__['bar']存在,并且会在我们查看Foo的命名空间之前就被返回。

当然,如果你想始终访问和操作类的属性,而不是实例的属性,你需要使用类的名字。

>>> Foo.bar
'Foo attribute'
>>> Foo.__dict__['bar']
'Foo attribute'

撰写回答