一、表结构设计
from django.db import models # Create your models here. class UserInfo(models.Model): '''员工表''' username = models.CharField(max_length=64,verbose_name="用户名") password = models.CharField(max_length=32,verbose_name="用户密码") def __str__(self): return self.username class Meta: verbose_name_plural="员工表" class ClassList(models.Model): '''班级表''' title = models.CharField(max_length=32,verbose_name="班级名") def __str__(self): return self.title class Meta: verbose_name_plural = "班级表" class Student(models.Model): '''学生表''' name = models.CharField(max_length=32,verbose_name="学生姓名") password = models.CharField(max_length=32,verbose_name="学生密码") cls = models.ForeignKey(to="ClassList",verbose_name="所属班级") def __str__(self): return self.name class Meta: verbose_name_plural = "学生表" class Questionnaire(models.Model): '''问卷表''' title = models.CharField(max_length=32,verbose_name="问卷名") cls = models.ForeignKey(to="ClassList",verbose_name="问卷班级") create_user = models.ForeignKey(to="UserInfo",verbose_name="创建问卷的用户") def __str__(self): return self.title class Meta: verbose_name_plural = "问卷表" class Questions(models.Model): '''问卷问题表''' caption = models.CharField(max_length=32,verbose_name="问题题目") type_choices = ( (1,"打分"), (2,"单选"), (3,"评价") ) question_type = models.IntegerField(choices=type_choices,verbose_name="问题类型") questionnaire = models.ForeignKey(to="Questionnaire",verbose_name="所属问卷",default=1) def __str__(self): return self.caption class Meta: verbose_name_plural = "问卷问题表" class Answer(models.Model): '''问卷回答表''' #谁什么时候对那个问题作答了 student = models.ForeignKey(to="Student",verbose_name="所属学生") queston = models.ForeignKey(to="Questions",verbose_name="所属问题") option = models.ForeignKey(to="Option",null=True,blank=True) val = models.IntegerField(null=True,blank=True,verbose_name="数字答案") content = models.CharField(max_length=255,null=True,blank=True,verbose_name="文本答案") def __str__(self): return self.content class Meta: verbose_name_plural = "问卷回答表" class Option(models.Model): '''问卷单选题的选项表''' name = models.CharField(max_length=32,verbose_name="选项名") score = models.IntegerField(verbose_name="选项对应的分值") question = models.ForeignKey(to="Questions",verbose_name="所属问题") def __str__(self): return str(self.score) class Meta: verbose_name_plural = "问卷单选题的选项表"
二、具体实现
urls.py
1 from django.conf.urls import url 2 from django.contrib import admin 3 from app01 import views 4 urlpatterns = [ 5 url(r'^admin/', admin.site.urls), 6 url(r'^index/$', views.index), 7 url(r'^questionedit/(\d+)/$', views.questionedit), 8 url(r'^questionedit2/(\d+)/$', views.questionedit2), 9 url(r'^questionsave/(\d+)/$', views.questionsave), 10 url(r'^student_login/$', views.student_login), 11 url(r'^score/(\d+)/(\d+)/$', views.score), 12 ]
views.py
1 from django.core.validators import RegexValidator 2 from django.db.models.aggregates import Count 3 from django.forms.forms import Form 4 from django.http.response import JsonResponse 5 from django.shortcuts import render, HttpResponse,redirect 6 from app01 import models 7 from django.forms import ModelForm,fields,widgets 8 import json 9 from django.core.exceptions import ValidationError 10 from django.core.validators import RegexValidator 11 # Create your views here. 12 def index(request): 13 Questionnaire_obj = models.Questionnaire.objects.all() 14 #查询问卷所属的班级的学生个数 15 for naire in Questionnaire_obj: 16 naire.part_num = models.Answer.objects.filter(queston__in=naire.questions_set.all()).values_list('student_id').distinct().count() 17 print(naire.part_num) 18 return render(request,"index.html",{"Questionnaire_obj":Questionnaire_obj}) 19 20 class QuestionForm(ModelForm): 21 class Meta: 22 model = models.Questions 23 fields = ["caption","question_type"] 24 25 error_messages = { 26 "caption":{"required":"不能为空"} 27 } 28 widgets ={ 29 "caption":widgets.Textarea(attrs={"class": "question","rows":0,"cols":0}) 30 } 31 32 class OptionModelForm(ModelForm): 33 class Meta: 34 model = models.Option 35 fields = ["name","score"] 36 37 def questionedit(request,nid): 38 # 方式一: 39 # #查询当前问卷的所有的问题 40 # que_list = models.Questions.objects.filter(questionnaire_id=nid).all() 41 # question_list = [] 42 # if not que_list: 43 # '''新建的问题,还没有创建问题''' 44 # form = QuestionForm() 45 # question_list.append(form) 46 # return render(request,"questionedit.html",{"question_list":question_list}) 47 # else: 48 # '''已经创建了问题的''' 49 # for que in que_list: 50 # print(que,"que===") 51 # form = QuestionForm(instance=que) 52 # question_list.append(form) 53 # return render(request,"questionedit.html",{"question_list":question_list}) 54 55 # 方式二: 56 #查询当前问卷的所有的问题 57 # def inner(): 58 # que_list = models.Questions.objects.filter(questionnaire_id=nid).all() 59 # if not que_list: 60 # '''新建的问题,还没有创建问题''' 61 # form = QuestionForm() 62 # yield form 63 # else: 64 # '''已经创建了问题的''' 65 # for que in que_list: 66 # form = QuestionForm(instance=que) 67 # yield form 68 # return render(request,"questionedit.html",{"form":inner()}) 69 70 71 # 方式三,yield返回的时候吧form作为一个字典的key返回 72 # def inner(): 73 # que_list = models.Questions.objects.filter(questionnaire_id=nid).all() 74 # if not que_list: 75 # '''新建的问题,还没有创建问题''' 76 # form = QuestionForm() 77 # yield {"form":form,"obj":None} 78 # else: 79 # '''已经创建了问题的''' 80 # for que in que_list: 81 # print(que) 82 # form = QuestionForm(instance=que) 83 # temp = {"form":form,"obj":que,"option_class":"hide","options":None} 84 # if que.question_type == 2: 85 # '''如果选项类型是单选的时候''' 86 # temp["option_class"] = "" 87 # #如果是单选的时候让显示所有的选项 88 # question_option_list =[] 89 # option_list = models.Option.objects.filter(question=que) 90 # for obj in option_list: 91 # vm = OptionModelForm(instance=obj) 92 # question_option_list.append(vm) 93 # print(question_option_list,"pppppppppppppp") 94 # temp["options"] = question_option_list 95 # yield temp 96 # return render(request, "questionedit.html", {"form": inner()}) 97 98 # 方式四 99 def inner(): 100 que_list = models.Questions.objects.filter(questionnaire_id=nid).all() 101 if not que_list: 102 '''新建的问题,还没有创建问题''' 103 form = QuestionForm() 104 yield {"form":form,"obj":None,'option_class':"hide","options":None} 105 else: 106 '''已经创建了问题的''' 107 for que in que_list: 108 print(que) 109 form = QuestionForm(instance=que) 110 temp = {"form":form,"obj":que,"option_class":"hide","options":None} 111 if que.question_type == 2: 112 '''如果选项类型是单选的时候''' 113 temp["option_class"] = "" 114 #如果是单选的时候让显示所有的选项 115 def inner_loop(quee): 116 option_list = models.Option.objects.filter(question=quee) 117 for v in option_list: 118 yield {"form":OptionModelForm(instance=v),"obj":v} 119 temp["options"] = inner_loop(que) 120 yield temp 121 return render(request, "questionedit.html", {"form": inner(),"nid":nid}) 122 123 def questionedit2(request,nid): 124 def inner(): 125 que_list = models.Questions.objects.filter(questionnaire_id=nid).all() 126 if not que_list: 127 '''新建的问题,还没有创建问题''' 128 form = QuestionForm() 129 yield {"form": form, "obj": None, 'option_class': "hide", "options": None} 130 else: 131 '''已经创建了问题的''' 132 for que in que_list: 133 print(que) 134 form = QuestionForm(instance=que) 135 temp = {"form": form, "obj": que, "option_class": "hide", "options": None} 136 if que.question_type == 2: 137 '''如果选项类型是单选的时候''' 138 temp["option_class"] = "" 139 140 # 如果是单选的时候让显示所有的选项 141 def inner_loop(quee): 142 option_list = models.Option.objects.filter(question=quee) 143 for v in option_list: 144 yield {"form": OptionModelForm(instance=v), "obj": v} 145 146 temp["options"] = inner_loop(que) 147 yield temp 148 return render(request,"questionedit.html",{"form":inner()}) 149 150 151 def questionsave(request,nid): 152 ret = {"status":True,"msg":None,"data":None} 153 try: 154 if request.is_ajax(): 155 #得到新提交的数据 156 data=request.body.decode("utf8") 157 post_data_list = json.loads(data) 158 #找到所有的问题列表 159 question_list = models.Questions.objects.filter(questionnaire_id=nid) 160 #找到用户提交的所有的问题id 161 post_id_list = [i.get("id") for i in post_data_list if i.get("id")] 162 # print(post_id_list,"post_id_list") #['1', '2', '1', '2', '1', '2', '1', '2'] post_id_list 163 #找到数据库中的所有的问题id 164 question_id_list = [i.id for i in question_list] 165 # print(question_id_list,"question_id_list") #[1, 2] question_id_list 166 #数据库中的那些id需要删除(数据库里有前端没有的数据删除) 167 del_id_list = set(question_id_list).difference(post_id_list) 168 169 #循环ajax发过来的那些问题列表, 170 for item in post_data_list: 171 #item就是用户传进来的每个问题 172 caption = item.get("caption") 173 type_id = item.get("type_id") 174 qid = item.get("id") 175 options = item.get("options") 176 if not qid in question_id_list: 177 #如果前端传进来的id不在数据库里面,就说明要新增 178 new_question_obj = models.Questions.objects.create(caption=caption,question_type=type_id,questionnaire_id=nid) 179 if type_id==2: 180 for op in options: 181 name = op.get("name") 182 score = op.get("score") 183 models.Option.objects.create(name=name,score=score,question=new_question_obj) 184 else: 185 #否则说明是要更新 186 models.Questions.objects.filter(id=qid).update(caption=caption,question_type=type_id,questionnaire_id=qid) 187 if not options: 188 #如果没有options就把数据库的options记录给删除了 189 models.Option.objects.filter(id=nid).delete() 190 else: 191 #如果有先删除原来的后创建新传进来的 192 models.Option.objects.filter(id=nid).delete() 193 for op in options: 194 name = op.get("name") 195 score = op.get("score") 196 models.Option.objects.create(name=name,score=score,question_id=qid) 197 models.Questions.objects.filter(id__in=del_id_list).delete() 198 except Exception as e: 199 ret['msg'] = str(e) 200 ret["status"] = False 201 return JsonResponse(ret) 202 203 204 class StudentForm(ModelForm): 205 # password = fields.CharField(max_length=8, validators=[RegexValidator("\d+", "密码只能是数字")], 206 # error_messages={"max_length":"8"} 207 # ) 208 # 这里如果写上password,下面也有了,就会把下面的给覆盖了 209 class Meta: 210 model=models.Student 211 fields=["name","password"] 212 213 error_messages ={ 214 "name":{"required":"用户名不能为空"}, 215 "password":{"required":"密码不能为空","max_length":"密码长度不能大于8位"}, 216 }, 217 widgets = { 218 "password": widgets.PasswordInput(attrs={'placeholder': 'password', 'class': 'form-control'}), 219 "name": widgets.TextInput(attrs={'placeholder': 'username', 'class': 'form-control'}) 220 } 221 222 223 def student_login(request): 224 # obj = models.Student.objects.all().first() 225 # print(obj.id,obj.name) 226 if request.method=="GET": 227 form = StudentForm() 228 else: 229 print("============") 230 form = StudentForm(data=request.POST) 231 if form.is_valid(): 232 print("======",form.cleaned_data) 233 user = models.Student.objects.filter(**form.cleaned_data).first() 234 if user: 235 request.session["id"] =user.id 236 request.session["user"] =user.name 237 class_id = request.session.get("class_id") 238 qn_id = request.session.get("qn_id") 239 # if class_id==None or qn_id==None: 240 # return redirect("/index/") 241 return redirect('/score/%s/%s'%(class_id,qn_id)) 242 else: 243 return render(request,"student_login.html",{"form":form}) 244 return render(request, "student_login.html", {"form": form}) 245 246 247 def func(val): 248 #参数要有,Form用正则匹配的时候不用加括号,自己就会执行这个函数,去验证 249 if len(val)<15: 250 raise ValidationError("字数不能小于15字") 251 252 def score(request,class_id,qn_id): 253 # print(class_id,qn_id) 254 student_id = request.session.get("id") 255 print(student_id,"student_id") 256 request.session["class_id"] = class_id 257 request.session["qn_id"] = qn_id 258 if not student_id: 259 return redirect("/student_login/") 260 #查看当前用户是否是要评论的班级的学生 261 262 stu1 = models.Student.objects.filter(cls=class_id,id=student_id).count() 263 print("stu1",stu1) 264 if not stu1: 265 return HttpResponse("你还不是这个班的学生呢,你无权访问我们这次问卷") 266 267 #当前学生是否已经评论过当前问卷 268 stu2 = models.Answer.objects.filter(student_id=student_id,queston__questionnaire_id=qn_id).count() 269 # print(stu2) 270 if stu2: 271 return HttpResponse("你已经答过了,感谢你的参与。无法再进行第二次答卷") 272 273 #验证通过以后就开始展示答卷的页面了 274 # 展开当前问卷下的所有的问题 275 question_list = models.Questions.objects.filter(questionnaire_id=qn_id) 276 question_dict = {} 277 for que in question_list: 278 print(que.id) 279 print("asssdsfsfs",models.Option.objects.filter(question_id=que.id).values_list('id', 'name')) 280 # que是每一个问题 281 if que.question_type==1: #打分 282 question_dict["val_%s"%que.id] = fields.ChoiceField( 283 label=que.caption, 284 error_messages={"required":"不能为空"}, 285 widget = widgets.RadioSelect, 286 choices = [(i,i) for i in range(1,11)] 287 ) 288 elif que.question_type==2: #单选 289 question_dict["option_id_%s"%que.id] = fields.ChoiceField( 290 label=que.caption, 291 error_messages={"required":"不能为空"}, 292 widget = widgets.RadioSelect, 293 choices=models.Option.objects.filter(question_id=que.id).values_list('id', 'name') #拿自己的选项 294 ) 295 296 else: #评价 297 question_dict["content_%s"%que.id] = fields.CharField( 298 label=que.caption, 299 error_messages={"required": "不能为空"}, 300 widget=widgets.Textarea, 301 validators=[func,] #这里的func不用加参数 302 ) 303 304 MyTestForm = type("MyTestForm",(Form,),question_dict) #三个参数分别是:类名,继承的父类,后面是一个字典 305 if request.method =="GET": 306 form = MyTestForm() 307 return render(request,"score.html",{"question_list":question_list,"form":form}) 308 else: 309 form = MyTestForm(request.POST) 310 if form.is_valid(): 311 #如果验证成功 312 print(form.cleaned_data,"2222222") 313 objs = [] 314 for key,v in form.cleaned_data.items(): 315 print(key,v,"1111111") 316 k,qid = key.rsplit('_',1) 317 print(k,qid,"2223333") 318 answer_dict = {'student_id':student_id,'queston_id':qid,k:v} 319 320 objs.append(models.Answer(**answer_dict)) 321 models.Answer.objects.bulk_create(objs) 322 return HttpResponse("感谢你的参与!!") 323 return render(request, "score.html", {"question_list": question_list, "form": form})
templates
1 2 "en"> 3 4 "UTF-8"> 5 "X-UA-Compatible" content="IE=edge"> 6 "viewport" content="width=device-width"> 7Title 8 "stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"> 9 "stylesheet" href="/static/css/index.css"> 10 "stylesheet" href="/static/css/questionedit.css"> 11 12 13 14 15 16 {#导航条#} 17 46class="container-fluid"> 4765 66class="row"> 4864class="left"> 4951class="col-md-3">50class="right"> 5263class="col-md-9"> 5362class="panel panel-default"> 54 5561class="panel-body"> 56 {% block content %} 57 58 {% endblock %} 5960
1 {% extends "base.html" %} 2 3 {% block content %} 4 5
"checkbox"> | 8问卷调查名称 | 9问卷调查班级 | 10参与人数 | 11问卷选项 | 12调查地址 | 13查看评分 | 14操作 | 15 16 17 {% for Questionnaire in Questionnaire_obj %} 18
---|---|---|---|---|---|---|---|
"checkbox"> | 20{{ Questionnaire.title }} | 21{{ Questionnaire.cls.title }} | 22{{ Questionnaire.part_num }}/{{ Questionnaire.cls.student_set.all.count }} | 23"/questionedit/{{ Questionnaire.id }}/">编辑问卷 | 24"/score/{{ Questionnaire.cls.id }}/{{ Questionnaire.id }}/">/score/{{ Questionnaire.cls.id }}/{{ Questionnaire.id }}/ | 25"">查看评分 | 26""> | 27
1 {% extends "base.html" %} 2 {% block content %} 3class="pull-right"> 4 5 67class="ccc"> 830 111 {% endblock %}9 {% for item in form %} 10
29- 11
27 {% endfor %} 28class="glyphicon glyphicon-remove pull-right delquestion">12"{{ item.obj.id }}"> 13 {#26{{ item.form }}
#} 14问题名称:{{ item.form.caption }}
15class="name">类型名称:{{ item.form.question_type }} 16 class="{{ item.option_class }} addoption">添加选项 17
1819 {% for v in item.options %} 20
25- class="{{ v.obj.id }}">{{ v.form }} 21 class="glyphicon glyphicon-remove deloption"> 22
23 {% endfor %} 24
1 2 "en"> 3 4 "UTF-8"> 5 "X-UA-Compatible" content="IE=edge"> 6 "viewport" content="width=device-width"> 726 27Title 8 16 17 18
1 2 "en"> 3 4 "UTF-8"> 5 "X-UA-Compatible" content="IE=edge"> 6 "viewport" content="width=device-width"> 7 "stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"> 8Title 9 14 15 16class="container"> 1730 31class="row"> 1829class="col-md-4 col-md-offset-3"> 19 2728