返回修改后的类和使用type()之间的区别

时间:2009-08-09 12:28:13

标签: python django class types

我想这更像是一个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 个答案:

答案 0 :(得分:5)

Lennart的假设是正确的:元类确实是罪魁祸首。无需猜测,只需查看the sources:当前位于该文件第53行的元类DeclarativeFieldsMetaclass,并根据属性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__,顺便说一下)。区别是微妙但重要的:我们使用1-arg形式来获取表单类的类型(即元类),然后调用THAT元类,而不是直接使用type的3-args形式。以3-args形式。

确实是黑暗神奇的,但那时框架的缺点就是使用“纯粹用于语义糖的花哨的元类东西”& c:只要你想完全按照框架所支持的那样,你就在三叶草中但是为了摆脱这种支持甚至一点点可能需要反补贴的魔法(这在一定程度上解释了为什么我宁愿使用轻量级,透明的设置,比如werkzeug,而不是像我这样的魔法框架Rails或Django:我对深黑魔法的掌握并不意味着我很乐意在普通的生产代码中使用它......但是,这是另一个讨论; - )。

答案 1 :(得分:3)

我只是尝试使用直接的非django类,并且它有效。所以这不是Python问题,而是Django问题。

在这种情况下(尽管我不是100%肯定),这是一个Form类在创建类时所做的事情的问题。我认为它有一个元类,并且这个元类将在类创建期间完成表单初始化。这意味着在创建类之后添加的任何字段都将被忽略。

因此,您需要创建一个新类,就像使用type()语句一样,以便包含元类的类创建代码,现在使用新字段。

答案 2 :(得分:1)

值得注意的是,此代码段对于所需的结尾来说是一种非常糟糕的方法,并且涉及对Django Form对象的常见误解 - Form对象应该与HTML表单一对一映射。执行此类操作的正确方法(不需要弄乱任何元类魔法)是使用多个Form对象和inline formset

或者,如果由于某种奇怪的原因你真的想要将事物保存在单个Form对象中,只需在Form的__init__方法中操作self.fields。