• 问卷的表结构设计和具体实现


    ######################################

    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 = "问卷单选题的选项表"

    ##############################################

    表结构分析
    1,答案表,谁答题的,什么问题,单选的选项,打分的结果,平路你的结果
    2,单选表,属于哪一个问题,选项的名字,选项的值,一个选项一行,方便扩展
    3,问卷表,比较简单,就是一个头,问卷的名称,谁创建的,
    4,问卷问题表,属于哪一个问卷,问题描述,问题的类型,

    ##########################################

    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^index/$', views.index),
        url(r'^questionedit/(d+)/$', views.questionedit),
        url(r'^questionedit2/(d+)/$', views.questionedit2),
        url(r'^questionsave/(d+)/$', views.questionsave),
        url(r'^student_login/$', views.student_login),
        url(r'^score/(d+)/(d+)/$', views.score),
    ]

    ###########################################

    from django.core.validators import RegexValidator
    from django.db.models.aggregates import Count
    from django.forms.forms import Form
    from django.http.response import JsonResponse
    from django.shortcuts import render, HttpResponse,redirect
    from app01 import models
    from django.forms import ModelForm,fields,widgets
    import json
    from django.core.exceptions import ValidationError
    from django.core.validators import RegexValidator
    # Create your views here.
    def index(request):
        Questionnaire_obj = models.Questionnaire.objects.all()
        #查询问卷所属的班级的学生个数
        for naire in Questionnaire_obj:
            naire.part_num = models.Answer.objects.filter(queston__in=naire.questions_set.all()).values_list('student_id').distinct().count()
            print(naire.part_num)
        return render(request,"index.html",{"Questionnaire_obj":Questionnaire_obj})
    
    class QuestionForm(ModelForm):
        class Meta:
            model = models.Questions
            fields = ["caption","question_type"]
    
            error_messages = {
                "caption":{"required":"不能为空"}
            }
            widgets ={
                "caption":widgets.Textarea(attrs={"class": "question","rows":0,"cols":0})
            }
    
    class OptionModelForm(ModelForm):
        class Meta:
            model = models.Option
            fields = ["name","score"]
    
    def questionedit(request,nid):
        # 方式一:
        # #查询当前问卷的所有的问题
        # que_list = models.Questions.objects.filter(questionnaire_id=nid).all()
        # question_list = []
        # if not que_list:
        #     '''新建的问题,还没有创建问题'''
        #     form = QuestionForm()
        #     question_list.append(form)
        #     return render(request,"questionedit.html",{"question_list":question_list})
        # else:
        #     '''已经创建了问题的'''
        #     for que in que_list:
        #         print(que,"que===")
        #         form = QuestionForm(instance=que)
        #         question_list.append(form)
        # return render(request,"questionedit.html",{"question_list":question_list})
    
        # 方式二:
        #查询当前问卷的所有的问题
        # def inner():
        #     que_list = models.Questions.objects.filter(questionnaire_id=nid).all()
        #     if not que_list:
        #         '''新建的问题,还没有创建问题'''
        #         form = QuestionForm()
        #         yield form
        #     else:
        #         '''已经创建了问题的'''
        #         for que in que_list:
        #             form = QuestionForm(instance=que)
        #             yield form
        # return render(request,"questionedit.html",{"form":inner()})
    
    
        # 方式三,yield返回的时候吧form作为一个字典的key返回
        # def inner():
        #     que_list = models.Questions.objects.filter(questionnaire_id=nid).all()
        #     if not que_list:
        #         '''新建的问题,还没有创建问题'''
        #         form = QuestionForm()
        #         yield {"form":form,"obj":None}
        #     else:
        #         '''已经创建了问题的'''
        #         for que in que_list:
        #             print(que)
        #             form = QuestionForm(instance=que)
        #             temp = {"form":form,"obj":que,"option_class":"hide","options":None}
        #             if que.question_type == 2:
        #                 '''如果选项类型是单选的时候'''
        #                 temp["option_class"] = ""
        #                 #如果是单选的时候让显示所有的选项
        #                 question_option_list =[]
        #                 option_list = models.Option.objects.filter(question=que)
        #                 for obj in option_list:
        #                     vm = OptionModelForm(instance=obj)
        #                     question_option_list.append(vm)
        #                 print(question_option_list,"pppppppppppppp")
        #                 temp["options"] = question_option_list
        #             yield temp
        # return render(request, "questionedit.html", {"form": inner()})
    
        # 方式四
        def inner():
            que_list = models.Questions.objects.filter(questionnaire_id=nid).all()
            if not que_list:
                '''新建的问题,还没有创建问题'''
                form = QuestionForm()
                yield {"form":form,"obj":None,'option_class':"hide","options":None}
            else:
                '''已经创建了问题的'''
                for que in que_list:
                    print(que)
                    form = QuestionForm(instance=que)
                    temp = {"form":form,"obj":que,"option_class":"hide","options":None}
                    if que.question_type == 2:
                        '''如果选项类型是单选的时候'''
                        temp["option_class"] = ""
                        #如果是单选的时候让显示所有的选项
                        def inner_loop(quee):
                            option_list = models.Option.objects.filter(question=quee)
                            for v in option_list:
                                yield {"form":OptionModelForm(instance=v),"obj":v}
                        temp["options"] = inner_loop(que)
                    yield temp
        return render(request, "questionedit.html", {"form": inner(),"nid":nid})
    
    def questionedit2(request,nid):
            def inner():
                que_list = models.Questions.objects.filter(questionnaire_id=nid).all()
                if not que_list:
                    '''新建的问题,还没有创建问题'''
                    form = QuestionForm()
                    yield {"form": form, "obj": None, 'option_class': "hide", "options": None}
                else:
                    '''已经创建了问题的'''
                    for que in que_list:
                        print(que)
                        form = QuestionForm(instance=que)
                        temp = {"form": form, "obj": que, "option_class": "hide", "options": None}
                        if que.question_type == 2:
                            '''如果选项类型是单选的时候'''
                            temp["option_class"] = ""
    
                            # 如果是单选的时候让显示所有的选项
                            def inner_loop(quee):
                                option_list = models.Option.objects.filter(question=quee)
                                for v in option_list:
                                    yield {"form": OptionModelForm(instance=v), "obj": v}
    
                            temp["options"] = inner_loop(que)
                        yield temp
            return render(request,"questionedit.html",{"form":inner()})
    
    
    def questionsave(request,nid):
        ret = {"status":True,"msg":None,"data":None}
        try:
            if request.is_ajax():
                #得到新提交的数据
                data=request.body.decode("utf8")
                post_data_list = json.loads(data)
                #找到所有的问题列表
                question_list = models.Questions.objects.filter(questionnaire_id=nid)
                #找到用户提交的所有的问题id
                post_id_list = [i.get("id") for i in post_data_list if i.get("id")]
                # print(post_id_list,"post_id_list")  #['1', '2', '1', '2', '1', '2', '1', '2'] post_id_list
                #找到数据库中的所有的问题id
                question_id_list = [i.id for i in question_list]
                # print(question_id_list,"question_id_list")  #[1, 2] question_id_list
                #数据库中的那些id需要删除(数据库里有前端没有的数据删除)
                del_id_list = set(question_id_list).difference(post_id_list)
    
                #循环ajax发过来的那些问题列表,
                for item in post_data_list:
                    #item就是用户传进来的每个问题
                    caption = item.get("caption")
                    type_id = item.get("type_id")
                    qid = item.get("id")
                    options = item.get("options")
                    if not qid in question_id_list:
                        #如果前端传进来的id不在数据库里面,就说明要新增
                        new_question_obj = models.Questions.objects.create(caption=caption,question_type=type_id,questionnaire_id=nid)
                        if type_id==2:
                            for op in options:
                                name = op.get("name")
                                score = op.get("score")
                                models.Option.objects.create(name=name,score=score,question=new_question_obj)
                    else:
                        #否则说明是要更新
                        models.Questions.objects.filter(id=qid).update(caption=caption,question_type=type_id,questionnaire_id=qid)
                        if not options:
                            #如果没有options就把数据库的options记录给删除了
                            models.Option.objects.filter(id=nid).delete()
                        else:
                            #如果有先删除原来的后创建新传进来的
                            models.Option.objects.filter(id=nid).delete()
                            for op in options:
                                name = op.get("name")
                                score = op.get("score")
                                models.Option.objects.create(name=name,score=score,question_id=qid)
                    models.Questions.objects.filter(id__in=del_id_list).delete()
        except Exception as e:
            ret['msg'] = str(e)
            ret["status"] = False
        return JsonResponse(ret)
    
    
    class StudentForm(ModelForm):
        # password = fields.CharField(max_length=8, validators=[RegexValidator("d+", "密码只能是数字")],
        #                             error_messages={"max_length":"8"}
        #                             )
        # 这里如果写上password,下面也有了,就会把下面的给覆盖了
        class Meta:
            model=models.Student
            fields=["name","password"]
    
            error_messages ={
                "name":{"required":"用户名不能为空"},
                "password":{"required":"密码不能为空","max_length":"密码长度不能大于8位"},
            },
            widgets = {
                "password": widgets.PasswordInput(attrs={'placeholder': 'password', 'class': 'form-control'}),
                "name": widgets.TextInput(attrs={'placeholder': 'username', 'class': 'form-control'})
            }
    
    
    def student_login(request):
        # obj = models.Student.objects.all().first()
        # print(obj.id,obj.name)
        if request.method=="GET":
            form = StudentForm()
        else:
            print("============")
            form = StudentForm(data=request.POST)
            if form.is_valid():
                print("======",form.cleaned_data)
                user = models.Student.objects.filter(**form.cleaned_data).first()
                if user:
                    request.session["id"] =user.id
                    request.session["user"] =user.name
                    class_id = request.session.get("class_id")
                    qn_id = request.session.get("qn_id")
                    # if class_id==None or qn_id==None:
                    # return redirect("/index/")
                    return redirect('/score/%s/%s'%(class_id,qn_id))
                else:
                    return render(request,"student_login.html",{"form":form})
        return render(request, "student_login.html", {"form": form})
    
    
    def func(val):
        #参数要有,Form用正则匹配的时候不用加括号,自己就会执行这个函数,去验证
        if len(val)<15:
            raise ValidationError("字数不能小于15字")
    
    def score(request,class_id,qn_id):
        # print(class_id,qn_id)
        student_id = request.session.get("id")
        print(student_id,"student_id")
        request.session["class_id"] = class_id
        request.session["qn_id"] = qn_id
        if not student_id:
            return redirect("/student_login/")
        #查看当前用户是否是要评论的班级的学生
    
        stu1 = models.Student.objects.filter(cls=class_id,id=student_id).count()
        print("stu1",stu1)
        if not stu1:
            return HttpResponse("你还不是这个班的学生呢,你无权访问我们这次问卷")
    
        #当前学生是否已经评论过当前问卷
        stu2 = models.Answer.objects.filter(student_id=student_id,queston__questionnaire_id=qn_id).count()
        # print(stu2)
        if stu2:
            return HttpResponse("你已经答过了,感谢你的参与。无法再进行第二次答卷")
    
        #验证通过以后就开始展示答卷的页面了
        # 展开当前问卷下的所有的问题
        question_list = models.Questions.objects.filter(questionnaire_id=qn_id)
        question_dict = {}
        for que in question_list:
            print(que.id)
            print("asssdsfsfs",models.Option.objects.filter(question_id=que.id).values_list('id', 'name'))
            # que是每一个问题
            if que.question_type==1:   #打分
                question_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.question_type==2:  #单选
                question_dict["option_id_%s"%que.id] = fields.ChoiceField(
                    label=que.caption,
                    error_messages={"required":"不能为空"},
                    widget = widgets.RadioSelect,
                    choices=models.Option.objects.filter(question_id=que.id).values_list('id', 'name')  #拿自己的选项
                )
    
            else:  #评价
                question_dict["content_%s"%que.id] = fields.CharField(
                    label=que.caption,
                    error_messages={"required": "不能为空"},
                    widget=widgets.Textarea,
                    validators=[func,]   #这里的func不用加参数
                )
    
        MyTestForm = type("MyTestForm",(Form,),question_dict)  #三个参数分别是:类名,继承的父类,后面是一个字典
        if request.method =="GET":
            form = MyTestForm()
            return render(request,"score.html",{"question_list":question_list,"form":form})
        else:
            form = MyTestForm(request.POST)
            if form.is_valid():
                #如果验证成功
                print(form.cleaned_data,"2222222")
                objs = []
                for key,v in form.cleaned_data.items():
                    print(key,v,"1111111")
                    k,qid = key.rsplit('_',1)
                    print(k,qid,"2223333")
                    answer_dict = {'student_id':student_id,'queston_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})
    
    views.py

    ###########################################

    <!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">
        <title>Title</title>
        <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
        <link rel="stylesheet" href="/static/css/index.css">
        <link rel="stylesheet" href="/static/css/questionedit.css">
        <script src="/static/jquery-3.2.1.min.js"></script>
        <script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.js"></script>
        <script src="/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
    </head>
    <body>
    {#导航条#}
    <nav class="navbar label-primary">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                        data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand textstyle" href="#">CRM系统</a>
            </div>
    
            <!-- Collect the nav links, forms, and other content for toggling -->
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav">
                    <li class="active"><a href="#" class="textstyle">平台首页 <span class="sr-only">(current)</span></a></li>
                    <li><a href="#" class="textstyle">资产首页</a></li>
                </ul>
                <form class="navbar-form navbar-left">
                    <div class="form-group">
                        <input type="text" class="form-control" placeholder="Search">
                    </div>
                    <button type="submit" class="btn btn-default">Submit</button>
                </form>
            </div><!-- /.navbar-collapse -->
        </div><!-- /.container-fluid -->
    </nav>
    <div class="container-fluid">
        <div class="row">
            <div class="left">
                <div class="col-md-3"></div>
            </div>
            <div class="right">
                <div class="col-md-9">
                    <div class="panel panel-default">
                        <div class="panel-heading"><a href="">首页</a>/数据列表</div>
                        <div class="panel-body">
                        {% block content %}
                           
                        {% endblock %}
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    </body>
    </html>
    
    base.html

    ###########################################

    {% extends "base.html" %}
    
    {% block content %}
        <button class="btn btn-success addBtn">添加</button>
        <table class="table table-bordered active">
            <thead>
                <th><input type="checkbox"></th>
                <th>问卷调查名称</th>
                <th>问卷调查班级</th>
                <th>参与人数</th>
                <th>问卷选项</th>
                <th>调查地址</th>
                <th>查看评分</th>
                <th>操作</th>
            </thead>
            <tbody>
            {% for Questionnaire in Questionnaire_obj %}
                <tr>
                    <td><input type="checkbox"></td>
                    <td>{{ Questionnaire.title }}</td>
                    <td>{{ Questionnaire.cls.title }}</td>
                    <td>{{ Questionnaire.part_num }}/{{ Questionnaire.cls.student_set.all.count }}</td>
                    <td><a href="/questionedit/{{ Questionnaire.id }}/">编辑问卷</a></td>
                    <td><a href="/score/{{ Questionnaire.cls.id }}/{{ Questionnaire.id }}/">/score/{{ Questionnaire.cls.id }}/{{ Questionnaire.id }}/</a></td>
                    <td><a href="">查看评分</a></td>
                    <td><a href=""><button class="btn btn-danger">删除</button></a></td>
                </tr>
            {% endfor %}
        </table>
    {% endblock %}
    
    index.html

    ###########################################

    {% extends "base.html" %}
    {% block content %}
        <div class="pull-right">
            <button class="btn btn-success addquestion">添加</button>
            <button class="btn btn-info savebtn">保存</button>
        </div>
        <div class="ccc">
            <ol>
                {% for item in form %}
                    <li>
                        <div class="glyphicon glyphicon-remove pull-right delquestion"></div>
                        <div pk="{{ item.obj.id }}">
                            {#                <p>{{ item.form }}</p>#}
                            <p>问题名称:{{ item.form.caption }}</p>
                            <p class="name">类型名称:{{ item.form.question_type }}
                                <a class="{{ item.option_class }} addoption">添加选项</a>
                            </p>
                            <ul>
                                {% for v in item.options %}
                                    <li class="{{ v.obj.id }}">{{ v.form }}
                                        <span class="glyphicon glyphicon-remove deloption"></span>
                                    </li>
                                {% endfor %}
                            </ul>
                        </div>
                    </li>
                {% endfor %}
            </ol>
        </div>
        <script>
            //添加选项
            $(".ccc").on("click", ".addoption", function () {
                var ele_ul = $(this).parent().parent().children("ul");
                var s = '<li"><label for="id_name">选项名:</label><input type="text" name="name" maxlength="32" required="" id="id_name"><label for="id_score">选项对应的分值:</label><input type="number" name="score" required="" id="id_score"><span class="glyphicon glyphicon-remove deloption"></span></li>';
                ele_ul.append(s)
    
            });
    
            //删除选项(绑定事件委派)
            $('.ccc').on('click', '.deloption', function () {
                //找到当前的那一行删除
                $(this).parent().remove()
            });
    
            //删除问题(添加事件委派)
            $("ol").on('click', ".delquestion", function () {
                console.log($("body"));
                $(this).parent().remove()
            });
    
            //改变下拉框的触发不同的事件
            $(".ccc").on("click", "#id_question_type", function () {
                if ($(this).val() == 2) {
                    //如果是单选的时候,如果有选项就把下面的内容隐藏了
                    $(this).next().removeClass("hide");
                }
                else {
                    //否则就隐藏添加选项,吧西面的内容清空
                    $(".addoption").addClass("hide");
                    $(this).parent().next().empty()
                }
            });
    
            //添加问题
            $(".addquestion").click(function () {
                //克隆一个整个的
                var s = $("ol").children("li:last").clone();
                $("ol").append(s);
            });
            //保存修改的信息
            plist = [];
            $(".savebtn").click(function () {
                $("ol>li").each(function (i,v) {
    {#            console.log(i,v);   //v打印的是每一个li#}
                var id =$(this).find("div").eq(1).attr("pk");
                var caption = $(v).find("textarea").val();
                var type_id = $(v).find("select").val();
                console.log($(v).find("select"),type_id);
                if (type_id==2){
                    //如果类型id是2的时候,说明是有option的
                    var options_list = [];
                    var li = $(v).find("li");
                    li.each(function (i,v) {
    {#                    console.log(i,v);#}
                        var option_id = $(this).attr("class");
                        var option_name =$(this).find("input:first").val();
                        var option_score =$(this).find("input:last").val();
                        options_list.push({"option_id":option_id,"option_name":option_name,"option_score":option_score})
                    });
                    plist.push({"id":id,"caption":caption,"type_id":type_id,"options":options_list});
                }
                else {
                    plist.push({"id":id,"caption":caption,"type_id":type_id});
                }
            });
                $.ajax({
    {#                url: "/questionsave/"+s+"/",#}
                    url:"/questionsave/{{ nid }}/",
                    type: "post",
                    data: JSON.stringify(plist),
                    contentType: "json",
                    headers: {"X-CSRFToken": $.cookie('csrftoken')},
                    success: function (i, v) {
                        console.log(i, v);
                        location.href = "/index/"
                    }
                })
            })
    
        </script>
    {% endblock %}
    
    questionedit.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">
        <title>Title</title>
        <style>
            li{
                list-style-type: none;
            }
            ul li{
                display: inline-block;
            }
        </style>
    </head>
    <body>
    <form action="" method="post" novalidate>
        {% csrf_token %}
        {% for foo in form %}
        <p>{{ foo.label }}{{ foo }}{{ foo.errors.0 }}</p>
        {% endfor %}
        
        <input type="submit" value="提交">
    </form>
    </body>
    </html>
    
    score.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">
        <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
        <title>Title</title>
        <style>
            .container{
                margin-top: 50px;
            }
        </style>
    </head>
    <body>
    <div class="container">
        <div class="row">
            <div class="col-md-4 col-md-offset-3">
                <form action="" method="post" novalidate>
                    {% csrf_token %}
                   {% for foo in form %}
                   <p>{{ foo.label }}{{ foo }}{{ foo.errors.0 }}</p>
                   {% endfor %}
    
                    <button type="submit" class="btn btn-primary">登录</button>
                </form>
            </div>
        </div>
    </div>
    </body>
    </html>
    
    student_login

    ###########################################

    ###########################################

  • 相关阅读:
    20款时尚的 WordPress 企业模板【免费主题下载】
    3D Grid Effect – 使用 CSS3 制作网格动画效果
    WTF Forms – 使用 CSS 实现用户体验更好的表单
    Tiff – 值得你体验一下的可视化的字体对比工具
    Quill – 可以灵活自定义的开源的富文本编辑器
    Photopile JS – 帮助你实现精致的照片堆叠效果
    Web 前端开发人员和设计师必读精华文章【系列二十六】
    Shepherd – 在应用程序中轻松实现引导功能
    Gulp.js
    12款响应式的 jQuery 旋转木马(传送带)插件
  • 原文地址:https://www.cnblogs.com/andy0816/p/12511145.html
Copyright © 2020-2023  润新知