基於django的會議室預訂系統

會議室預訂系統

1、目標及業務流程

指望效果:javascript

 

業務流程:

  1. 用戶註冊

  2. 用戶登陸

  3. 預訂會議室

  4. 退訂會議室

  5. 選擇日期;今日以及之後日期

 

 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>
View Code

 

 

 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)
View Code

 

 注意: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,})
View Code

 

 

 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>
View Code

 

 

注意:

(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>
index.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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                        <input type="reset" class="btn btn-danger" value="重置">&nbsp;&nbsp;&nbsp;&nbsp;
                        <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>
reg.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})
View Code

 

 

8、項目源碼

https://github.com/Yun-Wangjun/BookSystem

相關文章
相關標籤/搜索