Django模型继承高效代码

时间:2018-05-01 06:29:32

标签: python django multiple-inheritance

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

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)

通过将所有这些逻辑放在⊧ 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'整整很多。

2 个答案:

答案 0 :(得分: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

因此,当submitted_value = None(GET)或不提交(submitted_value)时,相同的六行代码处理get_or_creating任何答案。

元素3的解决方案

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

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

答案 1 :(得分:0)

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

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