我在制作我的Django应用程序时遇到了各种困境,但我认为我遇到的问题通常可能适用于MVC模式。我正在建立一个Question
模型,可用于构建测验或问卷调查。 Question
基类将是一个简单的免费回复问题。我想支持不同类型的问题,例如多选问题或滑动比例问题,这些问题将是Question
基类的子类,其中添加了额外的字段,例如可能的选择数组。我希望能够扩展我的问题模型以支持将来更多类型的问题,为此我可以依赖多态并在模型层和视图层之间传递Question
类型的对象Question
的所有子类。
我遇到的问题是视图必须知道它为了呈现它而收到的问题类型。如果它得到一个多项选择问题,则需要绘制无线电选择窗口小部件等。所以现在如果我扩展我的模型有更多类型的问题,我必须将它添加到模型和视图层。这似乎打败了多态性的观点,因为接收Question
对象的视图总是必须知道所接收问题的子类类型。我可以通过将呈现问题的责任委托给模型来解决这个问题。如果Question
模型具有名为render_question()
的虚函数,其子类重写,则视图层可以调用该函数以获得正确的HTML输出,而不必担心问题的类型。但现在我遇到了将HTML渲染代码与模型绑定的问题。
有没有第三种解决方案没有我想到的解决方案的任何缺点?或者这是一个真正的困境,关于哪个人必须做出艰难的决定?
答案 0 :(得分:0)
模型/视图之间的分离旨在将表示与数据分离。您对Question
的多态模型层次结构的初始描述确实是一种有效的方法。
你真正想要做的是考虑使用Django的模型继承来处理数据层次结构,即:
BaseQuestion <- FreeQuestion,
MultipleChoiceQuestion,
SlidingScaleQuestion etc.
然后你可以构建一个BaseQuestionView
知道如何显示BaseQuestion
(例如渲染问题字符串,设置它的样式和不是什么)并使用相同的原理构造:
BaseQuestionView <- FreeQuestionView,
MultipleChoiceQuestionView,
SlidingScaleQuestionView
您可以使BaseQuestionView抽象为从数据库中提取所有BaseQuestion
模型实例,并调用在每个render_question
,FreeQuestionView
中实现的抽象MultipleChoiceQuestionView
方法, SlidingScaleQuestionView
个子类。因此FreeQuestionView
知道它使用FreeQuestion
模型,并且只实现如何为答案(文本字段)呈现小部件。 MultipleChoiceQuestionView
只会实现如何渲染无线电盒等。
换句话说,它几乎就是你在第一种情况下呈现的内容,除了渲染实现位于View类而不是Model类中。
当您想以不同方式渲染同一类对象时,可以应用相同的原则。
使用模型继承,您可以使用点表示法访问基本实例的任何子类,即:question.freequestion
。这将返回与基本实例关联的FreeQuestion实例,或者如果不是它的类,则引发Question.DoesNotExist
。
使用基于类的视图,您可以添加Mixins,它可以根据天气呈现您的问题,这是FreeQuestion,MultipleChoiceQuestion,使用python的MRO模式,或者您可以将它们子类化。
据我所知,Django没有自动方式在继承的模型和继承的视图之间建立关联,你必须自己制作映射。
也许最简单的方法是显式请求所有与FreeQuestions,MultipleChoiceQuestions等相关的初始Question QuerySet匹配的实例,然后将它们放入列表中,然后再将其提供给主渲染器,然后再渲染{{1}从mixin中找到渲染器方法。或者,您可以将问题类型保留在基类中,以避免必须处理类映射并让DB帮助您。
但是,通常您不希望模型行为针对同一个类动态更改(这是您使用BaseQuestion实际执行的操作)。在设计REST时尤其如此,因为您希望显式URL映射到显式具体而非抽象类型。