tl; dr :我正在寻找一种简单的方法来将模型的形式与给定的模型相关联,这样只知道我可以渲染适当的创建/编辑形式的模型。我已经通过在Model类中将相应的ModelForm
的名称存储为字符串属性来开发了一个解决方案,但是对于django来说是新手,我很好奇这是否是首选解决方案。
我正在研究django任务/项目管理网站,该网站将存储许多产品组的任务。导航到mysite/<ProductGroup>/create_task.html
应该将用户定向到用于在该产品组中创建任务/项目的表单。默认情况下,这将允许使用简单的Task
编辑基本ModelForm
模型实例。但是,对于特定的产品组,我希望可以选择子类化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
属性(例如{{ 1}})然后为SalesTask.form = 'SalesTaskForm'
的销售实例设置task_type外键。
生成的ProductGroup
视图函数可以智能地呈现给定产品组的相应表单:
create_task
这似乎有效,我对这个解决方案并不满意,但似乎需要将一个表格与一个给定的模型联系起来 - 并且对django来说相对较新 - 我想知道是否有一个内置的 - 或者更有说服力的处理方法?
提前致谢。
答案 0 :(得分:0)
根据sergzach的评论,我意识到我应该放弃现有的解决方案,转而使用基于类的通用视图。我找不到很多关于内置的基于类的视图的文档(除了简单的TemplateView
示例),但是挖掘django.views.gereic.edit
的来源会显示CreateView
视图类,它提供了非常有说服力的解决方案。
如果您想在url.conf中指定template_name
和model
作为参数,则可以直接导入并使用CreateView
。对于我的情况,我仍然想从url的正则表达式中获取product_group,然后使用给定产品组上的ContentType
字段来获取相应的模型。所以我的ProductGroup
类定义与上面的相同,我的url.py变为:
from mysite.views import CreateTask
urlpatterns = patterns('',
...
url(r'^product_groups/(?P<product_group>[\w-]+)/new_task$',
CreateTask.as_view(),
name='create_task'),
...
然后在views.py中,我只是将CreateView
子类化并覆盖它的get_form_class
方法,以从捕获的产品组中获取相应的模型:
class CreateTask(CreateView):
template_name = "item_form.html"
def get_form_class(self):
"""
Returns the form class to use in this view
"""
if self.form_class:
# If we pass form_class as an arg, use that
return self.form_class
if self.kwargs['product_group']:
# Otherwise, get the product_group from the url regex, get its associated
# task model subclass and have the form_facory generate a form_class
try:
product_group = ProductGroup.objects.get(product_group__iexact=
self.kwargs['product_group'])
except ProductGroup.DoesNotExist:
raise Http404
model = product_group.task_type.model_class()
# The remainder is straight from CreateView's definition:
else:
if self.model is not None:
# If a model has been explicitly provided, use it
model = self.model
elif hasattr(self, 'object') and self.object is not None:
# If this view is operating on a single object, use
# the class of that object
model = self.object.__class__
else:
# Try to get a queryset and extract the model class
# from that
model = self.get_queryset().model
return model_forms.modelform_factory(model)
CreateView
类(或从ModelFormMixin
继承的任何其他类)的一个非常灵活的特性是,如果模型的相应ModelForm
尚未创建或作为参数提供, ModelFormMixin
将使用ModelForm
自动生成通用model_form_factory
。因此,如果我不想进行任何自定义,我甚至不必创建相应的模型表单。因此,如果说销售组想要包含其他字段budget
,我可以简单地将基础Task
模型子类化为SalesTask
,将budget
字段添加到此子类中,然后将task_type
外键设置为指向SalesTask
而不是Task
。很光滑!