先梳理一下我们的会议室预定是怎么做的
1、使用django自带的auth模块做用户认证
2、使用form表单生成登录的form表单
3、使用bookstrap做登录的css的渲染
4、设计会议室预定的model,用户表,我们不自己设计,使用django的user表,在设计一个会议室的表,最后在设计一个预定的表
5、会议室预定的前端的页面我们用table标签,通过bootstrap进行css渲染
6、通过ajax将预定的信息发送给后端,后端写入数据库后,前端重新加载页面,实现预定的效果
-----------------------------------------------------------------------------------------------------
一、登录相关的知识点梳理
1、首先先看下如何利用django自带的user表,和如何通过继承的方式扩展user表,因为user表中的字段是固定的,我们如果想扩展的话,就可以通过继承的方式,或者建立一张一对一的表两种方式来实现扩展user表
a、通过继承的方式扩展user表的字段
from django.contrib.auth.models import AbstractUser class Userinfo(AbstractUser): iphone = models.CharField(max_length=11, null=True, blank=True) email = models.CharField(max_length=64, null=True, blank=True) create_time = models.DateTimeField(auto_now_add=True, blank=True) def __str__(self): return self.username
b、在settings中指明,django自带的auth模块要去我们新的userinfo表中去认证
AUTH_USER_MODEL = "app1.Userinfo"
c、还需要用到一个装饰器,就是校验用户是否登录的方法
这就是一个装饰器,可以用在任何需要登录的试图函数上
from django.contrib.auth.decorators import login_required
@login_required def index(request):
这里还 要定义,如果用户没有登录,我要跳转到什么页面,这个也要在settings中设置,下面的意思就是如果没有登录,则跳转到登录页面
LOGIN_URL = "/app1/login/"
d、这里还有一个点要掌握,就是注销,auth模块自带的logout方法会删除session和cookies
def logout(request): auth.logout(request) return redirect("/app1/login/")
2、然后我们通过forms生成登录页面的form表单
a、首先导入用到的模块
from django import forms from django.core.validators import RegexValidator from django.forms import widgets from django.contrib import auth from django.contrib.auth.decorators import login_required
b、然后写forms的代码
class Loginclass(forms.Form): user_name = forms.CharField( label="用户名", label_suffix=":", max_length=12, min_length=5, validators = [ RegexValidator(r'^\D{2}\d+\D+',code="checkfirst"), ], widget=widgets.Input(attrs={"placeholder":"请输入用户名","class":"form-control"}), error_messages= { "required":"用户名不能为空", "max_length":"用户名的最大长度为12", "min_length":"用户名的最小长度为5", "checkfirst":"用户名必须以2个非数字开头,中间为数字,以非数字结尾" } ) user_pwd = forms.CharField( label="密码", label_suffix=":", max_length=12, min_length=5, widget=widgets.PasswordInput(attrs={"placeholder": "请输入密码","class":"form-control"},render_value=True), # validators = [ # RegexValidator(r'^\d+',code="checkfirst",message="密码必须全部为数字"), # ], error_messages= { "required":"密码不能为空", "max_length":"密码的最大长度为12", "min_length":"密码的最小长度为5" } )
c、然后就是登录的视图函数,这里我们会做两层的校验,首先forms表单会做一层校验,保证数据是符合条件的,第二层校验就是auth模块会去userinfo表中去校验用户名和密码是否正确
def login(request): method = request.method.lower() if method == "get": obj = Loginclass() return render(request,"book_login.html",{"obj":obj}) else: obj = Loginclass(request.POST) if obj.is_valid(): user = auth.authenticate(username=obj.cleaned_data["user_name"],password=obj.cleaned_data["user_pwd"]) if user: auth.login(request,user) return redirect("/app1/index/") else: return redirect("/app1/login/") else: return render(request,"book_login.html",{"obj":obj})
这里有一段代码非常重要,下面这段代码就会在request添加一个属性就是request.user,这个的值就是当前登录的用户的对象
auth.login(request,user)
3、然后在看下登录页面的html的代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/css/bootstrap.min.css"> </head> <body> <h3>登陆页面</h3> <form method="post" action="/app1/login/" novalidate> {% csrf_token %} <div class="form-group {% if obj.user_name.errors.0 %} has-error {% endif %}"> <p>{{ obj.user_name.label }}{{ obj.user_name }}</p> <span class="help-block">{{ obj.user_name.errors.0 }}</span> </div> <div class="form-group {% if obj.user_pwd.errors.0 %} has-error {% endif %}"> <p>{{ obj.user_pwd.label }}{{ obj.user_pwd }}</p> <span class="help-block">{{ obj.user_pwd.errors.0 }}</span> </div> <div class="form-group"> <p><input type="submit" value="登陆" class="btn btn-success"></p> </div> </form> <script src="/static/jq/jquery-3.3.1.js"></script> <script src="/static/jq/bootstrap.min.js"></script> <script> </script> </body> </html>
这里复习一下这个点,就是报错的的效果,如果某个字段有错误信息,则会有一个圈红的效果
<div class="form-group {% if obj.user_name.errors.0 %} has-error {% endif %}"> <p>{{ obj.user_name.label }}{{ obj.user_name }}</p> <span class="help-block">{{ obj.user_name.errors.0 }}</span> </div>
-----------------------------------------------------------------------------------------------------
二、这里我们主要看下表结构的设计
a、会议室表
class room(models.Model): rid = models.AutoField(primary_key=True) title = models.CharField(max_length=64,verbose_name="会议室名称") num = models.SmallIntegerField() def __str__(self): return self.title class Meta: verbose_name = "会议室表" verbose_name_plural = verbose_name
b、预定表
class book(models.Model): bid = models.AutoField(primary_key=True) user_obj = models.ForeignKey(to=Userinfo,on_delete=models.CASCADE) room_obj = models.ForeignKey(to=room,on_delete=models.CASCADE) choices = ( ("1","01:00"), ("2","02:00"), ("3","03:00"), ("4","04:00"), ("5","05:00"), ("6","06:00"), ("7","07:00"), ("8","08:00"), ) time_id = models.CharField(choices=choices,max_length=64) book_time = models.DateTimeField(auto_now_add=True) # def __str__(self): # return self.user_obj + "预定了" + self.room_obj class Meta: unique_together = ["user_obj","room_obj","time_id"] verbose_name = "预定表" verbose_name_plural = verbose_name
c、因为我们要通过admin去增加数据,所有我们需要在admin中注册我们的表结构,这一步不是必须的
from django.contrib import admin # Register your models here. from app1 import models class rootclass(admin.ModelAdmin): list_display = ["rid","title","num"] class bookclass(admin.ModelAdmin): list_display = ["bid","user_obj","room_obj","time_id","book_time"] admin.site.register(models.room,rootclass) admin.site.register(models.book,bookclass)
-----------------------------------------------------------------------------------------------------
三、在看下预定会议室的功能的实现
a、首先看下视图函数的get方式是如何处理的
@login_required def index(request): import datetime current_time = datetime.datetime.now().date() # request.GET["book_time"] = "2018-12-02" check_time = request.GET.get("book_time",current_time) print(check_time) time_choice = models.book.choices room_list = models.room.objects.all() current_book_list = [] book_list = models.book.objects.all() for i in book_list: if str(i.book_time.strftime("%Y-%m-%d")) == "2018-12-04": temp = (i.user_obj,i.room_obj.rid,i.time_id) current_book_list.append(temp) htmls = "" print(current_book_list) for room in room_list: temp_r = """<tr><td>{meetname}:(容量:{num}人)</td>""".format(meetname = room.title,num = room.num) for t in time_choice: if current_book_list: for book_room in current_book_list: # print(str(book_room[1]),str(room.rid),str(book_room[2]),str(t[0]),sep="==================") if str(book_room[1]) == str(room.rid) and str(book_room[2]) == str(t[0]): current_login_user = request.user.username print(current_login_user,book_room[0],sep="-------------") if str(current_login_user) == str(book_room[0]): temp_t = "<td room_id={rid} time_id={tid} class='checked item'>{name}</td>".format(rid=room.rid,tid=t[0],name=book_room[0]) else: temp_t = "<td room_id={rid} time_id={tid} class='active_now item'>{name}</td>".format(rid=room.rid, tid=t[0], name=book_room[0]) break else: temp_t = "<td room_id={rid} time_id={tid} class='item'></td>".format(rid=room.rid, tid=t[0]) # temp_r = temp_r + temp_t else: temp_t = "<td room_id={rid} time_id={tid} class='item'></td>".format(rid=room.rid, tid=t[0]) temp_r = temp_r + temp_t temp_r = temp_r + "</tr>" htmls = htmls + temp_r # print(htmls) return render(request,"index.html",locals())
这里主要是针对不同的数据返回不同的html代码,主要是css样式的不同的
首先从数据库中拿到所有的预定的信息,然后对不同的数据渲染不同的css样式
不同的数据有,“未被选中的会议室”,“当前用户选中的会议室”,“非当前用户选中的会议室”
b、前端的代码的
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/datetimepicker/bootstrap-datetimepicker.min.css"> <style> .checked{ background-color: yellow; } .active_now{ background-color: red; } td{ text-align: center; } th{ text-align: center; } .td_active{ background-color: #2e6da4; } </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 times in time_choice %} <th>{{ times.1 }}</th> {% endfor %} </tr> </thead> <tbody> {{ htmls |safe }} </tbody> </table> <input type="button" class="btn btn-success pull-right keep" value="提交"> <a href="/app1/logout"><input type="button" class="btn btn-info pull-left keep" value="注销"></a> <script src="/static/jq/jquery-3.3.1.js"></script> <script src="/static/jq/bootstrap.min.js"></script> <script src="/static/datetimepicker/bootstrap-datetimepicker.min.js"></script> <script src="/static/datetimepicker/bootstrap-datetimepicker.zh-CN.js"></script> <script> var POST_DATA = { 'ADD':{}, "DEL":{} } function bindtd() { $(".item").bind("click",function () { var room_id = $(this).attr("room_id"); var time_id = $(this).attr("time_id"); if ($(this).hasClass("checked")){ $(this).removeClass("checked"); $(this).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"); var del_index = POST_DATA.ADD[room_id].indexOf(time_id); POST_DATA.ADD[room_id].splice(del_index,1) } else if ($(this).hasClass("active_now")){ {# pass#} } else { $(this).addClass("td_active"); if (POST_DATA.ADD[room_id]){ POST_DATA.ADD[room_id].push(time_id); {# alert(time_id)#} } else { POST_DATA.ADD[room_id] = [time_id,] } } }) } bindtd(); {# 提交ajax数据#} $(".keep").bind("click",function () { $.ajax( { url:"/app1/book/", type:"POST", data:JSON.stringify(POST_DATA), contentType:"application/json", {# 编码格式1: application/json 这个是编码格式,告诉后端,我这次发的是json格式的数据,数据在请求体里的格式为'{"a":1,"b":2}',在视图函数中进行反序列化处理#} {# 编码格式2:这个是默认的格式, 一般我们传递一个字典的格式过去,其实是用urldecode格式的数据发送给后端,在请求体里传递的数据类型为a=1&b=2&c=3#} success:function (data) { document.location.reload() } } ) }) </script> </body> </html>
这里需要注意的是这里
function bindtd() { $(".item").bind("click",function () { var room_id = $(this).attr("room_id"); var time_id = $(this).attr("time_id"); if ($(this).hasClass("checked")){ $(this).removeClass("checked"); $(this).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"); var del_index = POST_DATA.ADD[room_id].indexOf(time_id); POST_DATA.ADD[room_id].splice(del_index,1) } else if ($(this).hasClass("active_now")){ {# pass#} } else { $(this).addClass("td_active"); if (POST_DATA.ADD[room_id]){ POST_DATA.ADD[room_id].push(time_id); {# alert(time_id)#} } else { POST_DATA.ADD[room_id] = [time_id,] } } }) } bindtd();
分别对空的表格,刚才点击过的预定的表格【但是还未更新到数据库】,当前用户预定的表格,其他用户预定的表格做分别的处理,添加不同的样式,同时更新POST_DAT
这里还有一个知识点
就是增加js的数组中的数据
POST_DATA.DEL[room_id].push(time_id)
js删除数组中的元素的方式,首先要获取要删除元素的索引,然后在删除
var del_index = POST_DATA.ADD[room_id].indexOf(time_id); POST_DATA.ADD[room_id].splice(del_index,1)
c、最后在看下更新数据库的试图函数
import json def book(request): method = request.method.lower() if method == "post": add_dict = json.loads(request.body.decode('utf-8'))["ADD"] del_dict = json.loads(request.body.decode('utf-8'))["DEL"] print(add_dict,del_dict) user_obj = request.user if add_dict: for k in add_dict.keys(): for v in add_dict[k]: models.book.objects.create( user_obj = user_obj, room_obj = models.room.objects.get(rid=int(k)), time_id = v, ) if del_dict: for k in del_dict.keys(): for v in del_dict[k]: models.book.objects.filter( user_obj = user_obj, room_obj = models.room.objects.get(rid=int(k)), time_id = v ).delete() return HttpResponse("ok")
首先从ADD的字典中拿出数据,然后调用create方法更新数据
然后从DEL的字典中拿出数据,然后调用del方法更新数据
最后返回结果,前端重新加载当前页面即可