一、表結構設計
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 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width"> 7 <title>Title</title> 8 <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"> 9 <link rel="stylesheet" href="/static/css/index.css"> 10 <link rel="stylesheet" href="/static/css/questionedit.css"> 11 <script src="/static/jquery-3.2.1.min.js"></script> 12 <script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.js"></script> 13 <script src="/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script> 14 </head> 15 <body> 16 {#導航條#} 17 <nav class="navbar label-primary"> 18 <div class="container-fluid"> 19 <!-- Brand and toggle get grouped for better mobile display --> 20 <div class="navbar-header"> 21 <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" 22 data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> 23 <span class="sr-only">Toggle navigation</span> 24 <span class="icon-bar"></span> 25 <span class="icon-bar"></span> 26 <span class="icon-bar"></span> 27 </button> 28 <a class="navbar-brand textstyle" href="#">CRM系統</a> 29 </div> 30 31 <!-- Collect the nav links, forms, and other content for toggling --> 32 <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> 33 <ul class="nav navbar-nav"> 34 <li class="active"><a href="#" class="textstyle">平台首頁 <span class="sr-only">(current)</span></a></li> 35 <li><a href="#" class="textstyle">資產首頁</a></li> 36 </ul> 37 <form class="navbar-form navbar-left"> 38 <div class="form-group"> 39 <input type="text" class="form-control" placeholder="Search"> 40 </div> 41 <button type="submit" class="btn btn-default">Submit</button> 42 </form> 43 </div><!-- /.navbar-collapse --> 44 </div><!-- /.container-fluid --> 45 </nav> 46 <div class="container-fluid"> 47 <div class="row"> 48 <div class="left"> 49 <div class="col-md-3"></div> 50 </div> 51 <div class="right"> 52 <div class="col-md-9"> 53 <div class="panel panel-default"> 54 <div class="panel-heading"><a href="">首頁</a>/數據列表</div> 55 <div class="panel-body"> 56 {% block content %} 57 58 {% endblock %} 59 </div> 60 </div> 61 </div> 62 </div> 63 </div> 64 </div> 65 </body> 66 </html>

1 {% extends "base.html" %} 2 3 {% block content %} 4 <button class="btn btn-success addBtn">添加</button> 5 <table class="table table-bordered active"> 6 <thead> 7 <th><input type="checkbox"></th> 8 <th>問卷調查名稱</th> 9 <th>問卷調查班級</th> 10 <th>參與人數</th> 11 <th>問卷選項</th> 12 <th>調查地址</th> 13 <th>查看評分</th> 14 <th>操作</th> 15 </thead> 16 <tbody> 17 {% for Questionnaire in Questionnaire_obj %} 18 <tr> 19 <td><input type="checkbox"></td> 20 <td>{{ Questionnaire.title }}</td> 21 <td>{{ Questionnaire.cls.title }}</td> 22 <td>{{ Questionnaire.part_num }}/{{ Questionnaire.cls.student_set.all.count }}</td> 23 <td><a href="/questionedit/{{ Questionnaire.id }}/">編輯問卷</a></td> 24 <td><a href="/score/{{ Questionnaire.cls.id }}/{{ Questionnaire.id }}/">/score/{{ Questionnaire.cls.id }}/{{ Questionnaire.id }}/</a></td> 25 <td><a href="">查看評分</a></td> 26 <td><a href=""><button class="btn btn-danger">刪除</button></a></td> 27 </tr> 28 {% endfor %} 29 </table> 30 {% endblock %}

1 {% extends "base.html" %} 2 {% block content %} 3 <div class="pull-right"> 4 <button class="btn btn-success addquestion">添加</button> 5 <button class="btn btn-info savebtn">保存</button> 6 </div> 7 <div class="ccc"> 8 <ol> 9 {% for item in form %} 10 <li> 11 <div class="glyphicon glyphicon-remove pull-right delquestion"></div> 12 <div pk="{{ item.obj.id }}"> 13 {# <p>{{ item.form }}</p>#} 14 <p>問題名稱:{{ item.form.caption }}</p> 15 <p class="name">類型名稱:{{ item.form.question_type }} 16 <a class="{{ item.option_class }} addoption">添加選項</a> 17 </p> 18 <ul> 19 {% for v in item.options %} 20 <li class="{{ v.obj.id }}">{{ v.form }} 21 <span class="glyphicon glyphicon-remove deloption"></span> 22 </li> 23 {% endfor %} 24 </ul> 25 </div> 26 </li> 27 {% endfor %} 28 </ol> 29 </div> 30 <script> 31 //添加選項 32 $(".ccc").on("click", ".addoption", function () { 33 var ele_ul = $(this).parent().parent().children("ul"); 34 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>'; 35 ele_ul.append(s) 36 37 }); 38 39 //刪除選項(綁定事件委派) 40 $('.ccc').on('click', '.deloption', function () { 41 //找到當前的那一行刪除 42 $(this).parent().remove() 43 }); 44 45 //刪除問題(添加事件委派) 46 $("ol").on('click', ".delquestion", function () { 47 console.log($("body")); 48 $(this).parent().remove() 49 }); 50 51 //改變下拉框的觸發不同的事件 52 $(".ccc").on("click", "#id_question_type", function () { 53 if ($(this).val() == 2) { 54 //如果是單選的時候,如果有選項就把下面的內容隱藏了 55 $(this).next().removeClass("hide"); 56 } 57 else { 58 //否則就隱藏添加選項,吧西面的內容清空 59 $(".addoption").addClass("hide"); 60 $(this).parent().next().empty() 61 } 62 }); 63 64 //添加問題 65 $(".addquestion").click(function () { 66 //克隆一個整個的 67 var s = $("ol").children("li:last").clone(); 68 $("ol").append(s); 69 }); 70 //保存修改的信息 71 plist = []; 72 $(".savebtn").click(function () { 73 $("ol>li").each(function (i,v) { 74 {# console.log(i,v); //v打印的是每一個li#} 75 var id =$(this).find("div").eq(1).attr("pk"); 76 var caption = $(v).find("textarea").val(); 77 var type_id = $(v).find("select").val(); 78 console.log($(v).find("select"),type_id); 79 if (type_id==2){ 80 //如果類型id是2的時候,說明是有option的 81 var options_list = []; 82 var li = $(v).find("li"); 83 li.each(function (i,v) { 84 {# console.log(i,v);#} 85 var option_id = $(this).attr("class"); 86 var option_name =$(this).find("input:first").val(); 87 var option_score =$(this).find("input:last").val(); 88 options_list.push({"option_id":option_id,"option_name":option_name,"option_score":option_score}) 89 }); 90 plist.push({"id":id,"caption":caption,"type_id":type_id,"options":options_list}); 91 } 92 else { 93 plist.push({"id":id,"caption":caption,"type_id":type_id}); 94 } 95 }); 96 $.ajax({ 97 {# url: "/questionsave/"+s+"/",#} 98 url:"/questionsave/{{ nid }}/", 99 type: "post", 100 data: JSON.stringify(plist), 101 contentType: "json", 102 headers: {"X-CSRFToken": $.cookie('csrftoken')}, 103 success: function (i, v) { 104 console.log(i, v); 105 location.href = "/index/" 106 } 107 }) 108 }) 109 110 </script> 111 {% endblock %}

1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width"> 7 <title>Title</title> 8 <style> 9 li{ 10 list-style-type: none; 11 } 12 ul li{ 13 display: inline-block; 14 } 15 </style> 16 </head> 17 <body> 18 <form action="" method="post" novalidate> 19 {% csrf_token %} 20 {% for foo in form %} 21 <p>{{ foo.label }}{{ foo }}{{ foo.errors.0 }}</p> 22 {% endfor %} 23 24 <input type="submit" value="提交"> 25 </form> 26 </body> 27 </html>

1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width"> 7 <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"> 8 <title>Title</title> 9 <style> 10 .container{ 11 margin-top: 50px; 12 } 13 </style> 14 </head> 15 <body> 16 <div class="container"> 17 <div class="row"> 18 <div class="col-md-4 col-md-offset-3"> 19 <form action="" method="post" novalidate> 20 {% csrf_token %} 21 {% for foo in form %} 22 <p>{{ foo.label }}{{ foo }}{{ foo.errors.0 }}</p> 23 {% endfor %} 24 25 <button type="submit" class="btn btn-primary">登錄</button> 26 </form> 27 </div> 28 </div> 29 </div> 30 </body> 31 </html>