【问题标题】:Django Model inheritance for efficient code用于高效代码的 Django 模型继承
【发布时间】:2018-10-11 05:47:17
【问题描述】:

我有一个 Django 应用程序,它使用抽象基类(“答案”)并根据问题对象所需的 answer_type 创建不同的答案。 (这个项目开始于民意调查教程)。现在的问题是:

class Question(models.Model):
    ANSWER_TYPE_CHOICES = (
    ('CH', 'Choice'),
    ('SA', 'Short Answer'),
    ('LA', 'Long Answer'),
    ('E3', 'Expert Judgement of Probabilities'),
    ('E4', 'Expert Judgment of Values'),
    ('BS', 'Brainstorms'),
    ('FB', 'Feedback'),
    )
    answer_type = models.CharField(max_length=2,
                               choices=ANSWER_TYPE_CHOICES,
                               default='SA')
    question_text = models.CharField(max_length=200, default="enter a question here")

答案是:

class Answer(models.Model):
"""
Answer is an abstract base class which ensures that question and user are
always defined for every answer
"""
question = models.ForeignKey(Question, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
class Meta:
    abstract = True
    ordering = ['user']

目前,我在 Answer 中有一个方法(覆盖 get_or_update_answer()),它带有特定于类型的指令来查看正确的表并收集或创建正确类型的对象。

    @classmethod
def get_or_update_answer(self, user, question, submitted_value={}, pk_ans=None):
    """
    this replaces get_or_update_answer with appropriate handling for all
    different Answer types. This allows the views answer and page_view to get
    or create answer objects for every question type calling this function.
    """
    if question.answer_type == 'CH':
        if not submitted_value:
            # by default, select the top of a set of radio buttons
            selected_choice = question.choice_set.first()
            answer, _created = Vote.objects.get_or_create(
                user=user,
                question=question,
                defaults={'choice': selected_choice})
        else:
            selected_choice = question.choice_set.get(pk=submitted_value)
            answer = Vote.objects.get(user=user, question=question)
            answer.choice = selected_choice

    elif question.answer_type == 'SA':
        if not submitted_value:
            submitted_value = ""
            answer, _created = Short_Answer.objects.get_or_create(
                user=user,
                question=question,
                defaults={'short_answer': submitted_value})
        else:
            answer = Short_Answer.objects.get(
                user=user,
                question=question)
            answer.short_answer = hashtag_cleaner(submitted_value['short_answer'])
 etc... etc... (similar handling for five more types)

通过将所有这些逻辑放在“models.py”中,我可以为 page_view 加载任意数量问题的用户答案:

    for question in page_question_list:
        answers[question] = Answer.get_or_update_answer(user, question, submitted_value, pk_ans)

我相信有一种更 Pythonic 的方式来设计这段代码 - 我还没有学会使用它,但我不确定是什么。类似于接口的东西,这样每个对象类型都可以实现自己的 Answer.get_or_update_answer() 版本,Python 将使用适合该对象的版本。这将使扩展“models.py”更加整洁。

【问题讨论】:

    标签: python django multiple-inheritance


    【解决方案1】:

    我最近重新审视了这个问题,将一两百行代码替换为五或十行代码,并认为有一天它可能对有人找到我在这里所做的有用。

    我遇到的问题有几个要素 - 首先,需要创建、保存和检索许多答案类型;其次,GET 与 POST 二分法(以及我始终创建答案并将其发送到表单的特殊解决方案);第三,一些类型有不同的逻辑(头脑风暴每个用户可以有多个答案,反馈甚至不需要响应 - 如果它是为用户创建的,它已经呈现出来。)这些元素可能掩盖了一些删除的机会重复,这使得访问者模式非常合适。

    元素 1 和 2 的解决方案

    在views.py 中创建了一个映射到相关Answer 子类的question.answer_type 代码字典(因为很难将它放在models.py 中并解决依赖关系):

    # views.py: 
    ANSWER_CLASS_DICT = {
    'CH': Vote,
    'SA': Short_Answer,
    'LA': Long_Answer,
    'E3': EJ_three_field,
    'E4': EJ_four_field,
    'BS': Brainstorm,
    'FB': FB,}
    

    然后我可以获得我想要'get_or_created'对于任何问题的答案类:

    ANSWER_CLASS_DICT[question.answer_type]
    

    我将它作为参数传递给类方法:

    # models.py:
    def get_or_update_answer(self, user, question, Cls, submitted_value=None, pk_ans=None):            
        if not submitted_value:
                answer, _created = Cls.objects.get_or_create(user=user, question=question)
        elif isinstance(submitted_value, dict):
                answer, _created = Cls.objects.get_or_create(user=user, question=question)
            for key, value in submitted_value.items():
                    setattr(answer, key, value)
        else:
            pass
    

    所以相同的六行代码处理 get_or_creating 任何 Answer 当提交_值=无 (GET) 或不 (提交_值)。

    元素 3 的解决方案

    元素三的解决方案是扩展模型,为重新访问同一问题的用户分离至少三种类型的处理: 'S' - 单一的,允许他们只记录一个答案,重新访问和修改答案,但绝不给出两个不同的答案。 'T' - 跟踪,这允许他们每次更新他们的答案,但会记录他们的答案可用的历史(例如对研究人员。) 'M' - 多个,允许为一个问题提交多个答案。

    在所有这些更改之后仍在修复错误,所以我不会发布代码。 下一个功能:复合问题和问题模板,因此人们可以使用管理员筛选来制作自己的答案类型。

    【讨论】:

      【解决方案2】:

      根据您所展示的内容,您是重新实现 Visitor pattern 的大部分方法,这是处理这种情况的一种非常标准的方式(您有一堆相关的子类,每个子类都需要自己的处理逻辑,并希望遍历它们的实例并对每个实例做一些事情)。

      我建议看看该模式是如何工作的,也许更明确地实施它。

      【讨论】:

        猜你喜欢
        • 2015-02-26
        • 2022-06-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-04-08
        • 1970-01-01
        • 2015-09-29
        相关资源
        最近更新 更多