会议室预订系统(meeting room booking system)

会议室预订系统(meeting room booking system)

 

正文

一、目标及业务流程

期望效果:

业务流程:

  1. 用户登录
  2. 预定会议室
  3. 退订会议室
  4. 选择日期;今日以及以后日期

二、表结构设计和生成

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:

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())
index视图函数
<!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>
index.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、点击事件预定和取消——组织数据

?
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、使用日历插件

 // 日历插件
    $('#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;
     }

六、视图处理图书预定和取消

1、json.loads()

 

2、批量插入预订数据、删除预订数据

 

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

 

转载于:https://www.cnblogs.com/FWF1944/p/10876496.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值