Гипотеза Леннарта верна: метакласс действительно виновник. Не нужно угадывать, просто посмотрите на источники : метакласс DeclarativeFieldsMetaclass
в настоящее время находится в строке 53 этого файла и добавляет атрибуты base_fields
и, возможно, media
в зависимости от того, какие атрибуты имеет класс время создания. В строке 329 и далее вы видите:
class Form(BaseForm):
"A collection of Fields, plus their associated data."
# This is a separate class from BaseForm in order to abstract the way
# self.fields is specified. This class (Form) is the one that does the
# fancy metaclass stuff purely for the semantic sugar -- it allows one
# to define a form using declarative syntax.
# BaseForm itself has no way of designating self.fields.
__metaclass__ = DeclarativeFieldsMetaclass
Это подразумевает, что существует некоторая хрупкость в создании нового класса с базой type
- поставляемая черная магия может или не может пройти! Более надежный подход заключается в использовании типа EmployeeForm
, который подберет любой метакласс, который может быть задействован - т.е.
return type(EmployeeForm)('EmployeeForm', (forms.Form, ), EmployeeForm.__dict__)
(нет необходимости копировать это __dict__
, кстати). Разница тонкая, но важная: вместо непосредственного использования формы 3-args type
мы используем форму 1-arg, чтобы выбрать тип (т. Е. Метакласс) класса формы, а затем вызвать метакласс TH в Форма 3-args.
На самом деле чертовски волшебно, но в этом-то и обратная сторона фреймворков, в которых используется «причудливый метакласс для чисто семантического сахара» и т. чтобы получить от этой поддержки даже немного, может потребоваться уравновешивающее волшебство (что объясняет, почему часто я предпочел бы использовать легкую, прозрачную настройку, такую как werkzeug, а не среду, которая наполняет меня магией, такой как Rails или Django do: мое мастерство глубокой черной магии НЕ означает, что я счастлив использовать его в простом производственном коде ... но это другое обсуждение; -).