【发布时间】:2012-07-26 20:47:52
【问题描述】:
tl;dr:我正在寻找一种将模型的表单与给定模型相关联的简单方法,以便仅知道模型我可以呈现适当的创建/编辑表单。我已经开发了一个解决方案,将相应的ModelForm 的名称作为字符串属性存储在 Model 类上,但是对于 django 来说是新手,我很好奇这是否是首选解决方案。
我正在开发 django 任务/项目管理站点,该站点将存储许多产品组的任务。导航至mysite/<ProductGroup>/create_task.html 应将用户引导至用于在该产品组内创建任务/项目的表单。默认情况下,这将允许使用简单的ModelForm 编辑基本Task 模型实例。但是,对于特定的产品组,我希望选择将Task 模型子类化(例如SalesTask)并显示特定于该子类的ModeForm(例如SalesTaskForm)。
我目前的解决方案是将任务对象类型作为内容类型存储在ProductGroup 模型中,例如:
class ProductGroup(models.Model):
task_type = models.ForeignKey(ContentType)
...
<define other fields here>
然后定义一个带有特殊字符串属性的基本任务模型,给出相应的ModelForm,以便在渲染时使用,例如:
<models.py>
class Task(models.Model):
product_group = models.ForeignKey(ProductGroup)
...
<define task fields common to all Task subclasses>
...
# Associate model with a form (regular python class attribute,
# not a django field)
form = 'TaskForm'
<forms.py>
class TaskForm(ModelForm):
class Meta:
model = Task
*Note that it would be slightly more convenient if I could set Task.form equal
to the actual TaskForm(ModelForm) class rather than a string, but I couldn't
get around the circular imports when trying this route (models.py `Task`
would need to import `Taskorm` from forms.py, which itself needs to import
`Task`).*
此设置允许我通过简单地继承 Task 和 TaskForm,覆盖模型子类定义上的 Task.form 属性(例如 SalesTask.form = 'SalesTaskForm'),轻松扩展给定产品组的任务模型,然后设置ProductGroup 的销售实例的 task_type 外键。
生成的create_task 视图函数可以智能地为给定产品组呈现适当的表单:
<views.py>
...
import mysite.forms as taskforms
...
def create_task(request, name):
try:
product_group = ProductGroup.object.get(product_group_iexact=name)
except ProductGroup.DoesNotExist:
raise Http404
if request.method == 'POST':
task_model = product_group.item_type.model_class()
try:
form = taskforms.__getattribute__(task_model.form)
except AttributeError:
raise Http404
if form.is_valid():
# Process form
...
这似乎可行,我对解决方案并不不满,但将表单与给定模型相关联似乎是一种常见的需求——对于 django 来说相对较新——我想知道是否有内置的-in 或更雄辩的方法来处理这个问题?
提前致谢。
【问题讨论】:
-
为什么要把表单弄得这么复杂:form = taskforms.__getattribute__(task_model.form)?据我了解,您在这一行构造了一个具有某种类型的表单。
-
@sergzach 不确定我是否遵循(尽管如果我缺少更简单的方法,我肯定很感兴趣)。为了确保在同一页面上:
task_model.form是存储在Task模型(或其子类)中的字符串,让我知道Task的给定子类使用什么形式。为了使Task模型的所有子类的视图通用,我需要这一行来加载适当的表单类型。 -
传统的方式是为每个模型创建一个单独的表单。这不是 DRY 的问题,从概念上讲,它是模型的输入扩展。当您创建一个新表单时,您将其分配给一个模型。模型不应包含对其形式的任何引用,这违反了 Django 的封装。模型不知道他们的形式。
-
@sergzach 我明白你的意思。实际上,
Task模型的所有子类都有自己的形式(以显示它们的附加字段)。对于大多数产品组,基本型号Task和TaskForm就足够了。但我想避免对我的视图函数进行特殊封装,以便使用Task的子类为所有产品组显示适当的形式。我想我想要做的可能是非标准的,所以可能没有任何内置方法。就我个人而言,我觉得这个实现对我来说更容易维护,但也许我正在为未来的意外问题做好准备? -
是的,如果您的代码变得更加困难,就会出现意料之外的问题。如果你走这条路,你应该准备好用非标准方法解决简单的标准任务。我认为是时候提醒一条基本的 Python 规则了:“特殊情况并不足以打破规则。”。为什么不考虑将视图实现为类并应用类继承以避免重复行?你可以想出一些非常紧凑但又不违反规则的东西。
标签: django django-forms