2009-08-09 44 views
3

我猜这是一个比django更多的python问题,但我无法在其他任何地方复制此行为,所以我将使用不起作用的确切代码预期。返回修改后的类和使用类型之间的区别()

我是工作在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的类忽略我的其他字段,但返回类型()的结果完美。

回答

5

Lennart的假设是正确的:元类确实是罪魁祸首。无需猜测,只需查看the sources:元类为DeclarativeFieldsMetaclass,当前在该文件的第53行,并根据该类在创建时的属性添加属性base_fields和可能的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 

这意味着有与基地type创建一个新的类中的一些脆弱性 - 提供的黑魔法可能会或可能不会进行过!更坚实的方法是使用,将拿起可能涉及任何元类的EmployeeForm类型 - 即:

return type(EmployeeForm)('EmployeeForm', (forms.Form,), EmployeeForm.__dict__) 

(无需复制__dict__,顺便说一句)。这个区别很微妙但很重要:我们不是直接使用type的3-args形式,而是使用1-arg形式来获取表单类的类型(即元类),然后在三维表单中调用THAT元类,参数形式。

确实是黑色的神奇英语,但那么这就是框架的缺点,它会使用纯粹用于语义糖的“花哨的元类”& c:只要你想做框架支持的东西, ,但为了摆脱这种支持,即使有一点可能需要反补贴魔术(这有助于解释为什么我经常会使用像werkzeug这样的轻量级,透明的设置,而不是像我这样把魔术放在我身上的框架) Rails或Django做到了:我对黑魔法的掌握并不意味着我很高兴不得不将它用于简单的生产代码......但是,这是另一个讨论;-)。

+0

我认为dict(EmployeeForm .__ dict__)的原因是之前的forms.Form用于返回一个DictProxy而不是一个字典。 – agiliq 2009-10-21 09:32:56

3

我刚刚尝试过这与直接非Django类,它的工作。所以这不是一个Python问题,而是一个Django问题。

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

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

1

值得注意的是,这段代码片段对于期望的结果来说是一种很差的手段,并且涉及到对Django Form对象的常见误解 - Form对象应该与HTML表单一一映射。做这种事情的正确方法(不需要与任何元类魔法混合)是使用多个Form对象和inline formset。或者,如果出于某种奇怪的原因,你真的想把东西保存在一个Form对象中,只需在Form的__init__方法中操作self.fields即可。

+0

formset_factory创建多个相同形式的实例并将其透明地保存。此示例基于员工部门在表单上显示不同的字段。 我不确定当你说前者可以用于后者时,我明白你的意思。 你能详细说一下吗?提前致谢。 – 2009-10-21 09:08:37

+0

如果您的意图是显示一个允许编辑部门所有员工姓名的html表单,则内联formset是您想要的。但是,如果某个部门出现了问题,您希望展示佣金而不是其他人,这是实现这一目标的方法。两者都是正交的,并且可以匹配在一起。 – agiliq 2009-10-21 09:43:05

相关问题