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


    一、目标及业务流程

    期望效果:

    业务流程:

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

    二、表结构设计和生成

    1、models.py(用户继承AbstractUser)

    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

    from django.contrib.auth.models import AbstractUser
    
    class UserInfo(AbstractUser):
        """用户信息表"""
        tel = models.CharField(max_length=32)
    

      如上所示,即可在Django的基础上添加我们所需要的信息。

    (2)设置model的时候,设置三个字段联合唯一

    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模型,其值引用一个自定义的模型。

    AUTH_USER_MODEL = "app01.UserInfo"
    

      上面的值表示Django应用的名称(必须位于INSTALLLED_APPS中)和你想使用的User模型的名称。

    注意:在创建任何迁移或者第一次运行 manager.py migrate 前设置 AUTH_USER_MODEL

      设置AUTH_USER_MODEL数据库结构有很大的影响。改变了一些会使用到的表格,并且会影响到一些外键和多对多关系的构造。在你有表格被创建后更改此设置是不被 makemigrations 支持的,并且会导致你需要手动修改数据库结构,从旧用户表中导出数据,可能重新应用一些迁移。

    3、数据迁移及创建超级用户

    $ python3 manage.py makemigrations
    $ python3 manage.py migrate
    

      这里遇到了一个问题:创建项目时没有创建应用,手动通过manage.py startapp user创建子项目,修改AUTH_USER_MODEL后执行makemigrations一直报错,找不到app01。

      创建两个超级用户:

    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:

    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:

    <!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视图函数:

    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:

    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)数据处理还是在后台更加方便,前台渲染后台传递来的标签字符串

    <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格式化函数

    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 函数可以接受不限个参数,位置可以不按顺序。

    >>>"{} {}".format("hello", "world")    # 不设置指定位置,按默认顺序
    'hello world'
     
    >>> "{0} {1}".format("hello", "world")  # 设置指定位置
    'hello world'
     
    >>> "{1} {0} {1}".format("hello", "world")  # 设置指定位置
    'world hello world'
    

      还可以设置参数:

    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)循环会议室生成行,循环时段生成列,标签字符串拼接处理

    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)  # 2018-08-17
        # 取预约日期,没有指定取当前日期
        book_date = request.GET.get("book_date", date)
        print(book_date)  # 2018-08-17
    

      index页面访问中,如果没有指定日期,默认显示的就是当前日的预定信息。

      因此在循环生成表格时,可以循环确定单元格是否被预定,已经被预定的添加class=‘active’属性。

    # 构建标签
    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)在预定单元格添加预定人姓名,并根据登录人判断显示单元格

    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中添加样式:

    <style>
        .active {
            background-color: green!important;
            color: white;
        }
        .another_active {
            background-color: #0f5687;
            color: white;
        }
    </style>
    

    显示效果如下:

      

    五、前端部分数据处理(index.html)

    <!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=" 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、点击事件预定和取消——组织数据

    <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)取消预定事件

    <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为值:

    <script>
        // room_id 为键,time_id 为值  {1:[4,5],2:[4,] }   {3:[9,10]}
        var POST_DATA = {
            "ADD":{},
            "DEL":{}
        };
    </script>
    

      在空白单元格点击,获取添加数据到POST_DATA中,以完成预定工作:

    // 为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)临时预定取消数据处理

    // 取消临时预定
    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数组操作常用方法

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

    // 发送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=""
                }
            }
        })
    });
    

      网络编程本质是浏览器和服务器之间发送字符串。

    POST请求
    浏览器——————》server
        "请求首行
    Content-Type:url_encode
    
    a=1&b=2"
        "请求首行
    Content-Typr:application/json
    
    ("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、使用日历插件

      这一块没有视频需要研究一下。

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

    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、删除预订数据

  • 相关阅读:
    在vue项目中如何关闭Eslint校验
    npm安装依赖过程中报错:Error: Can't find Python executable "python", you can set the PYTHON env variable
    如何在SAE搭建属于自己的黑盒xss安全测试平台
    掌握下面常用函数,学php不再难
    解决ThinkPHP中开启调试模式无法加载模块的问题。
    生成规则:Q+年后两位+月+日卡券类型+八位字母和数字随机
    SPI的全名为Service Provider Interface
    03、第三个阶段,Java 核心技术
    02、第二个阶段,Java 基础入门
    01、第一个阶段,环境和工具准备
  • 原文地址:https://www.cnblogs.com/xiugeng/p/9490830.html
Copyright © 2020-2023  润新知