问卷调查
问卷调查也可以成为满意度调查,是公司中针对每个用户开发的调查的功能。其目的可以是针对员工合格与否的调查也可以是对某个项目或者活动进行的意见调查。
表关系创建
这里用的是班级中学生的调查情况,也可以用于公司里部门的调查
一共6张表
from django.db import models class UserInfo(models.Model): """ 员工表 """ name = models.CharField(max_length=32) def __str__(self): return self.name class ClassList(models.Model): """ 班级表 """ title = models.CharField(max_length=32) def __str__(self): return self.title class Student(models.Model): """ 学生表 """ user = models.CharField(max_length=32) pwd = models.CharField(max_length=32) cls = models.ForeignKey(to=ClassList) def __str__(self): return self.user class Questionnaire(models.Model): """ 问卷表 """ title = models.CharField(max_length=64) cls = models.ForeignKey(to=ClassList) creator = models.ForeignKey(to=UserInfo) def __str__(self): return self.title class Question(models.Model): """ 问题 """ caption = models.CharField(max_length=64) question_types = ( (1,'打分'), (2,'单选'), (3,'评价'), ) tp = models.IntegerField(choices=question_types) naire = models.ForeignKey(Questionnaire,default=1) def __str__(self): return self.caption class Option(models.Model): """ 单选题的选项 """ name = models.CharField(verbose_name='选项名称',max_length=32) score = models.IntegerField(verbose_name='选项对应的分值') qs = models.ForeignKey(to=Question) def __str__(self): return self.name class Answer(models.Model): """ 回答 """ stu = models.ForeignKey(to=Student) question = models.ForeignKey(to=Question) option = models.ForeignKey(to="Option",null=True,blank=True) val = models.IntegerField(null=True,blank=True) content = models.CharField(max_length=255,null=True,blank=True) def __str__(self): return self.stu
路由分发
这里依旧没有写注销的功能,登录功能也较为简单
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^log_in/$',views.log_in ), url(r'^index/$',views.index ), url(r'^$',views.index ), url(r'^edit/(d+)/$',views.edit ),#编辑问卷 url(r'^score/(d+)/(d+)/$', views.score),#填写问卷 ]
登录功能
def log_in(request): if request.method=="POST": user=request.POST.get("user") pwd=request.POST.get("pwd") obj=models.Student.objects.filter(user=user,pwd=pwd) print(obj) if obj: return redirect('/index/') else: return HttpResponse('错误') return render(request,'log_in.html')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>login</title> </head> <body> <form action="" method="post"> {% csrf_token %} user:<input type="text"> pwd:<input type="password"> <input type="submit"> </form> </body> </html>
首页
def index(request): q_all=models.Questionnaire.objects.all()#所有的问卷 cls_all=models.ClassList.objects.all()#所有的班级(部门) stu_all=models.Student.objects.all()#所有的学生(员工) return render(request,'index.html',{'q_all':q_all,'cls_all':cls_all,'stu_all':stu_all})
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>index</title> <script src="/static/jquery-3.2.1.min.js"></script> <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css"> <script src="/static/bootstrap-3.3.7-dist/js/bootstrap.js"></script> <style> .p1 ul li { list-style: none; color: white; float: left; margin-right: 40px; padding-bottom: 30px; } </style> </head> <body> <div class="container"> <div class="row"> <div class="col-md-2"> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">CRM系统</h3> </div> <div class="panel-body"> <p>xxxxxxxxxx</p> <p>xxxxxxxxxx</p> <p>xxxxxxxxxx</p> <p>xxxxxxxxxx</p> <p>xxxxxxxxxx</p> <p>xxxxxxxxxx</p> <p>调查问卷列表</p> </div> </div> </div> <div class="col-md-10"> <div class="panel panel-default "> <div class="panel-heading p1"> <ul> <li><a href="">平台首页</a></li> <li><a href="">资产首页</a></li> </ul> <div class="pull-right"> <ul> <li><a href="">任务</a></li> <li><a href="">通知</a></li> <li><a href="">消息</a></li> </ul> </div> </div> <div class="panel-body"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">首页/数据列表</h3> </div> <div class="panel-body"> <button class="btn btn-success">添加</button> <table class="table table-bordered"> <th> {# <td><input type="checkbox"></td>#} <td>调查问卷名称</td> <td>问卷调查班级</td> <td>参与人数</td> <td>问卷选项</td> <td>调查地址</td> <td>查看评分</td> <td>操作</td> </th> {% for q in q_all %} <tr> <td><input type="checkbox"></td> <td><a href="">{{ q.title }}</a></td> <td>{{ q.cls.title }}</td> <td>0/{{ q.cls.student_set.count }}</td> <td><a href="/edit/{{ q.id }}/">编辑问卷</a></td> <td><a href="">xxxxxxxxxxx</a></td> <td><a href="">查看评分</a></td> <td><a href="">删除</a></td> </tr> {% endfor %} </table> </div> </div> </div> </div> </div> </div> </div> </body> </html>
编辑问卷
这里需要用到modelform组件,我们在app文件夹下创建一个form.py文件,专门用来放置form组件
from django.forms import ModelForm class QuestionModelForm(ModelForm): class Meta: model = models.Question fields = ['caption','tp'] error_messages = { 'caption': {'required': '不能为空', 'invalid': '格式错误'} } widgets = { 'caption': wd.Input(attrs={'class': 'form-control caption_input',"style":"height: 40px"}), 'tp': wd.Select(attrs={'class': 'form-control tp',"style":" 120px",}) } class OptionModelForm(ModelForm): class Meta: model = models.Option fields = ['name','score'] widgets = { 'name': wd.Input(attrs={'class': '',"style":" 80px"}), 'score': wd.Input(attrs={'class': '',"style":" 80px"}) }
因为这里的数据,后端进行循环后传递给前端,前端还需继续循环,所以这里我们用到生成器。后端每一次循环后yeild惰性循环,待前端需要时在进行循环,减小数据库操作规模、节省内存。
注意:前端页面对问卷的增加和删除需要用到事件委派。保存按钮提交的信息中,需要区分出点了添加按钮后又删掉的问题,这种问题就不要让他跑数据库啦,直接不进行操作就好了
def edit(request,pid): # if request.is_ajax(): # edit_form = Question_form() # response={'form':edit_form} # return HttpResponse(json.dumps(response)) # # edit_form=Question_form() # edit_obj=models.Questionnaire.objects.get(id=id) # return render(request,'edit.html',{'edit_obj':edit_obj,'edit_form':edit_form}) """ 问题 :param request: :param pid: 问卷ID :return: """ if request.method=='GET': print('编辑问卷页面') def inner(): que_list = models.Question.objects.filter(naire_id=pid) # [Question,Question,Question] if not que_list: # 新创建的问卷,其中还么有创建问题 form = QuestionModelForm() yield {'form': form, 'obj': None, 'option_class': 'hide', 'options': None} else: # 含问题的问卷 for que in que_list: form = QuestionModelForm(instance=que) temp = {'form': form, 'obj': que, 'option_class': 'hide', 'options': None} if que.tp == 2:#是选择类型 temp['option_class'] = '' temp['add_a'] = '添加选项'#前端生成按钮 temp['add_pic'] = 'glyphicon glyphicon-plus add-ques'#图标 # 获取当前问题的所有选项que def inner_loop(quee): option_list = models.Option.objects.filter(qs=quee) for v in option_list: yield {'form': OptionModelForm(instance=v), 'obj': v} temp['options'] = inner_loop(que) yield temp return render(request, 'edit.html', {'form_list': inner(),'pid':pid}) else: print('~~~~~~~~~~~~~保存问卷中') ret = {'status': True, 'msg': None, 'data': None} try: # 新提交的数据: plist=json.loads(request.POST.get('plist')) for item in plist: item['id']=int(item.get('id')) item['tp']=int(item.get('tp')) if len(item['options'])>0: for i in item['options']: i['id']=int(i['id']) i['score']=int(i['score']) print('转义后的问题',plist) # ajax_post_list = [ # { # 'id': 2, # 'caption': "鲁宁爱不是番禺??", # 'tp': 1, # # }, # { # 'id': None, # 'caption': "八级哥肾好不好?", # 'tp': 3 # }, # { # 'id': None, # 'caption': "鲁宁脸打不打?", # 'tp': 2, # "options": [ # {'id': 1, 'name': '绿', 'score': 10}, # {'id': 2, 'name': '翠绿', 'score': 8}, # ] # }, # ] question_list = models.Question.objects.filter(naire_id=pid) # 用户提交的所有问题ID post_id_list = [i.get('id') for i in plist if i.get('id')] print('用户提交的所有问题ID',post_id_list) # 数据库中获取的现在已有的问题ID question_id_list = [i.id for i in question_list] print('数据库中获取的现在已有的问题ID',question_id_list) # 数据库中的那些ID需要删除? del_id_list = set(question_id_list).difference(set(post_id_list)) print('数据库中的那些ID需要删除',del_id_list) # 循环ajax提交过来的所有问题信息 for item in plist: print(item) # item就是用户提交过来的一个问题 qid = item.get('id') caption = item.get('caption') tp = item.get('tp') options = item.get('options') print(qid,caption,tp,options) if qid not in question_id_list: # 要新增 new_question_obj = models.Question.objects.create(caption=caption, tp=tp) if tp == 2: for op in options: models.Option.objects.create(qs=new_question_obj, name=op.get('name'), score=op.get('score')) print('新增问题成功') else: # 要更新 print('要更新',qid) models.Question.objects.filter(id=qid).update(caption=caption, tp=tp) print('更新非单选问题成功') if not options: #类型由单选改为了其他类型,就需要将想关联的选项删除 models.Option.objects.filter(qs_id=qid).delete() print('删除选项成功') else: # 更新选项(不推荐) models.Option.objects.filter(qs_id=qid).delete() for op in options: models.Option.objects.create(name=op.get("name"), score=op.get('score'), qs_id=qid) print('更新选项成功') models.Question.objects.filter(id__in=del_id_list).delete() except Exception as e: ret['msg'] = str(e) ret['status'] = False print('保存问卷结束') return HttpResponse(json.dumps(ret))
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>编辑问卷题目</title> <script src="/static/jquery-3.2.1.min.js"></script> <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css"> <script src="/static/bootstrap-3.3.7-dist/js/bootstrap.js"></script> <style> .head { background-color: #2e6da4; height: 30px; line-height: 30px; } .head div a { color: whitesmoke; } .head div { margin-right: 30px; } .col-md-2 { margin-right: -30px; } .menu { border: solid 1px grey; height: 800px; } .two-btn .btn { margin: 20px 0; } .two-btn { height: 80px; } form { padding: 20px 0; } .glyphicon-remove { padding: 20px; } {# .question{#} {# background:#dce7f4 ;#} {# }#} ol { padding: 0; list-style: none; counter-reset: sectioncounter } ol > li:before { content: '问题' counter(sectioncounter) ':'; counter-increment: sectioncounter; font-size: 18px; color: #d4d4d4; } ol > li:nth-of-type(odd) { background-color: #f9f9f9; } .tp { display: inline-block; } </style> </head> <body> <div class="container-fluid"> <div class="row"> <div class="col-md-2"> <div class="head"> <span>CRM系统</span> </div> <div class="menu"> <div>xxxxxxxxxxxx</div> <div>xxxxxxxxxxxx</div> <div>xxxxxxxxxxxx</div> <div>xxxxxxxxxxxx</div> <div>xxxxxxxxxxxx</div> <div>xxxxxxxxxxxx</div> <div>xxxxxxxxxxxx</div> </div> </div> <div class="col-md-10"> <div class="head"> <div class="pull-left"><a href="">平台首页</a></div> <div class="pull-left"><a href="">资产首页</a></div> <div class="pull-right"><a href="">任务</a></div> <div class="pull-right"><a href="">通知</a></div> <div class="pull-right"><a href="">消息</a></div> </div> <div class="content"> <div class="two-btn"> <div class="pull-right"> <button class="btn btn-success " id="add-btn">添加</button> <button class="btn btn-primary" id="save-btn">保存</button> </div> </div> <ol class="que"> {% for item in form_list %} <li pk="{{ item.obj.id }}"> <div class="question"> <div class="pull-right"> <a href="javascript:;"><span class="glyphicon glyphicon-remove"></span></a> </div> <div class="row"> <div class="col-md-8 col-md-offset-1"> <div class="row"> <form class="form-horizontal"> {% csrf_token %} <div class="form-group"> <label class="col-sm-2 control-label">问题:</label> <div class="col-sm-9"> {{ item.form.caption }} </div> </div> <div class="form-group typeselect"> <label class="col-sm-2 control-label">类型:</label> <div class="col-sm-10 type"> {{ item.form.tp }} <a href="javascript:;" class="{{ item.add_pic }} ">{{ item.add_a }}</a> <ul> {% for v in item.options %} {# 选项#} <li id="{{ v.obj.id }}">{{ v.form }}<a href="javascript:;"><span class="glyphicon glyphicon-remove"></span></a> </li> {% endfor %} </ul> </div> </div> </form> </div> </div> </div> </div> </li> {% endfor %} </ol> </div> </div> </div> </div> <script> //更改问题类型 $('.que').on('change', '.tp', function () { var d = $(this).val() if (d == 2) { var x = '<a href="javascript:;" class="glyphicon glyphicon-plus">添加选项</a>' $(this).next().html('添加选项').attr({'class': 'glyphicon glyphicon-plus add-ques', 'href': 'javascript:;'}) var s = '<li id="100100100010011111111">选项名称:<input type="text" id="id_name"> 选项对应的分值:<input type="text" id="id_score"><a href="javascript:;"><span class="glyphicon glyphicon-remove"></span></a></li>' $(this).next().next().append(s) } else { $(this).next().next().children('li').remove() $(this).next().html('').attr({'class': ''}) } }) //添加选项 $('.que').on('click', '.add-ques', function () { $(this).next().append('<li id="100100100010011111111">选项名称:<input type="text" id="id_name"> 选项对应的分值:<input type="text" id="id_score"><a href="javascript:;"><span class="glyphicon glyphicon-remove"></span></a></li>') }) //删除选项 $('.que').on('click', '.question .pull-right a', function () { $(this).parent().parent().parent().remove() }) //删除整个问题 $('.que').on('click', '.typeselect li a', function () { $(this).parent().remove() }) //添加问题 $('#add-btn').click(function () { var li = $('.content>ol>li:first').clone(true) var a = '<a href="javascript:;" class=" "></a><ul></ul>' $('.content>ol').append(li) $('.content>ol>li:last').find('.caption_input').val('') $('.content>ol>li:last').find('select').value = 1 $('.content>ol>li:last').find('select').next().html('') $('.content>ol>li:last').find('select').next().next().children('li').remove() $('.content>ol>li:last').attr('pk', '10010010001001') }) //ajax提交 //点击保存按钮得到所有问题的数据 var plist = []; $('#save-btn').click(function () { $('.content>ol>li').each(function () { var dic = {'id': undefined, 'caption': undefined, 'tp': undefined, 'options': []} console.log($(this).attr('pk')) dic['id'] = $(this).attr('pk'); dic['caption'] = $(this).find('.caption_input').val() var tp = $(this).find('select').val() if (tp == 2) { $(this).find('li').each(function () { var option_dic = {'id': undefined, 'name': undefined, 'score': undefined} option_dic['id'] = $(this).attr('id') option_dic['name'] = $(this).find('#id_name').val() option_dic['score'] = $(this).find('#id_score').val() dic['options'].push(option_dic) }) } dic['tp'] = tp plist.push(dic) }); console.log(plist) $.ajax({ url: '/edit/{{ pid }}/', type: 'POST', data: { "csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val(), 'pid':{{ pid }}, 'plist': JSON.stringify(plist), }, success: function (data) { JSON.parse(data) if (data.msg) { alert(data.msg) } else { alert('保存成功') location.href = '/index/' } } }) }) </script> </body> </html>
填写问卷
#用于限制输入长度的函数 def func(val): if len(val) < 15: raise ValidationError('你太短了') def score(request, class_id, qn_id): """ :param request: :param class_id: 班级ID :param qn_id: 问卷ID :return: """ # student_id = request.session['student_info']['id'] student_id = 1 # 1. 当前登录用户是否是要评论的班级的学生 ct1 = models.Student.objects.filter(id=student_id, cls_id=class_id).first() if not ct1: return HttpResponse('你只能评论自己班级的问卷,是不是想转班?') # 2. 你是否已经提交过当前问卷答案 ct2 = models.Answer.objects.filter(stu_id=student_id, question__naire_id=qn_id).first() if ct2: return HttpResponse('你已经参与过调查,无法再次进行') # 3. 展示当前问卷下的所有问题 # question_list = models.Question.objects.filter(naire_id=qn_id) from django.forms import Form from django.forms import fields from django.forms import widgets field_dict = {} for que in question_list: print(que.caption) if que.tp == 1: field_dict['val_%s' % que.id] = fields.ChoiceField( label=que.caption, error_messages={'required':'必填'}, widget=widgets.RadioSelect, choices=[(i, i) for i in range(1, 11)] ) elif que.tp == 2: field_dict['option_id_%s' % que.id] = fields.ChoiceField( label=que.caption, widget=widgets.RadioSelect, choices=models.Option.objects.filter( qs_id=que.id).values_list('id', 'name')) else: from django.core.exceptions import ValidationError from django.core.validators import RegexValidator # field_dict['x_%s' % que.id] = fields.CharField( # label=que.caption, widget=widgets.Textarea,validators=[RegexValidator(regex=""),]) field_dict['content_%s' % que.id] = fields.CharField( label=que.caption, widget=widgets.Textarea, validators=[func, ]) # 类 MyTestForm = type("MyTestForm", (Form,), field_dict) if request.method == 'GET': form = MyTestForm() return render(request, 'score.html', {'question_list': question_list, 'form': form}) else: # 15字验证 # 不允许为空 form = MyTestForm(request.POST) if form.is_valid(): print(form.cleaned_data) # {'x_2': '3', 'x_9': 'sdfasdfasdfasdfasdfasdfasdf', 'x_10': '13'} objs = [] for key,v in form.cleaned_data.items(): k,qid = key.rsplit('_',1) answer_dict = {'stu_id':student_id,'question_id':qid,k:v} objs.append(models.Answer(**answer_dict)) models.Answer.objects.bulk_create(objs) return HttpResponse('感谢您的参与!!!') return render(request, 'score.html', {'question_list': question_list, 'form': form})
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> <style> ul{ list-style-type: none; } ul li{ display: inline-block; } </style> </head> <body> <div> <form method="post" novalidate> {% csrf_token %} {% for fd in form %} <div> <p>{{ fd.label }} {{ fd.errors.0 }}</p> {{ fd }} </div> {% endfor %} <input type="submit" value="提交"> </form> </div> </body> </html>