0

tl;dr:我正在寻找一种将模型的表单与给定模型相关联的简单方法,以便仅知道模型我可以呈现适当的创建/编辑表单。我已经开发了一个解决方案,将相应ModelForm的名称作为字符串属性存储在 Model 类上,但是对于 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模型子类定义(例如SalesTask.form = 'SalesTaskForm')上的属性,然后为 的销售实例设置 task_type 外键,轻松扩展给定产品组的任务模型ProductGroup

然后,生成的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 来说相对较新——我想知道是否有内置或更雄辩的方法来处理这个?

提前致谢。

4

1 回答 1

0

根据 sergzach 的评论,我意识到我应该放弃当前的解决方案并改用基于类的通用视图。我找不到太多关于基于内置类的视图的文档(除了简单的TemplateView示例),但是深入研究了视图类的源代码django.views.gereic.editCreateView它提供了一个非常有说服力的解决方案。

如果想在 url.conf 中指定template_nameandmodel作为参数,可以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模型的对应项或未将其作为参数提供,则将使用. 因此,如果我不想要任何自定义,我什至不必创建相应的模型表单。因此,如果说销售组想要包含一个附加字段,我可以简单地将基本模型子类化为,将字段添加到该子类,然后将外键设置为指向而不是。非常光滑!ModelFormModelFormMixinModelFormmodel_form_factorybudgetTaskSalesTaskbudgettask_typeSalesTaskTask

于 2012-07-27T17:26:11.440 回答