美文网首页Django学习工作django.tip
Django:动态问卷系统的Model设计

Django:动态问卷系统的Model设计

作者: 治部少辅 | 来源:发表于2016-05-31 09:17 被阅读1403次
    questionnaire

    问卷设计的问题

    之前的一个网站项目中,对方希望我们实现一个允许用户自定义的问卷系统。我之前也没有什么经验,经过摸索做出了如下的设计,这里和大家分享一下。按照要求,问卷中包含四种不同的问题:

    • 单选题
    • 多选题
    • 问答题
    • 图片题

    问卷中问题的种类和数量不限。

    设计思路

    问卷系统首先可以拆解成为两个部分,一是问卷结构的设计,二是答卷结构的设计。二者十分类似,但是又略有不同。

    问卷结构设计

    一副问卷应当有一个统一的入口,一个对应于『问卷』这一实体概念的抽象。问卷拥有一些描述整体的属性,如创建时间,创建问卷的等等(有的人可能会试图将回答了问卷的用户和这里的问卷本身联系起来,但事实上和这些用户关联的应当是答案)。同时,不同的问题隶属于一个共同的问卷实体。这些问题,既共享一些通用的性质,但是基于不同的具体问题类型而又有不同的特点(选择题和问答题就有不同的属性)。特别的,对于选择题而言,题目和选项又建立起了一对多的关系。综合上面的描述,问卷部分的结构应当如下图所示:

    Struction of questionnaire

    答卷结构设计

    显然,问卷答卷直接是应该是一对多的。而答卷又是由针对问卷中各个问题的答案组成的。特别的,选择题的答案又指向所选的选项。故而,答卷部分的结构和问卷部分基本是相同的。

    示例代码

    以开头我说的问题为例。

    问卷

    首先我们需要为问卷建立一个model

    class Questionnaire(models.Model):
        created_at = models.DateTimeField(auto_now_add=True, editable=False)
        modified_at = models.DateTimeField(auto_now=True, editable=False)
        is_active = models.BooleanField(default=True)
    
        participants = models.ManyToManyField(settings.AUTH_USER_MODEL)
        user = models.ForeignKey(settings.AUTH_USER_MODEL)
    
        def __str__(self):
            return smart_str(self.user.username + "的问卷")
    
        class Meta:
            ordering = ["-created_at"]
    

    由于不同的问题具有一些共享的属性,我们可以创建一个抽象的基类类表示这些共通的属性,例子如下:

    class Question(models.Model):
        """这个类是单个问题的抽象"""
        question = models.CharField(max_length=200, verbose_name="问题")
        required = models.BooleanField(default=True, help_text="这个问题是否必须回答")
    
        questionnaire = models.ForeignKey(Questionnaire)
        order_in_list = models.IntegerField(default=1)  # 在问卷列表中的顺序,从1开始
        created_at = models.DateTimeField(auto_now_add=True, editable=False)
            
        class Meta:
            abstract = True
    

    为不同问题类型我们需要创建对应的model:

    class ChoiceQuestion(Question):
        """选择题"""
        multi_choice = models.BooleanField(default=False, verbose_name="是否为多选")
    
    
    TEXT_QUESTION_TYPE = 0
    FILE_QUESTION_TYPE = 1
    
    
    class NonChoiceQuestion(Question):
        """主观题"""
        type = models.SmallIntegerField(verbose_name="主观题类型",
                                        choices=(
                                            (TEXT_QUESTION_TYPE, '问答题'),
                                            (FILE_QUESTION_TYPE, '文件题')
                                        ), default=0)
    

    虽然问题描述中给出了4中不同的问题,但是实际上只需要两类就足以描述。中单选题和多选题只是能够同时选择的选项数量不同,题目本身的描述特点是相同的。而文件题和问答题只是答案的形式不一样,题目本身的描述特点是一样的。(综上来看,问题主要是表现问题的『描述』性)

    最后,我们需要为问答题设计选项:

    class Choice(models.Model):
        question = models.ForeignKey(ChoiceQuestion, related_name="choices")
        description = models.CharField(max_length=50)
        multi_choice = models.BooleanField(default=False, verbose_name="是否为多选")
        order_in_list = models.IntegerField(default=1)  # 在选项列表中的顺序,从1开始
    

    答案部分

    首先是答卷:

    class AnswerSheet(models.Model):
        """答卷的抽象"""
        user = models.ForeignKey(settings.AUTH_USER_MODEL)      # 答题者
        questionnaire = models.ForeignKey(Questionnaire)     # 对应问卷
    
        created_at = models.DateTimeField(auto_now_add=True, editable=False)
        modified_at = models.DateTimeField(auto_now=True, editable=False)
        is_active = models.BooleanField(default=True)
    

    答案则无法像问题只需要两个类既可以表示,这里我们需要为四种问题设计四种答案类:

    class Answer(models.Model):
        """答案的基类"""
        answer_sheet = models.ForeignKey(AnswerSheet)
    
        class Meta:
            abstract = True
    
    
    class SingleChoiceAnswer(Answer):
        """单选题的答案"""
        choice = models.ForeignKey(Choice, related_name="single_choice_answers")  # 单选题
        question = models.ForeignKey(ChoiceQuestion, related_name="single_choice_answer_set")
    
    class MultiChoiceAnswer(Answer):
        """多选题的答案"""
        choices = models.ManyToManyField(Choice, related_name="multi_choice_answers")
        question = models.ForeignKey(ChoiceQuestion, related_name="multi_choice_answers")
    
    
    class TextAnswer(Answer):
        """文字题的答案"""
        text = models.TextField()
        question = models.ForeignKey(NonChoiceQuestion)
    
    
    class FileAnswer(Answer):
        file = models.ImageField(upload_to="your_upload_path")
        question = models.ForeignKey(NonChoiceQuestion)
        is_image = models.BooleanField(default=True)
    

    综上我们就完成了这个可定制答卷的model部分。

    相关文章

      网友评论

      • f9e8ff53cd71:你好, 不曉得Form跟view的部分怎麼設計呢?
        f9e8ff53cd71:我是台湾的朋友, 我另外有个题库的model, 每产生一个问卷的时候, 会从题库中抓取几个预设的题目(不同类型的问劝预设的题目不一样), 也就是在form.save()後要取得此问卷的id, 然後从题库抓取生成问卷的问题, 我对django还没有很熟悉, 不知是否可以加个微信交流一下
        治部少辅:@Darren_8728 这个要看具体的业务特点了吧。
      • 5be6199d6395:写的很好,也希望大家有空能来看看我写的。
      • spring2000:写得好 正好最近在研究django实现问卷 期待下篇:+1::+1:

      本文标题:Django:动态问卷系统的Model设计

      本文链接:https://www.haomeiwen.com/subject/hnucdttx.html