會議室預訂系統
1、目標及業務流程
指望效果:javascript
業務流程:
-
用戶註冊
-
用戶登陸
-
預訂會議室
-
退訂會議室
-
選擇日期;今日以及之後日期
2、表結構設計和生成
一、models.py(用戶繼承AbstractUser)
from django.db import models from django.contrib.auth.models import AbstractUser # Create your models here. class UserInfo(AbstractUser): tel = models.CharField(max_length=32,verbose_name="電話") avatar = models.FileField(upload_to="avatars/", default="avatars/timg.jpg", verbose_name="頭像") class Room(models.Model): """會議室表""" caption = models.CharField(max_length=32,verbose_name="會議室名稱") num = models.IntegerField(verbose_name="容納人數") # 容納人數 def __str__(self): return self.caption class Meta: verbose_name = "會議室信息" verbose_name_plural = verbose_name class Book(models.Model): """會議室預訂""" user = models.ForeignKey(to="UserInfo",on_delete=models.CASCADE) room = models.ForeignKey(to="Room",on_delete=models.CASCADE) date = models.DateField() time_choice = ( (1, "8:00"), (2, "9:00"), (3, "10:00"), (4, "11:00"), (5, "12:00"), (6, "13:00"), (7, "14:00"), (8, "15:00"), (9, "16:00"), (10, "17:00"), (11, "18:00"), (12, "19:00"), (13, "20:00"), (14, "21:00"), (15, "22:00"), (16, "23:00"), ) time_id = models.IntegerField(choices=time_choice) def __str__(self): return str(self.user)+"預約了"+str(self.room) class Meta: verbose_name = "預約信息" verbose_name_plural = verbose_name unique_together = ( ("room","date","time_id"), # 這三個字段聯合惟一,防止重複預訂 )
二、修改配置文件settings.py,覆蓋默認的User模型
Django容許你經過修改setting.py文件中的 AUTH_USER_MODEL 設置覆蓋默認的User模型,其值引用一個自定義的模型。css
1
|
AUTH_USER_MODEL
=
"app01.UserInfo"
|
上面的值表示Django應用的名稱(必須位於INSTALLLED_APPS中)和你想使用的User模型的名稱。html
注意:在建立任何遷移或者第一次運行 manager.py migrate 前設置 AUTH_USER_MODEL。前端
設置AUTH_USER_MODEL數據庫結構有很大的影響。改變了一些會使用到的表格,而且會影響到一些外鍵和多對多關係的構造。在你有表格被建立後更改此設置是不被 makemigrations 支持的,而且會致使你須要手動修改數據庫結構,從舊用戶表中導出數據,可能從新應用一些遷移。java
三、數據遷移及建立超級用戶
1
2
|
$ python3 manage.py makemigrations
$ python3 manage.py migrate
|
3、系統登陸login
urls.py:
from django.conf.urls import url from django.contrib import admin from django.views.static import serve from django.conf import settings from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), # 用戶登陸 url(r'^login/',views.acc_login), # 展現預訂信息 url(r'^index/',views.index), # 極驗滑動驗證碼 獲取驗證碼的url url(r'^pc-geetest/register', views.get_geetest), # media相關的路由設置 url(r'^media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}), # 處理預訂請求 url(r'^book/',views.book), # 首頁 url(r'^home/',views.home), # 註銷 url(r'^logout/',views.acc_logout), # 用戶註冊 url(r'^reg/',views.reg), # 臨時測試 url(r'^test/',views.test), # 修改密碼 url(r'^change_password/',views.change_password), ]
login.html(使用了滑動驗證)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>用戶登陸</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css"> <!-- 引入封裝了failback的接口--initGeetest --> <script src="http://static.geetest.com/static/tools/gt.js"></script> </head> <body> <h3 class="text-center" style="color: orangered">歡迎登陸會議室預訂系統</h3> <br> <div class="container"> <div class="row"> <form class="form-horizontal col-md-6 col-md-offset-4" autocomplete="off"> {% csrf_token %} <div class="form-group"> <label for="username" class="col-lg-2 control-label">用戶名</label> <div class="col-sm-6"> <input type="text" class="form-control" id="username" name="username" placeholder="用戶名"> </div> </div> <div class="form-group"> <label for="pwd" class="col-lg-2 control-label">密碼</label> <div class="col-sm-6"> <input type="password" class="form-control" id="pwd" name="pwd" placeholder="密碼"> </div> </div> <div class="form-group"> <!-- 放置極驗的滑動驗證碼 --> <div id="popup-captcha"></div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="button" id="login_btn" class="btn btn-info">登陸</button> <span class="login-error has-error text-danger"></span> </div> </div> </form> </div> </div> <script src="/static/js/jquery-3.3.1.min.js"></script> <script src="/static/bootstrap/js/bootstrap.min.js"></script> <script> var handlerPopup = function (captchaObj) { // 成功的回調 captchaObj.onSuccess(function () { var validate = captchaObj.getValidate(); // 1. 取到用戶填寫的用戶名和密碼 -> 取input框的值 var username = $("#username").val(); var password = $("#pwd").val(); $.ajax({ url: "/login/", // 進行二次驗證 type: "post", dataType: "json", data: { username: username, pwd: password, csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), geetest_challenge: validate.geetest_challenge, geetest_validate: validate.geetest_validate, geetest_seccode: validate.geetest_seccode }, success: function (data) { console.log(data); if (data.status) { // 有錯誤,在頁面上提示 $(".login-error").text(data.msg); } else { // 登錄成功 location.href = data.msg; } } }); }); $("#login_btn").click(function () { captchaObj.show(); }); // 將驗證碼加到id爲captcha的元素裏 captchaObj.appendTo("#popup-captcha"); // 更多接口參考:http://www.geetest.com/install/sections/idx-client-sdk.html }; //當再次點擊input輸入框時,錯誤提示要消失 $("#username,#pwd").focus(function () { $(".login-error").text(""); }) // 驗證開始須要向網站主後臺獲取id,challenge,success(是否啓用failback) $.ajax({ url: "/pc-geetest/register?t=" + (new Date()).getTime(), // 加隨機數防止緩存 type: "get", dataType: "json", success: function (data) { // 使用initGeetest接口 // 參數1:配置參數 // 參數2:回調,回調的第一個參數驗證碼對象,以後可使用它作appendTo之類的事件 initGeetest({ gt: data.gt, challenge: data.challenge, product: "popup", // 產品形式,包括:float,embed,popup。注意只對PC版驗證碼有效 offline: !data.success // 表示用戶後臺檢測極驗服務器是否宕機,通常不須要關注 // 更多配置參數請參見:http://www.geetest.com/install/sections/idx-client-sdk.html#config }, handlerPopup); } }) </script> </body> </html>
login視圖函數
from django.shortcuts import render,redirect, HttpResponse from django.contrib.auth import authenticate, login, logout from django.http import JsonResponse from geetest import GeetestLib from django.contrib.auth.decorators import login_required from app01 import models from app01 import forms import json import datetime # 登陸視圖 def acc_login(request): if request.method == "POST": print(request.POST) res = {"status": 0, "msg": ""} username = request.POST.get("username") password = request.POST.get("pwd") # 獲取極驗 滑動驗證碼相關的參數 gt = GeetestLib(pc_geetest_id, pc_geetest_key) challenge = request.POST.get(gt.FN_CHALLENGE, '') validate = request.POST.get(gt.FN_VALIDATE, '') seccode = request.POST.get(gt.FN_SECCODE, '') status = request.session[gt.GT_STATUS_SESSION_KEY] user_id = request.session["user_id"] if status: result = gt.success_validate(challenge, validate, seccode, user_id) else: result = gt.failback_validate(challenge, validate, seccode) print("####################", result) if result: user = authenticate(username=username, password=password) if user: login(request, user) res["msg"] = "/index/" else: res["status"] =1 res["msg"] = "認證失敗,請檢查用戶名及密碼是否正確" else: res["status"] = 1 res["msg"] = "驗證碼錯誤" print("**************", res) return JsonResponse(res) return render(request, 'login.html') # 請在官網申請ID使用,示例ID不可以使用 pc_geetest_id = "b46d1900d0a894591916ea94ea91bd2c" pc_geetest_key = "36fc3fe98530eea08dfc6ce76e3d24c4" # 處理極驗 獲取驗證碼的視圖 def get_geetest(request): user_id = 'test' gt = GeetestLib(pc_geetest_id, pc_geetest_key) status = gt.pre_process(user_id) request.session[gt.GT_STATUS_SESSION_KEY] = status request.session["user_id"] = user_id response_str = gt.get_response_str() return HttpResponse(response_str)
注意:auth模塊的authenticate()方法,提供了用戶認證,若是認證信息有效,會返回一個 User 對象;若是認證失敗,則返回None。python
4、index部分
一、引入admin組件(後臺數據管理組件)並完成admin註冊
admin.py
from django.contrib import admin from app01 import models from django.contrib.auth.admin import UserAdmin from django.utils.translation import gettext_lazy # Register your models here. # 配置會議室信息表 class RoomConfig(admin.ModelAdmin): list_display = ('caption','num') list_filter=('num',) search_fields = ('caption','num') # 配置預訂信息表 class BookConfig(admin.ModelAdmin): list_display = ('user','room','date','time_id') list_filter = ('user','room','date','time_id') search_fields = ('user','room','date','time_id') # 配置用戶管理表 class UserProfileAdmin(UserAdmin): list_display = ('username','last_login','is_superuser','is_staff','is_active','date_joined') list_filter = ('last_login', 'is_staff', 'date_joined', 'is_active') search_fields = ('username',) fieldsets = ( (None,{'fields':('username','password','first_name','last_name','email')}), (gettext_lazy('用戶信息'),{'fields':('username','email','tel','avatar')}), (gettext_lazy('用戶權限'), {'fields': ('is_superuser','is_staff','is_active', 'groups', 'user_permissions')}), (gettext_lazy('Important dates'), {'fields': ('last_login', 'date_joined')}), ) admin.site.register(models.Room,RoomConfig) admin.site.register(models.UserInfo,UserProfileAdmin) admin.site.register(models.Book,BookConfig)
注意:配置用戶管理表相當重要,若是不配置,你會發如今登陸admin後添加用戶時密碼是明文,並無被加密。jquery
若是你的用戶擴展表沒有擴展新的字段,能夠直接admin.site.register(models.UserInfo,UserAdmin)。git
二、登陸admin添加數據
三、index視圖函數數據處理和index.html模板渲染
index視圖github
@login_required(login_url="/login/") def index(request): date = datetime.datetime.now().date() # 若是沒有指定日期,默認使用當天日期 book_date = request.GET.get("book_date",date) print('日期:', request.GET.get("book_date")) print("book_date",book_date) # 獲取會議室時間段列表 time_choice = models.Book.time_choice print(time_choice) # 獲取會議室列表 room_list = models.Room.objects.all() # 獲取會議室預訂信息 book_list = models.Book.objects.filter(date=book_date) htmls='' for room in room_list: htmls += '<tr><td>{}({})</td>'.format(room.caption,room.num) for time in time_choice: # 判斷該單元格是否被預訂 flag = False for book in book_list: if book.room.pk == room.pk and book.time_id == time[0]: # 單元格被預約 flag = True break if flag: # 判斷當前登陸人與預訂會議室的人是否一致,一導致用info樣式 if request.user.username == book.user.username: htmls += '<td class="info item" room_id={} time_id={}>{}</td>'.format(room.pk, time[0],book.user.username) else: htmls += '<td class="success item" room_id={} time_id={}>{}</td>'.format(room.pk, time[0], book.user.username) else: htmls += '<td class="item" room_id={} time_id={}></td>'.format(room.pk,time[0]) htmls += "</tr>" return render(request,'index.html',{"time_choice":time_choice,"htmls":htmls,})
index前端ajax
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>index</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/datetimepicker/bootstrap-datetimepicker.min.css"> <style> .td_active{ background-color: purple; } #my_div{ top: 215px!important; } </style> </head> <body> <div class="page-header"> <h1 class="text-center">歡迎來到會議室預訂系統 <small class="text-info">{{ request.user.username }}</small></h1> </div> <div class="text-center"> <span>當前用戶:<img src="/static/img/info.png" alt=""></span> <span>其餘用戶:<img src="/static/img/success.png" alt=""></span> </div> <br> <br> <p class="text-center"> <span><a href="/home/">返回首頁</a></span> <span><a href="/logout/">註銷</a></span> </p> <div class="calender pull-right"> <div class='input-group' style="width: 230px;"> <span class="text-warning">注意:當前日期高亮顯示</span> <input type='text' autocomplete="off" class="form-control" id='datetimepicker11' placeholder="請選擇日期"/> <span class="input-group-addon"> <span class="glyphicon glyphicon-calendar"> </span> </span> </div> </div> <br> <br> <table class="table table-bordered"> <thead> <tr> <th>會議室/時間</th> {% for row in time_choice %} <th>{{ row.1 }}</th> {% endfor %} </tr> </thead> <tbody> {{ htmls|safe }} </tbody> </table> <div >{% csrf_token %}</div> <div class="col-lg-offset-6" > <button class="btn btn-info book_btn">預訂</button> </div> <script src="/static/js/jquery-3.3.1.min.js"></script> <script src="/static/bootstrap/js/bootstrap.min.js"></script> <script src="/static/datetimepicker/bootstrap-datetimepicker.min.js"></script> <script src="/static/datetimepicker/bootstrap-datetimepicker.zh-CN.js"></script> <script> // 日期格式化方法 Date.prototype.yun = function (fmt) { //author:yun var o = { "M+": this.getMonth() + 1, //月份 "d+": this.getDate(), //日 "h+": this.getHours(), //小時 "m+": this.getMinutes(), //分 "s+": this.getSeconds(), //秒 "q+": Math.floor((this.getMonth() + 3) / 3), //季度 "S": this.getMilliseconds() //毫秒 }; if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); for (var k in o) if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); return fmt; }; TODAY_DATE=new Date().yun("yyyy-MM-dd");//獲取當前日期 var POST_DATA={ "ADD":{}, "DEL":{}, }; function TdClick() { $(".item").click(function () { var room_id = $(this).attr("room_id"); var time_id = $(this).attr("time_id"); //取消預訂 if($(this).hasClass("info")){ $(this).removeClass("info").empty(); if (POST_DATA.DEL[room_id]){ POST_DATA.DEL[room_id].push(time_id); } else {POST_DATA.DEL[room_id]=[time_id,];} } //取消臨時預訂 else if($(this).hasClass("td_active")){ $(this).removeClass("td_active"); //console.log(room_id,time_id) var index=$.inArray(time_id,POST_DATA.ADD[room_id]); POST_DATA.ADD[room_id].splice(index,1); //console.log(POST_DATA.ADD[room_id]); console.log(POST_DATA); } // 增長預訂 else { $(this).addClass("td_active"); if (POST_DATA.ADD[room_id]){ POST_DATA.ADD[room_id].push(time_id); } else {POST_DATA.ADD[room_id]=[time_id,];} console.log(POST_DATA); } }); }; TdClick(); // 日期 if (location.search.slice(11)){ CHOOSE_DATE = location.search.slice(11) } else { CHOOSE_DATE = new Date().yun('yyyy-MM-dd'); console.log(CHOOSE_DATE); } // 經過ajax發送數據到後端 $(".book_btn").click(function () { $.ajax({ url:"/book/", type:"post", data:{ choose_date:CHOOSE_DATE, csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(), post_data:JSON.stringify(POST_DATA), }, dataType:"json", success:function (data) { console.log(data); if(data.status==1){ alert("預訂成功"); location.href=""; }else if (data.status==2){ alert("未修改信息"); location.href=""; } else { alert("已經被預約") location.href="" } }, }); }); // 日曆插件 function book_query(e) { CHOOSE_DATE=e.date.yun("yyyy-MM-dd"); location.href="/index/?book_date="+CHOOSE_DATE; }; /** 判斷輸入框中輸入的日期格式爲yyyy-mm-dd和正確的日期 */ function isDate(data){ var filter = /((^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(10|12|0?[13578])([-\/\._])(3[01]|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(11|0?[469])([-\/\._])(30|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(0?2)([-\/\._])(2[0-8]|1[0-9]|0?[1-9])$)|(^([2468][048]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([3579][26]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][13579][26])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][13579][26])([-\/\._])(0?2)([-\/\._])(29)$))/; if (filter.test(data)){ return true; }else { return false; } } $("#datetimepicker11").change(function () { var test = $(this).val(); if(isDate(test)){ if(test<TODAY_DATE){ alert("注意:日期不能小於當前日期!") } CHOOSE_DATE=test; location.href="/index/?book_date="+CHOOSE_DATE; }else { alert("日期格式錯誤!"); location.href=''; } }); $('#datetimepicker11').datetimepicker({ minView : 2, startView:2, language: "zh-CN", sideBySide: true, format: 'yyyy-mm-dd', startDate: TODAY_DATE, todayBtn:true, todayHighlight: 1,//當天日期高亮 enterLikeTab: false, bootcssVer:3, autoclose:true, }).on('changeDate',book_query).val(CHOOSE_DATE).css('font-weight','bold'); $(".datetimepicker.datetimepicker-dropdown-bottom-right.dropdown-menu").attr("id" ,"my_div"); </script> </body> </html>
注意:
(1)數據處理仍是在後臺更加方便,前臺渲染後臺傳遞來的標籤字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<table
class
=
"table table-bordered table-striped"
>
<thead>
<tr>
<th>會議室時間<
/
th>
{
%
for
time_choice
in
time_choices
%
}
{
# 在元組中取第二個值 #}
<th>{{ time_choice.
1
}}<
/
th>
{
%
endfor
%
}
<
/
tr>
<
/
thead>
<tbody>
{
# 因爲模板語法功能不夠強大,所以數據處理仍是放在後臺,在這裏渲染後臺傳遞來的標籤字符串 #}
{{ htmls|safe }}
<
/
tbody>
<
/
table>
|
因爲模板語法功能不夠強大,所以數據處理仍是放在後臺,在這裏渲染後臺傳遞來的標籤字符串。
(2)視圖函數字符串處理,運用format格式化函數
1
2
3
4
5
6
7
8
9
10
11
12
|
def
index(request):
# 拿到預約表中的時間段
time_choices
=
Book.time_choices
# 拿到全部的會議室
room_list
=
Room.objects.
all
()
# 構建標籤
htmls
=
""
for
room
in
room_list:
# 第一列td完成後,還有其餘td標籤須要添加,所以此處沒有閉合tr
htmls
+
=
"<tr><td>{}({})</td>"
.
format
(room.caption, room.num)
return
render(request,
"index.html"
,
locals
())
|
顯示效果:
(3)循環會議室生成行,循環時段生成列,標籤字符串拼接處理
def index(request): # 拿到預約表中的時間段 time_choices = Book.time_choices # 拿到全部的會議室 room_list = Room.objects.all() # 構建標籤 htmls = "" for room in room_list: # 有多少會議室生成多少行, # 每行僅生成了第一列。還有其餘td標籤須要添加,所以此處沒有閉合tr htmls += "<tr><td>{}({})</td>".format(room.caption, room.num) for time_choice in time_choices: # 有多少時段就生成多少列 # 一次循環就是一個td標籤 htmls += "<td></td>" # 循環完成後閉合tr標籤 htmls += "</tr>" return render(request, "index.html", locals())
好比會議室有3個,循環會議室生成三行,且拿到會議室名稱和人數限制生成首列;再循環時段,這裏有13個時段,所以生成13列,13個td標籤依次添加進一個tr中,顯示效果以下:
(4)給td標籤添加room_id和time_id屬性
for time_choice in time_choices: # 有多少時段就生成多少列 # 一次循環就是一個td標籤 htmls += "<td room_id={} time_id={}></td>".format(room.pk, time_choice[0])
這樣點擊單元格可肯定點擊的是哪一個會議室哪個時段的單元格,效果以下所示:
(5)獲取預定日期信息
import datetime def index(request): # 取當前日期 date = datetime.datetime.now().date() print(date) # 取預定日期,沒有指定取當前日期 book_date = request.GET.get("book_date", date) print(book_date)
index頁面訪問中,若是沒有指定日期,默認顯示的就是當前日的預約信息。
所以在循環生成表格時,能夠循環肯定單元格是否被預約,已經被預約的添加class=‘success’屬性。
# 構建標籤 htmls = "" for room in room_list: # 有多少會議室生成多少行, # 每行僅生成了第一列。還有其餘td標籤須要添加,所以此處沒有閉合tr htmls += "<tr><td>{}({})</td>".format(room.caption, room.num) for time_choice in time_choices: # 有多少時段就生成多少列 flag = False # False表明沒有預約,True表明已經預約 for book in book_list: # 循環肯定單元格是否被預約 if book.room.pk == room.pk and book.time_id == time_choice[0]: # 符合條件說明當前時段會議室已經被預約 flag = True break if flag: # 已經被預約,添加class='success' htmls += "<td class='success' room_id={} time_id={}></td>".format(room.pk, time_choice[0]) else: htmls += "<td room_id={} time_id={}></td>".format(room.pk, time_choice[0]) # 循環完成後閉合tr標籤 htmls += "</tr>"
(6)在預約單元格添加預約人姓名,並根據登陸人判斷顯示單元格
if flag: # 已經被預約,添加class='active' if request.user.pk == book.user.pk: # 當前登陸人查看本身的預定信息 htmls += "<td class='info item' room_id={} time_id={}>{}</td>".format(room.pk, time_choice[0], book.user.username) else: # 非當前登陸人本身的預定信息 htmls += "<td class='success item' room_id={} time_id={}>{}</td>".format(room.pk, time_choice[0], book.user.username) else: htmls += "<td room_id={} time_id={}></td>".format(room.pk, time_choice[0])
顯示效果以下:
5、前端部分數據處理(index.html)
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>index</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/datetimepicker/bootstrap-datetimepicker.min.css"> <style> .td_active{ background-color: purple; } #my_div{ top: 215px!important; } </style> </head> <body> <div class="page-header"> <h1 class="text-center">歡迎來到會議室預訂系統 <small class="text-info">{{ request.user.username }}</small></h1> </div> <div class="text-center"> <span>當前用戶:<img src="/static/img/info.png" alt=""></span> <span>其餘用戶:<img src="/static/img/success.png" alt=""></span> </div> <br> <br> <p class="text-center"> <span><a href="/home/">返回首頁</a></span> <span><a href="/logout/">註銷</a></span> </p> <div class="calender pull-right"> <div class='input-group' style="width: 230px;"> <span class="text-warning">注意:當前日期高亮顯示</span> <input type='text' autocomplete="off" class="form-control" id='datetimepicker11' placeholder="請選擇日期"/> <span class="input-group-addon"> <span class="glyphicon glyphicon-calendar"> </span> </span> </div> </div> <br> <br> <table class="table table-bordered"> <thead> <tr> <th>會議室/時間</th> {% for row in time_choice %} <th>{{ row.1 }}</th> {% endfor %} </tr> </thead> <tbody> <!--因爲模板語法功能不夠強大,所以數據處理仍是放在後臺,在這裏渲染後臺傳遞來的標籤字符串--> {{ htmls|safe }} </tbody> </table> <div >{% csrf_token %}</div> <div class="col-lg-offset-6" > <button class="btn btn-info book_btn">預訂</button> </div> <script src="/static/js/jquery-3.3.1.min.js"></script> <script src="/static/bootstrap/js/bootstrap.min.js"></script> <script src="/static/datetimepicker/bootstrap-datetimepicker.min.js"></script> <script src="/static/datetimepicker/bootstrap-datetimepicker.zh-CN.js"></script> <script> // 日期格式化方法 Date.prototype.yun = function (fmt) { //author:yun var o = { "M+": this.getMonth() + 1, //月份 "d+": this.getDate(), //日 "h+": this.getHours(), //小時 "m+": this.getMinutes(), //分 "s+": this.getSeconds(), //秒 "q+": Math.floor((this.getMonth() + 3) / 3), //季度 "S": this.getMilliseconds() //毫秒 }; if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); for (var k in o) if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); return fmt; }; TODAY_DATE=new Date().yun("yyyy-MM-dd");//獲取當前日期 var POST_DATA={ "ADD":{}, "DEL":{}, }; function TdClick() { $(".item").click(function () { var room_id = $(this).attr("room_id"); var time_id = $(this).attr("time_id"); //取消預訂 if($(this).hasClass("info")){ // 若是點擊的標籤具備info類,直接刪除info類並清空內容 $(this).removeClass("info").empty(); if (POST_DATA.DEL[room_id]){ // 在數據中已經存有會議室信息,將新單元格time_id添加進數組 POST_DATA.DEL[room_id].push(time_id); } else // 在數據中沒有存過對應會議室記錄,直接將time_id對其賦值建立一個字典 {POST_DATA.DEL[room_id]=[time_id,];} } //取消臨時預訂 else if($(this).hasClass("td_active")){ $(this).removeClass("td_active"); //點擊刪除臨時預訂的數據 var index=$.inArray(time_id,POST_DATA.ADD[room_id]); POST_DATA.ADD[room_id].splice(index,1); } // 增長預訂 else { $(this).addClass("td_active"); if (POST_DATA.ADD[room_id]){ // 在數據中已經存有會議室信息,將新單元格time_id添加進數組 POST_DATA.ADD[room_id].push(time_id); } else // 在數據中沒有存過對應會議室記錄,直接將time_id對其賦值建立一個字典 {POST_DATA.ADD[room_id]=[time_id,];} } }); }; TdClick(); // 日期 if (location.search.slice(11)){ CHOOSE_DATE = location.search.slice(11) } else { CHOOSE_DATE = new Date().yun('yyyy-MM-dd'); } // 經過ajax發送數據到後端 $(".book_btn").click(function () { $.ajax({ url:"/book/", type:"post", data:{ choose_date:CHOOSE_DATE, csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(), post_data:JSON.stringify(POST_DATA), }, dataType:"json", success:function (data) { console.log(data); if(data.status==1){ alert("預訂成功"); location.href=""; }else if (data.status==2){ alert("未修改信息"); location.href=""; } else { alert("已經被預約") location.href="" } }, }); }); // 日曆插件 function book_query(e) { CHOOSE_DATE=e.date.yun("yyyy-MM-dd"); location.href="/index/?book_date="+CHOOSE_DATE; }; /** 判斷輸入框中輸入的日期格式爲yyyy-mm-dd和正確的日期 */ function isDate(data){ var filter = /((^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(10|12|0?[13578])([-\/\._])(3[01]|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(11|0?[469])([-\/\._])(30|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(0?2)([-\/\._])(2[0-8]|1[0-9]|0?[1-9])$)|(^([2468][048]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([3579][26]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][13579][26])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][13579][26])([-\/\._])(0?2)([-\/\._])(29)$))/; if (filter.test(data)){ return true; }else { return false; } } $("#datetimepicker11").change(function () { var test = $(this).val(); if(isDate(test)){ if(test<TODAY_DATE){ alert("注意:日期不能小於當前日期!") } CHOOSE_DATE=test; location.href="/index/?book_date="+CHOOSE_DATE; }else { alert("日期格式錯誤!"); location.href=''; } }); $('#datetimepicker11').datetimepicker({ minView : 2, startView:2, language: "zh-CN", sideBySide: true, format: 'yyyy-mm-dd', startDate: TODAY_DATE, todayBtn:true, todayHighlight: 1,//當天日期高亮 enterLikeTab: false, bootcssVer:3, autoclose:true, }).on('changeDate',book_query).val(CHOOSE_DATE).css('font-weight','bold'); $(".datetimepicker.datetimepicker-dropdown-bottom-right.dropdown-menu").attr("id" ,"my_div"); </script> </body> </html>
一、點擊事件預約和取消——組織數據
var POST_DATA={ "ADD":{}, "DEL":{}, }; function TdClick() { $(".item").click(function () { var room_id = $(this).attr("room_id"); var time_id = $(this).attr("time_id"); //取消預訂 if($(this).hasClass("info")){ // 若是點擊的標籤具備info類,直接刪除info類並清空內容 $(this).removeClass("info").empty(); if (POST_DATA.DEL[room_id]){ // 在數據中已經存有會議室信息,將新單元格time_id添加進數組 POST_DATA.DEL[room_id].push(time_id); } else // 在數據中沒有存過對應會議室記錄,直接將time_id對其賦值建立一個字典 {POST_DATA.DEL[room_id]=[time_id,];} } //取消臨時預訂 else if($(this).hasClass("td_active")){ $(this).removeClass("td_active"); //點擊刪除臨時預訂的數據 var index=$.inArray(time_id,POST_DATA.ADD[room_id]); POST_DATA.ADD[room_id].splice(index,1); } // 增長預訂 else { $(this).addClass("td_active"); if (POST_DATA.ADD[room_id]){ // 在數據中已經存有會議室信息,將新單元格time_id添加進數組 POST_DATA.ADD[room_id].push(time_id); } else // 在數據中沒有存過對應會議室記錄,直接將time_id對其賦值建立一個字典 {POST_DATA.ADD[room_id]=[time_id,];} } }); }; TdClick();
注意:
(1)取消預約事件
<script> // 爲td綁定單擊事件 function BindTd() { $('.item').click(function () { // alert($(this).attr("room_id")); // 點擊顯示會議室id // 取消預約 if ($(this).hasClass("info")){ // 若是點擊的標籤具備active類,直接刪除active類並清空內容 $(this).removeClass("info").empty(); } else if ($(this).hasClass("td_active")) { $(this).removeClass("td_active"); } else { // 空白局域點擊 $(this).addClass("td_active"); } }) } BindTd(); </script>
在此次只處理了具備info類和td_active類的狀況,但沒有處理success類的狀況,由於這種須要判斷的狀況,必定要交給後端,不然就是前端一套後端一套,點擊保存按鈕發送的js,客戶能夠假裝一個data發送給服務器,若是不作聯合惟一,徹底交給前端會形成很嚴重的安全問題。
(2)數據組織和添加預約
建立以下所示用js字面量方式建立對象
POST_DATA,有兩個屬性(對象)ADD和DEL,這兩個對象的值以room_id爲鍵,以time_id爲值:
<script> // room_id 爲鍵,time_id 爲值 {1:[4,5],2:[4,] } {3:[9,10]} var POST_DATA = { "ADD":{}, "DEL":{} }; </script>
在空白單元格點擊,獲取添加數據到POST_DATA中,以完成預約工做:
function TdClick() { $(".item").click(function () { var room_id = $(this).attr("room_id"); var time_id = $(this).attr("time_id"); //取消預訂 if($(this).hasClass("info")){ // 若是點擊的標籤具備info類,直接刪除info類並清空內容 $(this).removeClass("info").empty(); if (POST_DATA.DEL[room_id]){ // 在數據中已經存有會議室信息,將新單元格time_id添加進數組 POST_DATA.DEL[room_id].push(time_id); } else // 在數據中沒有存過對應會議室記錄,直接將time_id對其賦值建立一個字典 {POST_DATA.DEL[room_id]=[time_id,];} } //取消臨時預訂 else if($(this).hasClass("td_active")){ $(this).removeClass("td_active"); //點擊刪除臨時預訂的數據 var index=$.inArray(time_id,POST_DATA.ADD[room_id]); POST_DATA.ADD[room_id].splice(index,1); } // 增長預訂 else { $(this).addClass("td_active"); if (POST_DATA.ADD[room_id]){ // 在數據中已經存有會議室信息,將新單元格time_id添加進數組 POST_DATA.ADD[room_id].push(time_id); } else // 在數據中沒有存過對應會議室記錄,直接將time_id對其賦值建立一個字典 {POST_DATA.ADD[room_id]=[time_id,];} console.log(POST_DATA.ADD); } }); }; TdClick();
(3)臨時預約取消數據處理
//取消臨時預訂 else if($(this).hasClass("td_active")){ $(this).removeClass("td_active"); //點擊刪除臨時預訂的數據 var index=$.inArray(time_id,POST_DATA.ADD[room_id]); POST_DATA.ADD[room_id].splice(index,1); }
(4)js數組操做經常使用方法
1 // shift:刪除原數組第一項,並返回刪除元素的值;若是數組爲空則返回undefined 2 var a = [1,2,3,4,5]; 3 var b = a.shift(); //a:[2,3,4,5] b:1 4 5 // pop:刪除原數組最後一項,並返回刪除元素的值;若是數組爲空則返回undefined 6 var a = [1,2,3,4,5]; 7 var b = a.pop(); //a:[1,2,3,4] b:5 8 9 // push:將參數添加到原數組末尾,並返回數組的長度 10 var a = [1,2,3,4,5]; 11 var b = a.push(6,7); //a:[1,2,3,4,5,6,7] b:7 12 13 // concat:返回一個新數組,是將參數添加到原數組中構成的 14 var a = [1,2,3,4,5]; 15 var b = a.concat(6,7); //a:[1,2,3,4,5] b:[1,2,3,4,5,6,7] 16 17 // splice(start,deleteCount,val1,val2,...):從start位置開始刪除deleteCount項,並從該位置起插入val1,val2,... 18 var a = [1,2,3,4,5]; 19 var b = a.splice(2,2,7,8,9); //a:[1,2,7,8,9,5] b:[3,4] 20 var b = a.splice(0,1); //同shift 21 a.splice(0,0,-2,-1); var b = a.length; //同unshift 22 var b = a.splice(a.length-1,1); //同pop 23 a.splice(a.length,0,6,7); var b = a.length; //同push 24 25 // reverse:將數組反序 26 // sort(orderfunction):按指定的參數對數組進行排序 27 28 // slice(start,end):返回從原數組中指定開始下標到結束下標之間的項組成的新數組 29 var a = [1,2,3,4,5]; 30 var b = a.slice(2,5); //a:[1,2,3,4,5] b:[3,4,5] 31 32 // join(separator):將數組的元素組起一個字符串,以separator爲分隔符,省略的話則用默認用逗號爲分隔符 33 var a = [1,2,3,4,5]; 34 var b = a.join("|"); //a:[1,2,3,4,5] b:"1|2|3|4|5"
網絡編程本質是瀏覽器和服務器之間發送字符串,以POST請求爲例
POST: 瀏覽器-------------------->server "請求首行\r\nContent-Type:url_encode\r\n\r\na=1&b=2" "請求首行\r\nContent-Type:application/json\r\n\r\n'{"a":1,"b":2}'" 在django的wsgi的request中: request.body:元數據'{"a":1,"b":2}' if 請求頭中的Content-Type==url_encode: request.POST=解碼a=1&b=2
二、日曆插件(datetimepicker)官方文檔:http://eonasdan.github.io/bootstrap-datetimepicker/
日曆html
<div class="calender pull-right"> <div class='input-group' style="width: 230px;"> <span class="text-warning">注意:當前日期高亮顯示</span> <input type='text' autocomplete="off" class="form-control" id='datetimepicker11' placeholder="請選擇日期"/> <span class="input-group-addon"> <span class="glyphicon glyphicon-calendar"> </span> </span> </div> </div>
日曆js代碼
// 日期格式化方法 Date.prototype.yun = function (fmt) { //author:yun var o = { "M+": this.getMonth() + 1, //月份 "d+": this.getDate(), //日 "h+": this.getHours(), //小時 "m+": this.getMinutes(), //分 "s+": this.getSeconds(), //秒 "q+": Math.floor((this.getMonth() + 3) / 3), //季度 "S": this.getMilliseconds() //毫秒 }; if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); for (var k in o) if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); return fmt; }; TODAY_DATE=new Date().yun("yyyy-MM-dd");//獲取當前日期 // 日期 if (location.search.slice(11)){ CHOOSE_DATE = location.search.slice(11) } else { CHOOSE_DATE = new Date().yun('yyyy-MM-dd'); } // 日曆插件 function book_query(e) { CHOOSE_DATE=e.date.yun("yyyy-MM-dd"); location.href="/index/?book_date="+CHOOSE_DATE; }; /** 判斷輸入框中輸入的日期格式爲yyyy-mm-dd和正確的日期 */ function isDate(data){ var filter = /((^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(10|12|0?[13578])([-\/\._])(3[01]|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(11|0?[469])([-\/\._])(30|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(0?2)([-\/\._])(2[0-8]|1[0-9]|0?[1-9])$)|(^([2468][048]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([3579][26]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][13579][26])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][13579][26])([-\/\._])(0?2)([-\/\._])(29)$))/; if (filter.test(data)){ return true; }else { return false; } } $("#datetimepicker11").change(function () { var test = $(this).val(); if(isDate(test)){ if(test<TODAY_DATE){ alert("注意:日期不能小於當前日期!") } CHOOSE_DATE=test; location.href="/index/?book_date="+CHOOSE_DATE; }else { alert("日期格式錯誤!"); location.href=''; } }); //初始化日曆 $('#datetimepicker11').datetimepicker({ minView : 2, startView:2, language: "zh-CN", sideBySide: true, format: 'yyyy-mm-dd', startDate: TODAY_DATE, todayBtn:true, todayHighlight: 1,//當天日期高亮 enterLikeTab: false, bootcssVer:3, autoclose:true, }).on('changeDate',book_query).val(CHOOSE_DATE).css('font-weight','bold'); $(".datetimepicker.datetimepicker-dropdown-bottom-right.dropdown-menu").attr("id" ,"my_div");
三、發送AJAX
// 經過ajax發送數據到後端 $(".book_btn").click(function () { $.ajax({ url:"/book/", type:"post", data:{ choose_date:CHOOSE_DATE, csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(), post_data:JSON.stringify(POST_DATA), }, dataType:"json", success:function (data) { console.log(data); if(data.status==1){ alert("預訂成功"); location.href=""; }else if (data.status==2){ alert("未修改信息"); location.href=""; } else { alert("已經被預約") location.href="" } }, }); });
6、視圖處理圖書預約和取消
def book(request): if request.method == "POST": choose_date = request.POST.get("choose_date") print("choose_date:", choose_date) # 獲取會議室時間段列表 time_choice = models.Book.time_choice try: # 向數據庫修改會議室預訂記錄 post_data = json.loads(request.POST.get("post_data")) if not post_data["ADD"] and not post_data["DEL"]: res = {"status":2, "msg":""} return HttpResponse(json.dumps(res)) user = request.user print(type(post_data), post_data) # 添加新的預訂信息 book_list = [] for room_id, time_id_list in post_data["ADD"].items(): for time_id in time_id_list: book_obj = models.Book(user=user, room_id=room_id, time_id=time_id, date=choose_date) book_list.append(book_obj) models.Book.objects.bulk_create(book_list) # 刪除舊的預訂信息 from django.db.models import Q remove_book = Q() for room_id,time_id_list in post_data["DEL"].items(): temp = Q() for time_id in time_id_list: temp.children.append(("room_id", room_id)) temp.children.append(("time_id", time_id)) temp.children.append(("user_id", request.user.pk)) temp.children.append(("date", choose_date)) remove_book.add(temp, "OR") if remove_book: models.Book.objects.filter(remove_book).delete() for time in post_data["DEL"][room_id]: models.Book.objects.filter(user=user, room_id=room_id, time_id=time, date=choose_date).delete() res = {"status": 1, "msg": ''} except Exception as e: res = {"status": 0, "msg": str(e)} return HttpResponse(json.dumps(res))
7、用戶註冊
註冊前端頁面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>歡迎註冊</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css"> <style> html, body { width: 100%; height: 100%; } #avatar-img { width: 80px; height: 80px; } .mui-content { background: url("/static/img/reg_bak.jpg") bottom center no-repeat #efeff4; background-size: 100% 100%; width: 100%; height: 100%; } </style> </head> <body> <div class="container mui-content"> <h3 class="text-center" style="color: orangered">歡迎註冊會議室預訂系統</h3> <div class="row"> <div class="col-md-6 col-md-offset-3"> <form novalidate action="/reg/" method="post" autocomplete="off" class="form-horizontal reg-form" enctype="multipart/form-data"> {% csrf_token %} <!--頭像--> <div class="form-group text-center" style="margin-top: 80px"> <label class="col-sm-2 control-label">頭像</label> <div class="col-sm-8"> <label for="id_avatar"><img id="avatar-img" src="/static/img/timg.jpg" alt=""></label> <input accept="image/*" type="file" name="avatar" id="id_avatar" style="display: none"> <span class="help-block"></span> </div> </div> <!--用戶名--> <div class="form-group"> <label for="{{ form_obj.username.id_for_label }}" class="col-sm-2 control-label">{{ form_obj.username.label }}</label> <div class="col-sm-8"> {{ form_obj.username }} <span class="help-block">{{ form_obj.username.errors.0 }}</span> </div> </div> <!--郵箱--> <div class="form-group"> <label for="{{ form_obj.email.id_for_label }}" class="col-sm-2 control-label">{{ form_obj.email.label }}</label> <div class="col-sm-8"> {{ form_obj.email }} <span class="help-block">{{ form_obj.email.errors.0 }}</span> </div> </div> <!--密碼--> <div class="form-group"> <label for="{{ form_obj.password.id_for_label }}" class="col-sm-2 control-label">{{ form_obj.password.label }}</label> <div class="col-sm-8"> {{ form_obj.password }} <span class="help-block">{{ form_obj.password.errors.0 }}</span> </div> </div> <!--確認密碼--> <div class="form-group"> <label for="{{ form_obj.re_password.id_for_label }}" class="col-sm-2 control-label">{{ form_obj.re_password.label }}</label> <div class="col-sm-8"> {{ form_obj.re_password }} <span class="help-block">{{ form_obj.re_password.errors.0 }}</span> </div> </div> <!--註冊--> <div class="form-group"> <div class="col-sm-offset-4 col-sm-12"> <button type="button" class="btn btn-success" id="reg-submit">註冊</button> <input type="reset" class="btn btn-danger" value="重置"> <a class="panel-warning" href="/home/">返回首頁</a> </div> </div> </form> </div> </div> </div> <script src="/static/js/jquery-3.3.1.min.js"></script> <script src="/static/bootstrap/js/bootstrap.min.js"></script> <script> // 找到頭像的input標籤綁定change事件 $("#id_avatar").change(function () { // 1. 建立一個讀取文件的對象 var fileReader = new FileReader(); // 取到當前選中的頭像文件 // console.log(this.files[0]); // 讀取你選中的那個文件 fileReader.readAsDataURL(this.files[0]); // 讀取文件是須要時間的 fileReader.onload = function () { // 2. 等上一步讀完文件以後才 把圖片加載到img標籤中 $("#avatar-img").attr("src", fileReader.result); }; }); // AJAX提交註冊的數據 $("#reg-submit").click(function () { // 取到用戶填寫的註冊數據,向後端發送AJAX請求 var formData = new FormData(); formData.append("username", $("#id_username").val()); formData.append("password", $("#id_password").val()); formData.append("re_password", $("#id_re_password").val()); formData.append("email", $("#id_email").val()); formData.append("avatar", $("#id_avatar")[0].files[0]); formData.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val()); $.ajax({ url: "/reg/", type: "post", processData: false, //告訴Jquery不要處理個人數據 contentType: false, //告訴jQuery不要設置content類型 data: formData, success:function (data) { if (data.status){ // 有錯誤就展現錯誤 // console.log(data.msg); // 將報錯信息填寫到頁面上 $.each(data.msg, function (k,v) { // console.log("id_"+k, v[0]); // console.log($("#id_"+k)); $("#id_"+k).next("span").text(v[0]).parent().parent().addClass("has-error"); }) }else { // 沒有錯誤就跳轉到指定頁面 location.href = data.msg; } } }) }); // 將全部的input框綁定獲取焦點的事件,將全部的錯誤信息清空 $("form input").focus(function () { $(this).next().text("").parent().parent().removeClass("has-error"); }); //給username的input輸入框,綁定失去焦點事件,失去焦點後檢測用戶名是否存在 $("#id_username").blur(function () { // 取到用戶填寫的值 var username = $(this).val(); //發ajax請求 $.ajax({ url:"/check_username_exist/", type:"get", data:{username:username,}, success:function (data) { if(data.status){ //有錯誤,用戶名已被註冊 $("#id_username").next().text(data.msg).parent().parent().addClass("has-error"); } } }) }) </script> </body> </html>
註冊後端頁面
def reg(request): if request.method == "POST": ret = {"status": 0, "msg": ""} form_obj = forms.RegForm(request.POST) print('request.POST'.center(80, '#')) print(request.POST) print('request.POST'.center(80, '#')) avatar_img = request.FILES.get("avatar") print(avatar_img) # 幫我作校驗 if form_obj.is_valid(): # 校驗經過,去數據庫建立一個新的用戶 form_obj.cleaned_data.pop("re_password") print(form_obj.cleaned_data) try: models.UserInfo.objects.create_user(**form_obj.cleaned_data,avatar=avatar_img) except Exception as e: print(e) ret["msg"] = "/login/" return JsonResponse(ret) else: print(form_obj.errors) ret["status"] = 1 ret["msg"] = form_obj.errors print(ret) print("=" * 120) return JsonResponse(ret) # 生成一個form對象 form_obj = forms.RegForm() print(form_obj.fields) return render(request,'reg.html',{"form_obj": form_obj})