会议室表结构设计和实现

########################################

from django.db import models

# Create your models here.
class MeetingRoom(models.Model):
    '''会议室'''
    name = models.CharField(max_length=32,verbose_name="会议室名称")
    class Meta:
        verbose_name_plural = "会议室"

    def __str__(self):
        return self.name

class ReserveRecord(models.Model):
    '''预定记录表'''
    data = models.DateField(verbose_name="预定日期")
    user = models.ForeignKey(to="UserInfo",verbose_name="预订人")
    room = models.ForeignKey(to="MeetingRoom",verbose_name="预定房间")
    time1 = (
        (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"),
    )
    timeline = models.IntegerField(choices=time1,verbose_name="预定时间")
    class Meta:
        verbose_name_plural = "预订记录表"
        unique_together = (
            ('data', 'timeline', 'room')
        )
    def __str__(self):
        return self.user.username

class UserInfo(models.Model):
    '''用户信息'''
    username = models.CharField(max_length=32,verbose_name="用户名",unique=True)
    password = models.CharField(max_length=64,verbose_name="密码")
    class Meta:
        verbose_name_plural = "用户信息"

    def __str__(self):
        return self.username

###############################################

表结构分析
1,会议室表,会议室的名称
2,会议室的预定表,预定的会议室,谁预定的,预定的时间,

###############################################

"""会议室预定 URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.11/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/$', views.index),
    url(r'^recording/$', views.recording),
    url(r'^login/$', views.login),
]

###############################################

from django.forms import Form
from django.forms import fields
from django.forms import widgets
from django.http.response import JsonResponse
from django.shortcuts import render,redirect
from app01 import models
# Create your views here.
import datetime
import json
# def index(request):
#     #查看所有会议室的名称
#     metting_list = models.ReserveRecord.time1
#     print("4444444",metting_list)
#     room_obj = models.MeetingRoom.objects.all()
#
#     return render(request,"index.html",{"metting_list":metting_list,"room_obj":room_obj})

class LoginForm(Form):
    username = fields.CharField(
        max_length=8,
        required=True,
        error_messages={
            "max_length":"用户名长度不能大于8位",
            "required":"用户名不能为空",
        },
        # label="用户名",
        # label_suffix=":",
        widget=widgets.TextInput(attrs={"class":"form-control","placeholder":"username","id":"username"})
    )
    password = fields.CharField(
        max_length=8,
        min_length=3,
        required=True,
        error_messages={
            "max_length":"密码长度不能大于8位",
            "min_length":"密码长度不能小于3位",
            "required":"密码不能为空",
        },
        # label="密码",
        # label_suffix=":",
        widget=widgets.PasswordInput(attrs={"class":"form-control","placeholder":"password","id":"password"}))
def login(request):
    if request.method =="GET":
        form = LoginForm()
        return render(request,"login.html",{"form":form})
    else:
        form = LoginForm(data=request.POST)
        if form.is_valid():
            user = models.UserInfo.objects.filter(**form.cleaned_data).first()
            if user:
                request.session["userinfo"]={"id":user.id,"name":user.username}
                return redirect("/index/")
                # cookie的方式设置
                # obj.set_cookie("name","zzz")
                # obj.set_signed_cookie("id",user.id,salt="aaaa")
                # obj.set_signed_cookie("id1",user.id)
                # return obj
        else:
            form.add_error('password','密码错误')
            return render(request,"login.html",{"form":form})
    return render(request, "login.html", {"form": form})


def index(request):
    metting_list = models.ReserveRecord.time1
    return render(request,"index.html",{"metting_list":metting_list})

def recording(request):
    response = {"status": True, "msg": None, "data": None}
    if request.method == "GET":
        current_data = datetime.datetime.now().date()  #日期类型
        #=======================获取指定日期所有的预定信息=========================
        try:
            ajax_date= request.GET.get("date")  #字符串类型
            # print(ajax_date,"============")
            ajax_date = datetime.datetime.strptime(ajax_date, '%Y-%m-%d').date()
            # print("date....",ajax_date)
            if ajax_date < current_data:
                raise Exception("查询时间不能是以前的时间")
            recording_list = models.ReserveRecord.objects.filter(data=ajax_date)  #查询的这一天的所有的记录
            # print(recording_list,"recording_list")  # [OBJ(1,room_id,user_id.time_id.data),OBJ(2,room_id,user_id.time_id.data)]
            # 吧这样的数据处理成字典的形式,提升查询速度
            # {
            #     1:{   #room_id
            #         2:{"username":2,"user_id":3}  #2表示time_id
            #     }
            # }
            recrding_dict = {}
            for i in recording_list:
               if i.room_id not in recrding_dict:
                    recrding_dict[i.room_id]={i.timeline:{"username":i.user.username,"user_id":i.user_id}}
               else:
                   # recrding_dict[i.room_id][i.timeline] = {i.timeline:{"username":i.user.username,"user_id":i.user_id}}
                   recrding_dict[i.room_id][i.timeline] = {"username":i.user.username,"user_id":i.user_id}
            print('--------',recrding_dict)
            #获取所有的会议室信息
            room_list = models.MeetingRoom.objects.all()
            #===========================生成会议室信息========================
            data = []
            for room in room_list:
                print(room)
                tr = []
                tr.append({"text": room.name, "attrs": {}})
                for tm in models.ReserveRecord.time1:
                    td = {"text": "", "attrs": {"room_id": room.id, "time_id": tm[0]}}
                    if room.id in recrding_dict and tm[0] in recrding_dict[room.id]:
                        #已预订,不确定是谁预定的,还得判断一下
                        td['attrs']['class'] = "chosen"
                        if recrding_dict[room.id][tm[0]]['user_id'] == request.session["userinfo"]["id"]:
                            #如果是自己预定
                            td['text'] = ''
                        else:
                            #如果是别人预定,加一个disabled属性不可编辑,只有自己的能编辑
                            td['text'] = recrding_dict[room.id][tm[0]]['username']
                            td['attrs']['disable'] = True
                    tr.append(td)
                data.append(tr)
            print('-==========',data)
            response["data"] = data
        except Exception as e:
            response["status"] = True
            response["msg"] = str(e)
    else:
        try:
            current_date = datetime.datetime.now().date()
            ajax_date = request.POST.get("date")
            ajax_date = datetime.datetime.strptime(ajax_date, '%Y-%m-%d').date()
            if ajax_date < current_date:
                 raise Exception("查询时间不是当前的时间")
            post_data = json.loads(request.POST.get("data"))  #由于发过来的数据是字符串,所以要转一下才能是字典
            print(post_data)  #{'ADD': {'1': ['5', '4'], '2': ['4', '7']}, 'DEL': {'2': ['4']}}}
            date = request.POST.get("date")   #获取日期
            # print(date)  #2017-12-12
            # 拿到数据以后
            # 如果time_id在ADD里面有,在Del里面也有值,就删除了,因为数据库里面已经有值了。就直接把add里的和del里的删除就行了
            for room_id ,time_list in post_data["DEL"].items():
                if room_id  not in post_data["ADD"]:
                    continue
                else:
                    for time_id in list(time_list):
                        if time_id in post_data["ADD"][room_id]:
                            post_data["ADD"][room_id].remove(time_id)
                            post_data["DEL"][room_id].remove(time_id)
                # print(post_data)
            #新增数据
            reserverecord_obj_list = []
            for room_id ,time_list in post_data["ADD"].items():
                for time_id in time_list:
                    # models.ReserveRecord.objects.create(room_id=room_id,time_id=time_id,date=date,user=request.session["userinfo"]["id"])
                    obj = models.ReserveRecord(room_id=room_id,timeline=time_id,data=date,user_id=request.session["userinfo"]["id"])
                    reserverecord_obj_list.append(obj)
            models.ReserveRecord.objects.bulk_create(reserverecord_obj_list)

            #删除会议室预定信息
            from django.db.models import Q
            remove_reserverecord = Q()
            for room_id,time_list in post_data["DEL"].items():
                for time_id in time_list:
                    temp = Q()
                    temp.connector = "AND"
                    temp.children.append(("user_id",request.session["userinfo"]["id"]))
                    temp.children.append(("data",date))
                    temp.children.append(("room_id",room_id))
                    temp.children.append(("timeline",time_id))

                    remove_reserverecord.add(temp,"OR")
            if remove_reserverecord:
                print(models.ReserveRecord.objects.filter(remove_reserverecord))
                models.ReserveRecord.objects.filter(remove_reserverecord).delete()
            # print(remove_reserverecord,"remove_reserverecord")
        except Exception as e:
            response["status"] = False
            response["msg"] = str(e)
    return JsonResponse(response)

###############################################

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width">
    <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
    <script src="/static/jquery-3.2.1.min.js"></script>
    <script src="/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
    <title>Title</title>
    <style>
        .container {
            margin-top: 100px;
        }
    </style>
</head>
<body>
<form action="/login/" method="post" novalidate>
    {% csrf_token %}
    <div class="container">
        <h3 style="text-align: center">请先登录</h3>
        <div class="row">
            <div class="col-md-4 col-md-offset-4">
                <div class="form-group">
                    <label for="username">用户名</label>
                    {{ form.username }}
                    <p style="color: red">{{ form.errors.username.0 }}</p>
                </div>
                <div class="form-group">
                    <label for="password">密码</label>
                    {{ form.password }}
                    <p style="color: red">{{ form.errors.password.0 }}</p>
                </div>
                <input type="submit" value="登录" class="btn btn-success col-sm-7 col-md-offset-2">
            </div>
        </div>

    </div>

</form>
</body>
</html>

login.html

###############################################

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/css/index.css">
    <link rel="stylesheet" href="/static/bootstrap-datetimepicker-master/css/bootstrap-datetimepicker.css">
    <script src="/static/jquery-3.2.1.min.js"></script>
    <script src="/static/bootstrap-datetimepicker-master/js/bootstrap-datetimepicker.js"></script>
    <script src="/static/bootstrap-datetimepicker-master/js/locales/bootstrap-datetimepicker.fr.js"></script>
    <script src="/static/bootstrap-datetimepicker-master/js/locales/bootstrap-datetimepicker.zh-CN.js"></script>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-11">
            <h1>会议室预定</h1>
            <div class="data pull-right">
                <button class="btn btn-primary" id="save">保存</button>
            </div>
            {#            日期#}
            <div class='col-sm-4 pull-right'>
                <div class="form-group">
                    <div class='input-group date' id='datetimepicker2'  placeholder="请选择日期">
                        <input type='text' class="form-control"/>
                        <span class="input-group-addon">
                    <span class="glyphicon glyphicon-calendar"></span>
                </span>
                    </div>
                </div>
            </div>
{#            表格#}
            <div>
                <table class="table table-bordered">
                    <thead>
                    <th>会议室</th>
                      {% for metting in metting_list %}
                      <th>{{ metting.1 }}</th>
                      {% endfor %}
                    </thead>
                    <tbody id = "tBody">
{#                        方式一:也可以这样渲染#}
{#                        {% for room in room_obj %}#}
{#                        <tr>#}
{#                            <td>{{ room.name }}</td>#}
{#                            {% for metting in metting_list %}#}
{#                            <td></td>#}
{#                            {% endfor %}#}
{#                        </tr>#}
{#                        {% endfor %}#}
{#                            方式二#}
{#                        发送ajax请求渲染#}
                    
                    </tbody>
                </table>
            </div>
{#            加载框#}
            <div class="shade hide"></div>
            <div class="loading hide"></div>
        </div>
    </div>
</div>
<script>
    //对Date的扩展
    // 对Date的扩展,将 Date 转化为指定格式的String
    // 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符,
    // 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
    // 例子:
    // (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423
    // (new Date()).Format("yyyy-M-d h:m:s.S")      ==> 2006-7-2 8:9:4.18
    Date.prototype.Format = 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;
    };

    $(function(){    //一开始加载的时候执行这个函数
        initDatepickle();
        initRecoringInfo(new Date().Format("yyyy-MM-dd"));
        initTdEvent();
        initSaveEvent();
    });

    POST_DATA = {
        "ADD":{},
        "DEL":{}
    };
    //时间控件初始化
    function initDatepickle() {
       $('#datetimepicker2').datetimepicker({
        minView: "month",//设置只显示到月份
        format: 'yyyy-mm-dd',//显示格式
{#        autoclose: true,//选完自动关闭#}
        todayBtn: true,
        language:"zh-CN",
         startDate: new Date(),  //以前的日期不能点
        bootcssVer:3  //小箭头
        }).on('changeDate', changeDate);
    }
    //点击日期插件的时候改变的函数
    function changeDate(ev) {
{#        console.log(ev.date);  //Wed Dec 13 2017 20:43:08 GMT+0800 (中国标准时间)#}
        CHOISE_DATE = ev.date.Format("yyyy-MM-dd");  //拿到的是插件的日期
        initRecoringInfo(CHOISE_DATE);

    }

    CHOISE_DATE = new Date().Format("yyyy-MM-dd");  //当change的时候会修改日期,它拿到的是当前的日期
   //获取预定记录发送ajax请求
    function initRecoringInfo(date) {   //这里穿进来的date就是上面转换成字符串的时间
{#        刚开始发送ajax的时候加载#}
        $(".shade,.loading").removeClass("hide");
        $(function () {
            $.ajax({
                url: "/recording/",
                type: "get",
                data: {"date": date},
                success: function (data) {
                    $(".shade,.loading").addClass("hide");
                    if (data.status) {
                        $("#tBody").empty();
                        $.each(data.data, function (i, item) {
{#                                                console.log(i,item);#}
                            var $tr = $("<tr>");
                            $.each(item, function (j, k) {
                                {#                        console.log(j,k);#}
                                var $td = $("<td>");
                                $td.text(k.text);
                                $.each(k.attrs, function (m, n) {
                                    console.log(m, n);
                                    $td.attr(m, n)
                                });
                                $tr.append($td);
                                {#                        if (k.chosen){#}
                                {#                            $("class").addClass("chosen")#}
                                {#                        }#}
                            });
                            $("#tBody").append($tr);
                        });

                        //吧del,add里面有的内容清空
                        CHOSEN_DATE = new Date().Format('yyyy-MM-dd');
                        POST_DATA = {
                            DEL:{},
                            ADD:{}
                        };
                    }
                    else {
                        alert(data.msg)
                    }
                },
                error:function () {
                     $(".shade,.loading").removeClass("hide");
                     alert("异常错误")
                }
            })
        })
    }

    //给td绑定事件,处理数据
    function initTdEvent(){
        //事件委派
        $("tBody").on("click","td[time_id][disable!='true']",function () {
            //添加一个样式
            var room_id = $(this).attr("room_id");
            var time_id = $(this).attr("time_id");
            if ($(this).hasClass("chosen")){
                $(this).removeClass("chosen");
                $(this).text("");
                //退订room_id = 1 ,time_id = 5的
                if (POST_DATA.DEL[room_id]) {
                    //如果有room_id,就添加一个time_id
                    POST_DATA.DEL[room_id].push(time_id)
                }else {
                    POST_DATA.DEL[room_id] = [time_id]
                }
            }
            else if ($(this).hasClass("temp")){
                //取消预定
                $(this).removeClass("temp");
                //从add中吧数据删除(先找到索引,然后如果存在就删除)
                var index = POST_DATA.ADD[room_id].indexOf(time_id);
                if (index!==-1) {
                    POST_DATA.ADD[room_id].splice(index,1)  //索引为n的删除一个
                }
            }else {
                //要预定,吧预定的结果添加进去
                $(this).addClass("temp");
                 if (POST_DATA.ADD[room_id]) {
                    //如果有room_id,就添加一个time_id
                    POST_DATA.ADD[room_id].push(time_id)
                }else {
                    POST_DATA.ADD[room_id] = [time_id]
                }
            }
        })
    }

    //通过ajax想后台发数据
    function initSaveEvent() {
        $("#save").click(function () {
            $.ajax({
                url:"/recording/",
                type:"post",
                data:{
                       data:JSON.stringify(POST_DATA),  //要发送的用户传过来的时间
                       date:CHOISE_DATE,  //发送的日期时间
                       csrfmiddlewaretoken:'{{ csrf_token }}'
                    },

                success:function (data) {
                    console.log(data);
                    if (data.status){
                        initRecoringInfo(CHOISE_DATE)
                    }
                    else {
                        alert(data.msg)
                    }
                }
            })
        })
    }
</script>
</body>
</html>

index.html

###############################################

table{
    margin-top: 80px;
}
td{
    height: 80px;
}
.chosen{
    background-color: gold;
}
.shade {
            position: fixed;
            z-index: 1040;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: #999;
            filter: alpha(opacity=50);
            -moz-opacity: 0.5;
            opacity: 0.5;
        }

.loading {
            position: fixed;
            z-index: 1050;
            top: 40%;
            left: 50%;
            height: 32px;
             32px;
            margin: 0 0 0 -16px;
            background: url(/static/img/loading.gif);
}
.temp{
    background-color: salmon;
}

index.css

###############################################

原文地址:https://www.cnblogs.com/andy0816/p/12511233.html