【问题标题】:Model polymorphism and model-view separation模型多态性和模型视图分离
【发布时间】:2012-07-11 16:49:36
【问题描述】:

我在制作 Django 应用程序时遇到了某种困境,但我认为我遇到的问题可能普遍适用于 MVC 模式。我正在制作一个Question 模型,可用于构建测验或问卷。 Question 基类将是一个简单的免费回答问题。我想支持不同类型的问题,例如多项选择题或滑动比例题,这些将是 Question 基类的子类,并添加了额外的字段,例如一系列可能的选择。我希望将来能够扩展我的问题模型以支持更多类型的问题,为此我可以依赖多态性并在模型层和视图层之间为所有子类传递Question 类型的对象Question.

我遇到的问题是视图必须知道它收到的问题类型才能呈现它。如果它得到一个多项选择问题,它需要绘制单选小部件等。所以现在如果我用更多类型的问题扩展我的模型,我必须将它添加到模型和视图层。这似乎违背了多态性的观点,因为接收Question 对象的视图总是必须知道收到的问题的子类类型。我可以通过将呈现问题的责任委托给模型来解决这个问题。如果Question 模型有一个名为render_question() 的虚函数被其子类覆盖,那么视图层可以调用该函数来获取正确的HTML 以输出,而不必担心问题的类型。但是现在我遇到了将 HTML 渲染代码与模型绑定的问题。

是否有第三种解决方案没有我所想到的解决方案的缺点?或者这真的是一个必须做出艰难决定的两难境地?

【问题讨论】:

    标签: django model-view-controller design-patterns database-design


    【解决方案1】:

    模型/视图之间的分离旨在将表示与数据分离。您对Question 的多态模型层次结构的初始描述确实是一种有效的方法。

    你真正想做的是考虑使用 Django 的模型继承来处理数据层次结构,即有:

    BaseQuestion <- FreeQuestion, 
                    MultipleChoiceQuestion, 
                    SlidingScaleQuestion etc.
    

    然后您可以构建一个知道如何显示BaseQuestionBaseQuestionView(例如渲染问题字符串、设置样式等等)并使用相同的原理构造:

    BaseQuestionView <- FreeQuestionView, 
                        MultipleChoiceQuestionView, 
                        SlidingScaleQuestionView
    

    您可以将 BaseQuestionView 抽象为从数据库中提取所有 BaseQuestion 模型实例并调用在每个 FreeQuestionViewMultipleChoiceQuestionViewSlidingScaleQuestionView 子类中实现的抽象 render_question 方法。因此FreeQuestionView 知道它与FreeQuestion 模型一起工作,并且只实现如何为答案(文本字段)呈现小部件。 MultipleChoiceQuestionView 只会实现如何渲染单选框等。

    换句话说,它几乎与您在第一个案例中展示的内容完全相同,只是渲染实现位于 View 类而不是 Model 类中。

    当您想以不同方式渲染同一类对象时,可以应用相同的原则。


    通过模型继承,您可以使用点符号访问基实例的任何子类,即:question.freequestion。这将返回给您与基本实例关联的 FreeQuestion 实例,如果这不是它的类,则引发 Question.DoesNotExist

    使用基于类的视图,您可以添加可以根据天气以不同方式呈现您的问题的 Mixin,这是 FreeQuestion、MultipleChoiceQuestion、使用 python 的 MRO 模式,或者您可以对它们进行子类化。

    据我所知,Django 没有自动的方法来建立继承模型和继承视图之间的关联,您必须自己进行映射。

    也许最简单的方法是单独显式请求与您的初始 Question QuerySet 相关的所有实例,这些实例与 FreeQuestions、MultipleChoiceQuestions 等相关,并将它们投射到列表中,然后再将其提供给您的主渲染器,然后将通过 map[question.__class__] 到从 mixin 中找到渲染器方法。或者,您可以将问题类型保留在基类中,以避免处理类映射,让数据库在这方面为您提供帮助。

    但是,通常您不希望您的模型行为针对同一个类动态更改(这正是您使用 BaseQuestion 所做的有效工作)。在考虑到 REST 进行设计时尤其如此,因为您希望显式 URL 映射到显式具体而非抽象类型。

    【讨论】:

    • 感谢您的回复,这似乎是一个不错的解决方案。不过我确实有一个跟进:我如何确保BaseQuestionView 的正确子类与BaseQuestion 的特定实例相关联?如果BaseQuestion 的每个实例在运行时都被实例化,那么当该问题被实例化时,我会将每个问题与一个属于BaseQuestionView 正确子类的委托相关联。但我不确定最好的方法是使用数据库查询填充的问题列表。您将如何实现此关联?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-28
    • 2010-12-09
    • 1970-01-01
    • 2017-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多