Python 返回修改的类和使用类型()之间的差异

Python 返回修改的类和使用类型()之间的差异,python,django,class,types,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 = emplo

我想这更像是一个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__))
[发件人:]

还有一件事我不明白,为什么返回修改过的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()的结果效果非常好。

我只是用非django类尝试了这个方法,结果很好。所以这不是Python的问题,而是Django的问题

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


因此,您需要创建一个新类,就像type()语句所做的那样,这样元类的类创建代码就包含在新字段中了。

Lennart的假设是正确的:元类确实是罪魁祸首。无需猜测,只需查看:元类当前位于该文件的第53行,它是
DeclarativeFieldsMetaclass
,并根据类在创建时具有的属性添加属性
base_字段
,可能还添加了属性
media
。在第329行ff处,您可以看到:

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
这意味着用base
type
创建一个新类时存在一些脆弱性——所提供的黑魔法可能会执行,也可能不会执行!一种更可靠的方法是使用
EmployeeForm
类型,它将获取可能涉及的任何元类,即:

(顺便说一句,无需复制该文件)。区别很微妙但很重要:我们不是直接使用
type
的3-args表单,而是使用1-arg表单获取表单类的类型(即元类),然后在3-args表单中调用该元类


的确,这是一个非常神奇的想法,但这就是框架的缺点,它使用“纯粹为了语义糖的奇特元类东西”&c:只要你想做框架所支持的事情,你就可以随心所欲,但要想摆脱这种支持,哪怕是一点点也可能需要抵消魔法(这在某种程度上解释了为什么我更喜欢使用轻量级、透明的设置,比如werkzeug,而不是像Rails或Django那样给我带来魔力的框架:我对深度黑魔法的掌握并不意味着我很乐意在简单的生产代码中使用它……但是,这是另一个讨论;-).

值得注意的是,这个代码片段是达到预期目的的一个非常糟糕的方法,并且涉及到对Django表单对象的一个常见误解,即表单对象应该与HTML表单一一对应。正确的方法是这样做(不需要任何元类魔法)是使用多个窗体对象和一个


或者,如果出于某种奇怪的原因,你真的想把东西保存在一个表单对象中,只需在表单的_init__方法中操作self.fields。

formset\u工厂创建同一表单的多个实例,并透明地保存它。这个示例根据员工部门在表单上显示不同的字段。我不确定我是否理解w当你说前者可以用于后者时,你是什么意思?你能详细说明一下吗?提前谢谢。如果你的目的是显示一个html表单,允许编辑一个部门的所有员工的姓名,那么内联表单集就是你想要的。然而,如果你想显示某个部门的佣金,而不是其他部门的佣金,这是一种方式。两者都是我认为dict(EmployeeForm.\uuu dict\uuu)的原因是早期的表单。表单用于返回DictProxy而不是dict。
return type(EmployeeForm)('EmployeeForm', (forms.Form, ), EmployeeForm.__dict__)