Как работает get_FIELD_display (в django)?

Я новичок в Django и Python и недавно наткнулся на ряд методов в документах Django, таких как Model.get_FOO_display(). На странице справки говорится, что вы можете заменить FOO на имя поля. Я пытался выяснить, как это возможно в Python и заглядывает в класс "Модель". Там я натолкнулся на это:

    def _get_FIELD_display(self, field):
        value = getattr(self, field.attname)
        return force_unicode(dict(field.flatchoices).get(value, value), strings_only=True)

Я не могу понять, как это возможно в Python: 1), чтобы написать это

class Person(models.Model):
    gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
p = Person(name='John', gender='M')

p.get_gender_display()

Итак, как Python "изменяет" FIELD (или FOO) на пол? Это какая-то "злая" нотация Питона? (Извините, я не могу спросить более четко) Coud вы указываете на какую-то страницу руководства Python?

2) почему источник из класса Model объявляет _get_FIELD_display() с лидирующим подчеркиванием, но извлечение извлечения из Python из вышеперечисленного записывает "p.get_gender_display()" без подчеркивания? Опять же, не могли бы вы дать для этого страницу руководства Python?

Ответы

Ответ 1

Все это управляется через метакласс - вы увидите в источнике, что класс Model определяет атрибут __metaclass__, который установлен на ModelBase. Метакласс относится к классу, так как класс относится к экземпляру.

Таким образом, метаклас вызывается, когда определен класс Django. Затем метаклас выполняет различный код, который определяет свойства класса модели. Таким образом, он выполняет итерацию через поля, объявленные в классе модели, и присваивает каждому атрибут экземпляра - таким образом работает декларативный синтаксис Django. Затем он вызывает каждый метод contribute_to_class, который добавляет к самому классу любые специфичные для поля методы, и одним из этих методов является метод _get_FOO_display. Вы можете увидеть источник для этого в django/db/models/fields/__init__.py.

Чтобы изменить _get_FIELD_display на get_myfieldname_display, он использует метод, называемый "currying", что означает, что он определяет новую функцию, которая применяет оригинальный метод с определенным параметром - в этом случае имя поле.

Если вам интересно все это, книга Марти Алчина Pro Django имеет отличное объяснение.

Ответ 2

Магический метод __getattr__ для классов может использоваться для реализации такого рода функций. __getattr__ вызывается для каждого obj.attribute, где attribute не существует на obj, и реализация может вернуть все, что пожелает.