先梳理一下我们的会议室预定是怎么做的
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方法更新数据
最后返回结果,前端重新加载当前页面即可