get_FIELD_display(在Django中)是如何工作的?
我刚开始学习Django和Python,最近在Django的文档中看到了一些方法,比如Model.get_FOO_display()。帮助页面上说,你可以把FOO替换成某个字段的名字。我一直在想这在Python中是怎么实现的,还查看了'Model'类的源代码。
def _get_FIELD_display(self, field):
value = getattr(self, field.attname)
return force_unicode(dict(field.flatchoices).get(value, value), strings_only=True)
我不太明白在Python中怎么能做到以下几点:
class Person(models.Model):
gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
p = Person(name='John', gender='M')
p.get_gender_display()
1) 怎么能写成这样
那么,Python是怎么把FIELD(或者FOO)变成gender的呢?这是不是某种Python的“神奇”写法?(抱歉,我问得不够清楚)你能给我指个Python的手册吗?
2) 为什么Model类的源代码中声明了_get_FIELD_display(),前面有个下划线,而上面提到的Python文档中却写成“p.get_gender_display()”,没有下划线?能不能再给我一个Python的手册链接?
2 个回答
在类里面,有一个神奇的方法叫做 __getattr__,可以用来实现这种功能。当你访问一个对象的属性,比如 obj.attribute
,如果这个属性在 obj
里面不存在,就会自动调用 __getattr__
方法。这个方法可以返回任何你想要的东西。
这一切都是通过元类来管理的。你会在源代码中看到,Model类定义了一个__metaclass__
属性,这个属性被设置为ModelBase
。元类就像是类对实例的关系。
所以,当一个Django类被定义时,就会调用元类。元类会执行一些代码,来确定这个模型类的属性。它会遍历模型类中声明的字段,并把每个字段作为实例属性来分配——这就是Django声明式语法的工作方式。接着,它会调用每个字段的contribute_to_class
方法,这个方法会把特定于字段的方法添加到类本身上,其中一个方法就是_get_FOO_display
方法。你可以在django/db/models/fields/__init__.py
中找到这个源代码。
为了把_get_FIELD_display
改成get_myfieldname_display
,它使用了一种叫做“柯里化”的技术,这意味着它定义了一个新函数,这个函数会用一个特定的参数(在这里就是字段的名字)来应用原来的方法。
如果你对这些内容感兴趣,Marty Alchin的书《Pro Django》里有很好的解释。