会议室预订系统(meeting room booking system)
一、目标及业务流程
期望效果:
业务流程:
- 用户登录
- 预定会议室
- 退订会议室
- 选择日期;今日以及以后日期
二、表结构设计和生成
1、models.py(用户继承AbstractUser)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | from django.db import models # Create your models here. from django.db import models from django.contrib.auth.models import AbstractUser class UserInfo(AbstractUser): """用户信息表""" tel = models.CharField(max_length = 32 ) class Room(models.Model): """会议室表""" caption = models.CharField(max_length = 32 ) # 会议室名字 num = models.IntegerField() # 会议室容纳人数 def __str__( self ): return self .caption class Book(models.Model): """会议室预订信息""" user = models.ForeignKey( "UserInfo" , on_delete = models.CASCADE) # CASCADE级联删除 room = models.ForeignKey( "Room" , on_delete = models.CASCADE) date = models.DateField() # 日期 time_choices = ( # 时段 ( 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' ), ) time_id = models.IntegerField(choices = time_choices) # 存数字,choices参数 class Meta: unique_together = ( # 三个联合唯一,防止有人重复预定 ( 'room' , 'date' , 'time_id' ), ) def __str__( self ): return str ( self .user) + "预定了" + str ( self .room) |
注意:
(1)Django中提供了AbstractUser类,可以用来自由定制需要的model
1 2 3 4 5 | from django.contrib.auth.models import AbstractUser class UserInfo(AbstractUser): """用户信息表""" tel = models.CharField(max_length = 32 ) |
如上所示,即可在Django的基础上添加我们所需要的信息。
(2)设置model的时候,设置三个字段联合唯一
1 2 3 4 5 6 7 | class Book(models.Model): """会议室预订信息""" .... class Meta: unique_together = ( # 三个联合唯一,防止有人重复预定 ( 'room' , 'date' , 'time_id' ), ) |
存的是key 显示的是value,且只能存key。
2、修改配置文件settings.py,覆盖默认的User模型
Django允许你通过修改setting.py文件中的 AUTH_USER_MODEL 设置覆盖默认的User模型,其值引用一个自定义的模型。
1 | AUTH_USER_MODEL = "app01.UserInfo" |
上面的值表示Django应用的名称(必须位于INSTALLLED_APPS中)和你想使用的User模型的名称。
注意:在创建任何迁移或者第一次运行 manager.py migrate 前设置 AUTH_USER_MODEL。
设置AUTH_USER_MODEL数据库结构有很大的影响。改变了一些会使用到的表格,并且会影响到一些外键和多对多关系的构造。在你有表格被创建后更改此设置是不被 makemigrations 支持的,并且会导致你需要手动修改数据库结构,从旧用户表中导出数据,可能重新应用一些迁移。
3、数据迁移及创建超级用户
1 2 | $ python3 manage.py makemigrations $ python3 manage.py migrate |
这里遇到了一个问题:创建项目时没有创建应用,手动通过manage.py startapp user创建子项目,修改AUTH_USER_MODEL后执行makemigrations一直报错,找不到app01。
创建两个超级用户:
1 2 3 4 5 6 7 | MacBook-Pro:MRBS hqs$ python3 manage.py createsuperuser Username: yuan Password:yuan1234 MacBook-Pro:MRBS hqs$ python3 manage.py createsuperuser Username: alex Password:alex1234 |
三、系统登录login
urls.py:
1 2 3 4 5 6 7 8 9 10 | from django.contrib import admin from django.urls import path from app01 import views urlpatterns = [ path( 'admin/' , admin.site.urls), path( 'login/' , views.login), path( 'index/' , views.index), path( 'book/' , views.book), ] |
简单login.html:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <!DOCTYPE html> <html lang = "en" > <head> <meta charset = "UTF-8" > <title>Title< / title> < / head> <body> <form action = " " method=" post"> 用户名:< input type = "text" name = "user" > 密码:< input type = "password" name = "pwd" > < input type = "submit" > < / form> < / body> < / html> |
login视图函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | from django.shortcuts import render, redirect # Create your views here. from django.contrib import auth def login(request): if request.method = = "POST" : user = request.POST.get( "user" ) pwd = request.POST.get( "pwd" ) user = auth.authenticate(username = user, password = pwd) if user: # 登录成功 auth.login(request, user) # 注册request.user,可以拿到登录用户对象所有信息 redirect( "/index/" ) return render(request, "login.html" ) |
注意:auth模块的authenticate()方法,提供了用户认证,如果认证信息有效,会返回一个 User 对象;如果认证失败,则返回None。
四、index部分
1、引入admin组件(后台数据管理组件)并完成admin注册
admin.py:
1 2 3 4 5 6 7 | from django.contrib import admin # Register your models here. from app01.models import * admin.site.register(UserInfo) admin.site.register(Book) admin.site.register(Room) |
2、在数据库添加数据
3、index视图函数数据处理和index.html模板渲染

def index(request): # 取当前日期 date = datetime.datetime.now().date() print(date) # 2018-08-17 # 取预约日期,没有指定取当前日期 book_date = request.GET.get("book_date", date) print(book_date) # 2018-08-17 # 拿到预定表中的时段 time_choices = Book.time_choices # 拿到所有的会议室 room_list = Room.objects.all() # 拿到预定信息 book_list = Book.objects.filter(date=book_date) # 构建标签 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 print(book) # 这个book是预定信息 if flag: # 已经被预定,添加class='active' if request.user.pk == book.user.pk: # 当前登录人查看自己的预约信息 htmls += "<td class='active' room_id={} time_id={}>{}</td>".format(room.pk, time_choice[0], book.user.username) else: # 非当前登录人自己的预约信息 htmls += "<td class='another_active' 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]) # 循环完成后闭合tr标签 htmls += "</tr>" return render(request, "index.html", locals())

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css"> <script src="/static/js/jquery-1.12.4.min.js"></script> <script src="/static/datetimepicker/bootstrap-datetimepicker.min.js"></script> <script src="/static/datetimepicker/bootstrap-datetimepicker.zh-CN.js"></script> <style> .active { background-color: green!important; color: white; } .another_active { background-color: #0f5687; color: white; } </style> </head> <body> <H3>会议室预定</H3> <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> </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 ()) |
显示效果:
这是Python2.6后新增了一种格式化字符串的函数 str.format(),它增强了字符串格式化的功能。基本语法是通过 {} 和 : 来代替以前的 % 。format 函数可以接受不限个参数,位置可以不按顺序。
1 2 3 4 5 6 7 8 | >>> "{} {}" . format ( "hello" , "world" ) # 不设置指定位置,按默认顺序 'hello world' >>> "{0} {1}" . format ( "hello" , "world" ) # 设置指定位置 'hello world' >>> "{1} {0} {1}" . format ( "hello" , "world" ) # 设置指定位置 'world hello world' |
还可以设置参数:
1 2 3 4 5 6 7 8 9 | print ( "网站名:{name}, 地址 {url}" . format (name = "菜鸟教程" , url = "www.runoob.com" )) # 通过字典设置参数 site = { "name" : "菜鸟教程" , "url" : "www.runoob.com" } print ( "网站名:{name}, 地址 {url}" . format ( * * site)) # 通过列表索引设置参数 my_list = [ '菜鸟教程' , 'www.runoob.com' ] print ( "网站名:{0[0]}, 地址 {0[1]}" . format (my_list)) # "0" 是必须的 |
(3)循环会议室生成行,循环时段生成列,标签字符串拼接处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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属性
1 2 3 | for time_choice in time_choices: # 有多少时段就生成多少列 # 一次循环就是一个td标签 htmls + = "<td room_id={} time_id={}></td>" . format (room.pk, time_choice[ 0 ]) |
这样点击单元格可确定点击的是哪个会议室哪一个时段的单元格,效果如下所示:
(5)获取预约日期信息
1 2 3 4 5 6 7 8 9 | import datetime def index(request): # 取当前日期 date = datetime.datetime.now().date() print (date) # 2018-08-17 # 取预约日期,没有指定取当前日期 book_date = request.GET.get( "book_date" , date) print (book_date) # 2018-08-17 |
index页面访问中,如果没有指定日期,默认显示的就是当前日的预定信息。
因此在循环生成表格时,可以循环确定单元格是否被预定,已经被预定的添加class=‘active’属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # 构建标签 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='active' htmls + = "<td class='active' 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)在预定单元格添加预定人姓名,并根据登录人判断显示单元格
1 2 3 4 5 6 7 8 9 10 11 12 | if flag: # 已经被预定,添加class='active' if request.user.pk = = book.user.pk: # 当前登录人查看自己的预约信息 htmls + = "<td class='active' room_id={} time_id={}>{}</td>" . format (room.pk, time_choice[ 0 ], book.user.username) else : # 非当前登录人自己的预约信息 htmls + = "<td class='another_active' 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 ]) |
在index中添加样式:
1 2 3 4 5 6 7 8 9 10 | <style> .active { background-color : green !important ; color : white ; } .another_active { background-color : #0f5687 ; color : white ; } </style> |
显示效果如下:
五、前端部分数据处理(index.html)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <title>Title</title> <link rel= "stylesheet" href= "/static/bootstrap/css/bootstrap.css" > <script src= "/static/js/jquery-1.12.4.min.js" ></script> <script src= "/static/datetimepicker/bootstrap-datetimepicker.min.js" ></script> <script src= "/static/datetimepicker/bootstrap-datetimepicker.zh-CN.js" ></script> <style> .active { background-color: green!important; color: white; } .another_active { background-color: #0f5687; color: white; } .td_active { background-color: lightblue; color: white; } </style> </head> <body> <H3>会议室预定</H3> <div class = "calender pull-right" > <div class = 'input-group' style= "width: 230px;" > <input type= 'text' class = "form-control" id= 'datetimepicker11' placeholder= "请选择日期" /> <span class = "input-group-addon" > <span class = "glyphicon glyphicon-calendar" ></span> </span> </div> </div> <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> <button class = "btn btn-success pull-right keep" >保存</button> <script> // 日期格式化方法 Date.prototype.yuan = function (fmt) { //author: meizz 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; }; // room_id 为键,time_id 为值 {1:[4,5],2:[4,] } {3:[9,10]} var POST_DATA = { "ADD" :{}, "DEL" :{} }; // 为td绑定单击事件 function BindTd() { $( '.item' ).click( function () { var room_id = $( this ).attr( "room_id" ); var time_id = $( this ).attr( "time_id" ); // 取消预定 if ($( this ).hasClass( "active" )){ // 如果点击的标签具有active类,直接删除active类并清空内容 $( this ).removeClass( "active" ).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" ); // 点击删除临时预定时添加的数据 // POST_DATA.ADD[room_id].pop(); // 这个是删除最后一个元素不对 POST_DATA.ADD[room_id].splice(POST_DATA.ADD[room_id].indexOf(time_id),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, ]; } } }) } BindTd(); // 日期 if (location.search.slice(11)){ CHOOSE_DATE = location.search.slice(11) } else { CHOOSE_DATE = new Date().yuan( 'yyyy-MM-dd' ); } // 发送ajax $( ".keep" ).click( function () { $.ajax({ url: "/book/" , type: "POST" , data:{ csrfmiddlewaretoken: '{{ csrf_token }}' , choose_date:CHOOSE_DATE, post_data:JSON.stringify(POST_DATA) }, dataType: "json" , success: function (data) { console.log(data); if (data.state){ // 预定成功 location.href= "" } else { alert( "预定的房间已经被预定" ); location.href= "" } } }) }); // 日历插件 $( '#datetimepicker11' ).datetimepicker({ minView: "month" , language: "zh-CN" , sideBySide: true , format: 'yyyy-mm-dd' , startDate: new Date(), bootcssVer: 3, autoclose: true }).on( 'changeDate' , book_query); function book_query(e) { CHOOSE_DATE=e.date.yuan( "yyyy-MM-dd" ); location.href= "/index/?book_date=" +CHOOSE_DATE; } </script> </html> |
1、点击事件预定和取消——组织数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | <script> // room_id 为键,time_id 为值 {1:[4,5],2:[4,] } {3:[9,10]} var POST_DATA = { "ADD" :{}, "DEL" :{} }; // 为td绑定单击事件 function BindTd() { $( '.item' ).click( function () { var room_id = $( this ).attr( "room_id" ); var time_id = $( this ).attr( "time_id" ); // 取消预定 if ($( this ).hasClass( "active" )){ // 如果点击的标签具有active类,直接删除active类并清空内容 $( this ).removeClass( "active" ).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" ); // 点击删除临时预定时添加的数据 // POST_DATA.ADD[room_id].pop(); // 这个是删除最后一个元素不对 POST_DATA.ADD[room_id].splice(POST_DATA.ADD[room_id].indexOf(time_id),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, ]; } } }) } BindTd(); </script> |
注意:
(1)取消预定事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <script> // 为td绑定单击事件 function BindTd() { $( '.item' ).click( function () { // alert($(this).attr("room_id")); // 点击显示会议室id // 取消预定 if ($( this ).hasClass( "active" )){ // 如果点击的标签具有active类,直接删除active类并清空内容 $( this ).removeClass( "active" ).empty(); } else if ($( this ).hasClass( "td_active" )) { $( this ).removeClass( "td_active" ); } else { // 空白局域点击 $( this ).addClass( "td_active" ); } }) } BindTd(); </script> |
在这次只处理了具有active类和td_active类的情况,但没有处理another_active类的情况,因为这种需要判断的情况,一定要交给后端,否则就是前端一套后端一套,点击保存按钮发送的js,客户可以伪装一个data发送给服务器,如果不做联合唯一,完全交给前端会造成很严重的安全问题。
(2)数据组织和添加预定
创建如下所示用js字面量方式创建对象
POST_DATA,有两个属性(对象)ADD和DEL,这两个对象的值以room_id为键,以time_id为值:
1 2 3 4 5 6 7 | <script> // room_id 为键,time_id 为值 {1:[4,5],2:[4,] } {3:[9,10]} var POST_DATA = { "ADD" :{}, "DEL" :{} }; </script> |
在空白单元格点击,获取添加数据到POST_DATA中,以完成预定工作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | // 为td绑定单击事件 function BindTd() { $( '.item' ).click( function () { var room_id = $( this ).attr( "room_id" ); var time_id = $( this ).attr( "time_id" ); // 取消预定 if ($( this ).hasClass( "active" )){ // 如果点击的标签具有active类,直接删除active类并清空内容 $( this ).removeClass( "active" ).empty(); } else if ($( this ).hasClass( "td_active" )) { $( this ).removeClass( "td_active" ); } 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, ] } } }) } |
点击两个按钮后,在页面控制台打印POST_DATA显示如下:
(3)临时预定取消数据处理
1 2 3 4 5 6 7 | // 取消临时预定 else if ($( this ).hasClass( "td_active" )) { $( this ).removeClass( "td_active" ); // 点击删除临时预定时添加的数据 // POST_DATA.ADD[room_id].pop(); // 这个是删除最后一个元素不对 POST_DATA.ADD[room_id].splice(POST_DATA.ADD[room_id].indexOf(time_id),1) } |
利用splice方法在数组中从指定位置开始删除,且指定仅删除一项。
(4)js数组操作常用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | // shift:删除原数组第一项,并返回删除元素的值;如果数组为空则返回undefined var a = [1,2,3,4,5]; var b = a.shift(); //a:[2,3,4,5] b:1 // pop:删除原数组最后一项,并返回删除元素的值;如果数组为空则返回undefined var a = [1,2,3,4,5]; var b = a.pop(); //a:[1,2,3,4] b:5 // push:将参数添加到原数组末尾,并返回数组的长度 var a = [1,2,3,4,5]; var b = a.push(6,7); //a:[1,2,3,4,5,6,7] b:7 // concat:返回一个新数组,是将参数添加到原数组中构成的 var a = [1,2,3,4,5]; var b = a.concat(6,7); //a:[1,2,3,4,5] b:[1,2,3,4,5,6,7] // splice(start,deleteCount,val1,val2,...):从start位置开始删除deleteCount项,并从该位置起插入val1,val2,... var a = [1,2,3,4,5]; var b = a.splice(2,2,7,8,9); //a:[1,2,7,8,9,5] b:[3,4] var b = a.splice(0,1); //同shift a.splice(0,0,-2,-1); var b = a.length; //同unshift var b = a.splice(a.length-1,1); //同pop a.splice(a.length,0,6,7); var b = a.length; //同push // reverse:将数组反序 // sort(orderfunction):按指定的参数对数组进行排序 // slice(start,end):返回从原数组中指定开始下标到结束下标之间的项组成的新数组 var a = [1,2,3,4,5]; var b = a.slice(2,5); //a:[1,2,3,4,5] b:[3,4,5] // join(separator):将数组的元素组起一个字符串,以separator为分隔符,省略的话则用默认用逗号为分隔符 var a = [1,2,3,4,5]; var b = a.join( "|" ); //a:[1,2,3,4,5] b:"1|2|3|4|5" |
2、发送AJAX
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // 发送ajax $( ".keep" ).click( function () { $.ajax({ url: "/book/" , type: "POST" , data:{ csrfmiddlewaretoken: '{{ csrf_token }}' , choose_date:CHOOSE_DATE, post_data:JSON.stringify(POST_DATA) }, dataType: "json" , success: function (data) { console.log(data); if (data.state){ // 预定成功 location.href= "" } else { alert( "预定的房间已经被预定" ); location.href= "" } } }) }); |
网络编程本质是浏览器和服务器之间发送字符串。
1 2 3 4 5 6 7 8 9 10 | POST请求 浏览器——————》server "请求首行\r\nContent-Type:url_encode\r\n\r\na=1&b=2" "请求首行\r\nContent-Typr: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 |
注意这里是选择在data中添加csrfmiddlewaretoken: '{{ csrf_token }}',来解决forbiden报错。
3、使用日历插件
这一块没有视频需要研究一下。
六、视图处理图书预定和取消
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | import datetime import json def book(request): print (request.POST) post_data = json.loads(request.POST.get( "post_data" )) # {"ADD":{"1":["5"],"2":["5","6"]},"DEL":{"3":["9","10"]}} choose_date = request.POST.get( "choose_date" ) res = { "state" : True , "msg" : None } try : # 添加预定 # post_data["ADD"] : {"1":["5"],"2":["5","6"]} book_list = [] for room_id, time_id_list in post_data[ "ADD" ].items(): for time_id in time_id_list: book_obj = Book(user = request.user, room_id = room_id, time_id = time_id, date = choose_date) book_list.append(book_obj) Book.objects.bulk_create(book_list) # 删除预定 from django.db.models import Q # post_data["DEL"]: {"2":["2","3"]} 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: Book.objects. filter (remove_book).delete() except Exception as e: res[ "state" ] = False res[ "msg" ] = str (e) return HttpResponse(json.dumps(res)) |
1、json.loads()
2、批量插入预订数据
3、Q查询
4、删除预订数据
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库