Разница между возвратом измененного класса и использованием type () - PullRequest
3 голосов
/ 09 августа 2009

Я полагаю, что это больше вопрос о Python, чем о Django, но я не мог повторить это поведение где-либо еще, поэтому я буду использовать точный код, который не работает должным образом.

Я работал над некоторыми динамическими формами в django, когда нашел этот фрагмент фабричной функции:

def get_employee_form(employee):
    """Return the form for a specific Board."""
    employee_fields = EmployeeFieldModel.objects.filter(employee = employee).order_by   ('order')
    class EmployeeForm(forms.Form):
        def __init__(self, *args, **kwargs):
            forms.Form.__init__(self, *args, **kwargs)
            self.employee = employee
        def save(self):
            "Do the save"
    for field in employee_fields:
        setattr(EmployeeForm, field.name, copy(type_mapping[field.type]))
    return type('EmployeeForm', (forms.Form, ), dict(EmployeeForm.__dict__))

[от: http://uswaretech.com/blog/2008/10/dynamic-forms-with-django/]

И есть одна вещь, которую я не понимаю, почему возвращение модифицированного EmployeeForm не помогает? Я имею в виду что-то вроде этого:

def get_employee_form(employee):
    #[...]same function body as before

    for field in employee_fields:
        setattr(EmployeeForm, field.name, copy(type_mapping[field.type]))
    return EmployeeForm

Когда я пытался вернуть измененный класс, django игнорировал мои дополнительные поля, но возвращаемый результат type () работает отлично.

Ответы [ 3 ]

5 голосов
/ 09 августа 2009

Гипотеза Леннарта верна: метакласс действительно виновник. Не нужно угадывать, просто посмотрите на источники : метакласс 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: мое мастерство глубокой черной магии НЕ означает, что я счастлив использовать его в простом производственном коде ... но это другое обсуждение; -).

3 голосов
/ 09 августа 2009

Я только что попробовал это с прямыми классами без джанго, и это сработало. Так что это не проблема Python, а проблема Django.

И в этом случае (хотя я не уверен на 100%), это вопрос того, что делает класс Form во время создания класса. Я думаю, что у него есть мета класс, и что этот мета класс завершит инициализацию формы во время создания класса. Это означает, что любые поля, добавляемые после создания класса, будут игнорироваться.

Следовательно, вам нужно создать новый класс, как это делается с помощью оператора type (), чтобы был задействован код создания класса мета-класса, теперь с новыми полями.

1 голос
/ 09 сентября 2009

Стоит отметить, что этот фрагмент кода является очень плохим средством для достижения желаемой цели и включает в себя общее недопонимание об объектах формы Django - что объект формы должен отображаться один в один с формой HTML. Правильный способ сделать что-то подобное (не требующее вмешательства в магию метаклассов) - использовать несколько объектов Form и встроенный набор форм .

.

Или, если по какой-то странной причине вы действительно хотите сохранить вещи в одном объекте Form, просто манипулируйте self.fields в методе __init__ формы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...