基于django的会议室预订系统
会议室预订系统
一、目标及业务流程
期望效果:
业务流程:
用户注册
用户登录
预订会议室
退订会议室
选择日期;今日以及以后日期
二、表结构设计和生成
1、models.py(用户继承AbstractUser)
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here. class UserInfo(AbstractUser):
tel = models.CharField(max_length=32,verbose_name="电话")
avatar = models.FileField(upload_to="avatars/", default="avatars/timg.jpg", verbose_name="头像") class Room(models.Model):
"""会议室表"""
caption = models.CharField(max_length=32,verbose_name="会议室名称")
num = models.IntegerField(verbose_name="容纳人数") # 容纳人数 def __str__(self):
return self.caption class Meta:
verbose_name = "会议室信息"
verbose_name_plural = verbose_name class Book(models.Model):
"""会议室预订"""
user = models.ForeignKey(to="UserInfo",on_delete=models.CASCADE)
room = models.ForeignKey(to="Room",on_delete=models.CASCADE)
date = models.DateField()
time_choice = (
(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"),
(14, "21:00"),
(15, "22:00"),
(16, "23:00"),
) time_id = models.IntegerField(choices=time_choice) def __str__(self):
return str(self.user)+"预定了"+str(self.room) class Meta:
verbose_name = "预定信息"
verbose_name_plural = verbose_name
unique_together = (
("room","date","time_id"), # 这三个字段联合唯一,防止重复预订
)
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 |
三、系统登录login
urls.py:
from django.conf.urls import url
from django.contrib import admin
from django.views.static import serve
from django.conf import settings
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 用户登录
url(r'^login/',views.acc_login),
# 展示预订信息
url(r'^index/',views.index),
# 极验滑动验证码 获取验证码的url
url(r'^pc-geetest/register', views.get_geetest),
# media相关的路由设置
url(r'^media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}),
# 处理预订请求
url(r'^book/',views.book),
# 首页
url(r'^home/',views.home),
# 注销
url(r'^logout/',views.acc_logout),
# 用户注册
url(r'^reg/',views.reg),
# 临时测试
url(r'^test/',views.test),
# 修改密码
url(r'^change_password/',views.change_password), ]
login.html(使用了滑动验证)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
<!-- 引入封装了failback的接口--initGeetest -->
<script src="http://static.geetest.com/static/tools/gt.js"></script>
</head>
<body>
<h3 class="text-center" style="color: orangered">欢迎登录会议室预订系统</h3>
<br>
<div class="container">
<div class="row">
<form class="form-horizontal col-md-6 col-md-offset-4" autocomplete="off">
{% csrf_token %}
<div class="form-group">
<label for="username" class="col-lg-2 control-label">用户名</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="username" name="username" placeholder="用户名">
</div>
</div>
<div class="form-group">
<label for="pwd" class="col-lg-2 control-label">密码</label>
<div class="col-sm-6">
<input type="password" class="form-control" id="pwd" name="pwd" placeholder="密码">
</div>
</div>
<div class="form-group">
<!-- 放置极验的滑动验证码 -->
<div id="popup-captcha"></div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="button" id="login_btn" class="btn btn-info">登录</button>
<span class="login-error has-error text-danger"></span>
</div>
</div>
</form>
</div>
</div> <script src="/static/js/jquery-3.3.1.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
<script> var handlerPopup = function (captchaObj) {
// 成功的回调
captchaObj.onSuccess(function () {
var validate = captchaObj.getValidate();
// 1. 取到用户填写的用户名和密码 -> 取input框的值
var username = $("#username").val();
var password = $("#pwd").val();
$.ajax({
url: "/login/", // 进行二次验证
type: "post",
dataType: "json",
data: {
username: username,
pwd: password,
csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(),
geetest_challenge: validate.geetest_challenge,
geetest_validate: validate.geetest_validate,
geetest_seccode: validate.geetest_seccode
},
success: function (data) {
console.log(data);
if (data.status) {
// 有错误,在页面上提示
$(".login-error").text(data.msg);
} else {
// 登陆成功
location.href = data.msg;
}
}
});
}); $("#login_btn").click(function () {
captchaObj.show();
});
// 将验证码加到id为captcha的元素里
captchaObj.appendTo("#popup-captcha");
// 更多接口参考:http://www.geetest.com/install/sections/idx-client-sdk.html
};
//当再次点击input输入框时,错误提示要消失
$("#username,#pwd").focus(function () {
$(".login-error").text("");
}) // 验证开始需要向网站主后台获取id,challenge,success(是否启用failback)
$.ajax({
url: "/pc-geetest/register?t=" + (new Date()).getTime(), // 加随机数防止缓存
type: "get",
dataType: "json",
success: function (data) {
// 使用initGeetest接口
// 参数1:配置参数
// 参数2:回调,回调的第一个参数验证码对象,之后可以使用它做appendTo之类的事件
initGeetest({
gt: data.gt,
challenge: data.challenge,
product: "popup", // 产品形式,包括:float,embed,popup。注意只对PC版验证码有效
offline: !data.success // 表示用户后台检测极验服务器是否宕机,一般不需要关注
// 更多配置参数请参见:http://www.geetest.com/install/sections/idx-client-sdk.html#config
}, handlerPopup);
}
}) </script>
</body>
</html>
login视图函数
from django.shortcuts import render,redirect, HttpResponse
from django.contrib.auth import authenticate, login, logout
from django.http import JsonResponse
from geetest import GeetestLib
from django.contrib.auth.decorators import login_required
from app01 import models
from app01 import forms
import json
import datetime
# 登录视图
def acc_login(request):
if request.method == "POST":
print(request.POST)
res = {"status": 0, "msg": ""}
username = request.POST.get("username")
password = request.POST.get("pwd")
# 获取极验 滑动验证码相关的参数
gt = GeetestLib(pc_geetest_id, pc_geetest_key)
challenge = request.POST.get(gt.FN_CHALLENGE, '')
validate = request.POST.get(gt.FN_VALIDATE, '')
seccode = request.POST.get(gt.FN_SECCODE, '')
status = request.session[gt.GT_STATUS_SESSION_KEY]
user_id = request.session["user_id"]
if status:
result = gt.success_validate(challenge, validate, seccode, user_id)
else:
result = gt.failback_validate(challenge, validate, seccode)
print("####################", result)
if result:
user = authenticate(username=username, password=password)
if user:
login(request, user)
res["msg"] = "/index/"
else:
res["status"] =1
res["msg"] = "认证失败,请检查用户名及密码是否正确"
else:
res["status"] = 1
res["msg"] = "验证码错误"
print("**************", res)
return JsonResponse(res)
return render(request, 'login.html') # 请在官网申请ID使用,示例ID不可使用
pc_geetest_id = "b46d1900d0a894591916ea94ea91bd2c"
pc_geetest_key = "36fc3fe98530eea08dfc6ce76e3d24c4" # 处理极验 获取验证码的视图
def get_geetest(request):
user_id = 'test'
gt = GeetestLib(pc_geetest_id, pc_geetest_key)
status = gt.pre_process(user_id)
request.session[gt.GT_STATUS_SESSION_KEY] = status
request.session["user_id"] = user_id
response_str = gt.get_response_str()
return HttpResponse(response_str)
注意:auth模块的authenticate()方法,提供了用户认证,如果认证信息有效,会返回一个 User 对象;如果认证失败,则返回None。
四、index部分
1、引入admin组件(后台数据管理组件)并完成admin注册
admin.py
from django.contrib import admin
from app01 import models
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import gettext_lazy
# Register your models here. # 配置会议室信息表
class RoomConfig(admin.ModelAdmin):
list_display = ('caption','num')
list_filter=('num',)
search_fields = ('caption','num') # 配置预订信息表
class BookConfig(admin.ModelAdmin):
list_display = ('user','room','date','time_id')
list_filter = ('user','room','date','time_id')
search_fields = ('user','room','date','time_id') # 配置用户管理表
class UserProfileAdmin(UserAdmin):
list_display = ('username','last_login','is_superuser','is_staff','is_active','date_joined')
list_filter = ('last_login', 'is_staff', 'date_joined', 'is_active')
search_fields = ('username',)
fieldsets = (
(None,{'fields':('username','password','first_name','last_name','email')}), (gettext_lazy('用户信息'),{'fields':('username','email','tel','avatar')}), (gettext_lazy('用户权限'), {'fields': ('is_superuser','is_staff','is_active',
'groups', 'user_permissions')}), (gettext_lazy('Important dates'), {'fields': ('last_login', 'date_joined')}),
) admin.site.register(models.Room,RoomConfig)
admin.site.register(models.UserInfo,UserProfileAdmin)
admin.site.register(models.Book,BookConfig)
注意:配置用户管理表至关重要,如果不配置,你会发现在登录admin后添加用户时密码是明文,并没有被加密。
如果你的用户扩展表没有扩展新的字段,可以直接admin.site.register(models.UserInfo,UserAdmin)。
2、登录admin添加数据
3、index视图函数数据处理和index.html模板渲染
index视图
@login_required(login_url="/login/")
def index(request): date = datetime.datetime.now().date()
# 如果没有指定日期,默认使用当天日期
book_date = request.GET.get("book_date",date)
print('日期:', request.GET.get("book_date"))
print("book_date",book_date)
# 获取会议室时间段列表
time_choice = models.Book.time_choice
print(time_choice)
# 获取会议室列表
room_list = models.Room.objects.all()
# 获取会议室预订信息
book_list = models.Book.objects.filter(date=book_date)
htmls=''
for room in room_list:
htmls += '<tr><td>{}({})</td>'.format(room.caption,room.num)
for time in time_choice:
# 判断该单元格是否被预订
flag = False
for book in book_list:
if book.room.pk == room.pk and book.time_id == time[0]:
# 单元格被预定
flag = True
break
if flag:
# 判断当前登录人与预订会议室的人是否一致,一致使用info样式
if request.user.username == book.user.username:
htmls += '<td class="info item" room_id={} time_id={}>{}</td>'.format(room.pk, time[0],book.user.username)
else:
htmls += '<td class="success item" room_id={} time_id={}>{}</td>'.format(room.pk, time[0],
book.user.username) else:
htmls += '<td class="item" room_id={} time_id={}></td>'.format(room.pk,time[0])
htmls += "</tr>"
return render(request,'index.html',{"time_choice":time_choice,"htmls":htmls,})
index前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/datetimepicker/bootstrap-datetimepicker.min.css">
<style>
.td_active{
background-color: purple;
} #my_div{
top: 215px!important;
}
</style> </head>
<body>
<div class="page-header">
<h1 class="text-center">欢迎来到会议室预订系统 <small class="text-info">{{ request.user.username }}</small></h1>
</div> <div class="text-center">
<span>当前用户:<img src="/static/img/info.png" alt=""></span>
<span>其他用户:<img src="/static/img/success.png" alt=""></span>
</div>
<br>
<br>
<p class="text-center">
<span><a href="/home/">返回首页</a></span>
<span><a href="/logout/">注销</a></span>
</p>
<div class="calender pull-right">
<div class='input-group' style="width: 230px;">
<span class="text-warning">注意:当前日期高亮显示</span>
<input type='text' autocomplete="off" class="form-control" id='datetimepicker11' placeholder="请选择日期"/>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar">
</span>
</span> </div>
</div>
<br>
<br>
<table class="table table-bordered">
<thead>
<tr>
<th>会议室/时间</th>
{% for row in time_choice %}
<th>{{ row.1 }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{{ htmls|safe }}
</tbody>
</table>
<div >{% csrf_token %}</div>
<div class="col-lg-offset-6" >
<button class="btn btn-info book_btn">预订</button>
</div> <script src="/static/js/jquery-3.3.1.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
<script src="/static/datetimepicker/bootstrap-datetimepicker.min.js"></script>
<script src="/static/datetimepicker/bootstrap-datetimepicker.zh-CN.js"></script> <script> // 日期格式化方法
Date.prototype.yun = function (fmt) { //author:yun
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;
};
TODAY_DATE=new Date().yun("yyyy-MM-dd");//获取当前日期
var POST_DATA={
"ADD":{},
"DEL":{},
};
function TdClick() {
$(".item").click(function () {
var room_id = $(this).attr("room_id");
var time_id = $(this).attr("time_id"); //取消预订
if($(this).hasClass("info")){
$(this).removeClass("info").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"); //console.log(room_id,time_id)
var index=$.inArray(time_id,POST_DATA.ADD[room_id]);
POST_DATA.ADD[room_id].splice(index,1);
//console.log(POST_DATA.ADD[room_id]);
console.log(POST_DATA); } // 增加预订
else {
$(this).addClass("td_active");
if (POST_DATA.ADD[room_id]){
POST_DATA.ADD[room_id].push(time_id);
}
else
{POST_DATA.ADD[room_id]=[time_id,];} console.log(POST_DATA);
}
});
};
TdClick(); // 日期 if (location.search.slice(11)){
CHOOSE_DATE = location.search.slice(11)
}
else {
CHOOSE_DATE = new Date().yun('yyyy-MM-dd');
console.log(CHOOSE_DATE);
}
// 通过ajax发送数据到后端
$(".book_btn").click(function () {
$.ajax({
url:"/book/",
type:"post",
data:{
choose_date:CHOOSE_DATE,
csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(),
post_data:JSON.stringify(POST_DATA),
},
dataType:"json",
success:function (data) {
console.log(data);
if(data.status==1){ alert("预订成功");
location.href="";
}else if (data.status==2){
alert("未修改信息");
location.href="";
}
else {
alert("已经被预定")
location.href=""
}
},
});
}); // 日历插件
function book_query(e) {
CHOOSE_DATE=e.date.yun("yyyy-MM-dd");
location.href="/index/?book_date="+CHOOSE_DATE;
}; /**
判断输入框中输入的日期格式为yyyy-mm-dd和正确的日期
*/
function isDate(data){
var filter = /((^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(10|12|0?[13578])([-\/\._])(3[01]|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(11|0?[469])([-\/\._])(30|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(0?2)([-\/\._])(2[0-8]|1[0-9]|0?[1-9])$)|(^([2468][048]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([3579][26]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][13579][26])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][13579][26])([-\/\._])(0?2)([-\/\._])(29)$))/;
if (filter.test(data)){
return true;
}else {
return false;
}
}
$("#datetimepicker11").change(function () {
var test = $(this).val();
if(isDate(test)){
if(test<TODAY_DATE){
alert("注意:日期不能小于当前日期!")
}
CHOOSE_DATE=test;
location.href="/index/?book_date="+CHOOSE_DATE;
}else {
alert("日期格式错误!");
location.href='';
}
}); $('#datetimepicker11').datetimepicker({
minView : 2,
startView:2,
language: "zh-CN",
sideBySide: true,
format: 'yyyy-mm-dd',
startDate: TODAY_DATE,
todayBtn:true,
todayHighlight: 1,//当天日期高亮
enterLikeTab: false,
bootcssVer:3,
autoclose:true,
}).on('changeDate',book_query).val(CHOOSE_DATE).css('font-weight','bold');
$(".datetimepicker.datetimepicker-dropdown-bottom-right.dropdown-menu").attr("id" ,"my_div"); </script>
</body>
</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 ()) |
显示效果:
(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)
# 取预约日期,没有指定取当前日期
book_date = request.GET.get("book_date", date)
print(book_date)
index页面访问中,如果没有指定日期,默认显示的就是当前日的预定信息。
因此在循环生成表格时,可以循环确定单元格是否被预定,已经被预定的添加class=‘success’属性。
# 构建标签
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='success'
htmls += "<td class='success' 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='info item' room_id={} time_id={}>{}</td>".format(room.pk, time_choice[0],
book.user.username)
else:
# 非当前登录人自己的预约信息
htmls += "<td class='success item' 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.html)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/datetimepicker/bootstrap-datetimepicker.min.css">
<style>
.td_active{
background-color: purple;
} #my_div{
top: 215px!important;
}
</style> </head>
<body>
<div class="page-header">
<h1 class="text-center">欢迎来到会议室预订系统 <small class="text-info">{{ request.user.username }}</small></h1>
</div> <div class="text-center">
<span>当前用户:<img src="/static/img/info.png" alt=""></span>
<span>其他用户:<img src="/static/img/success.png" alt=""></span>
</div>
<br>
<br>
<p class="text-center">
<span><a href="/home/">返回首页</a></span>
<span><a href="/logout/">注销</a></span>
</p>
<div class="calender pull-right">
<div class='input-group' style="width: 230px;">
<span class="text-warning">注意:当前日期高亮显示</span>
<input type='text' autocomplete="off" class="form-control" id='datetimepicker11' placeholder="请选择日期"/>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar">
</span>
</span> </div>
</div>
<br>
<br>
<table class="table table-bordered">
<thead>
<tr>
<th>会议室/时间</th>
{% for row in time_choice %}
<th>{{ row.1 }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
<!--由于模板语法功能不够强大,因此数据处理还是放在后台,在这里渲染后台传递来的标签字符串-->
{{ htmls|safe }}
</tbody>
</table>
<div >{% csrf_token %}</div>
<div class="col-lg-offset-6" >
<button class="btn btn-info book_btn">预订</button>
</div> <script src="/static/js/jquery-3.3.1.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
<script src="/static/datetimepicker/bootstrap-datetimepicker.min.js"></script>
<script src="/static/datetimepicker/bootstrap-datetimepicker.zh-CN.js"></script> <script> // 日期格式化方法
Date.prototype.yun = function (fmt) { //author:yun
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;
};
TODAY_DATE=new Date().yun("yyyy-MM-dd");//获取当前日期
var POST_DATA={
"ADD":{},
"DEL":{},
};
function TdClick() {
$(".item").click(function () {
var room_id = $(this).attr("room_id");
var time_id = $(this).attr("time_id"); //取消预订
if($(this).hasClass("info")){
// 如果点击的标签具有info类,直接删除info类并清空内容
$(this).removeClass("info").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");
//点击删除临时预订的数据
var index=$.inArray(time_id,POST_DATA.ADD[room_id]);
POST_DATA.ADD[room_id].splice(index,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,];}
}
});
};
TdClick(); // 日期 if (location.search.slice(11)){
CHOOSE_DATE = location.search.slice(11)
}
else {
CHOOSE_DATE = new Date().yun('yyyy-MM-dd'); }
// 通过ajax发送数据到后端
$(".book_btn").click(function () {
$.ajax({
url:"/book/",
type:"post",
data:{
choose_date:CHOOSE_DATE,
csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(),
post_data:JSON.stringify(POST_DATA),
},
dataType:"json",
success:function (data) {
console.log(data);
if(data.status==1){ alert("预订成功");
location.href="";
}else if (data.status==2){
alert("未修改信息");
location.href="";
}
else {
alert("已经被预定")
location.href=""
}
},
});
}); // 日历插件
function book_query(e) {
CHOOSE_DATE=e.date.yun("yyyy-MM-dd");
location.href="/index/?book_date="+CHOOSE_DATE;
}; /**
判断输入框中输入的日期格式为yyyy-mm-dd和正确的日期
*/
function isDate(data){
var filter = /((^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(10|12|0?[13578])([-\/\._])(3[01]|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(11|0?[469])([-\/\._])(30|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(0?2)([-\/\._])(2[0-8]|1[0-9]|0?[1-9])$)|(^([2468][048]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([3579][26]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][13579][26])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][13579][26])([-\/\._])(0?2)([-\/\._])(29)$))/;
if (filter.test(data)){
return true;
}else {
return false;
}
}
$("#datetimepicker11").change(function () {
var test = $(this).val();
if(isDate(test)){
if(test<TODAY_DATE){
alert("注意:日期不能小于当前日期!")
}
CHOOSE_DATE=test;
location.href="/index/?book_date="+CHOOSE_DATE;
}else {
alert("日期格式错误!");
location.href='';
}
}); $('#datetimepicker11').datetimepicker({
minView : 2,
startView:2,
language: "zh-CN",
sideBySide: true,
format: 'yyyy-mm-dd',
startDate: TODAY_DATE,
todayBtn:true,
todayHighlight: 1,//当天日期高亮
enterLikeTab: false,
bootcssVer:3,
autoclose:true,
}).on('changeDate',book_query).val(CHOOSE_DATE).css('font-weight','bold');
$(".datetimepicker.datetimepicker-dropdown-bottom-right.dropdown-menu").attr("id" ,"my_div"); </script>
</body>
</html>
index.html
1、点击事件预定和取消——组织数据
var POST_DATA={
"ADD":{},
"DEL":{},
};
function TdClick() {
$(".item").click(function () {
var room_id = $(this).attr("room_id");
var time_id = $(this).attr("time_id"); //取消预订
if($(this).hasClass("info")){
// 如果点击的标签具有info类,直接删除info类并清空内容
$(this).removeClass("info").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");
//点击删除临时预订的数据
var index=$.inArray(time_id,POST_DATA.ADD[room_id]);
POST_DATA.ADD[room_id].splice(index,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,];}
}
});
};
TdClick();
注意:
(1)取消预定事件
<script>
// 为td绑定单击事件
function BindTd() {
$('.item').click(function () {
// alert($(this).attr("room_id")); // 点击显示会议室id // 取消预定
if ($(this).hasClass("info")){
// 如果点击的标签具有active类,直接删除active类并清空内容
$(this).removeClass("info").empty();
}
else if ($(this).hasClass("td_active")) {
$(this).removeClass("td_active");
}
else {
// 空白局域点击
$(this).addClass("td_active");
}
})
}
BindTd();
</script>
在这次只处理了具有info类和td_active类的情况,但没有处理success类的情况,因为这种需要判断的情况,一定要交给后端,否则就是前端一套后端一套,点击保存按钮发送的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中,以完成预定工作:
function TdClick() {
$(".item").click(function () {
var room_id = $(this).attr("room_id");
var time_id = $(this).attr("time_id"); //取消预订
if($(this).hasClass("info")){
// 如果点击的标签具有info类,直接删除info类并清空内容
$(this).removeClass("info").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");
//点击删除临时预订的数据
var index=$.inArray(time_id,POST_DATA.ADD[room_id]);
POST_DATA.ADD[room_id].splice(index,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,];}
console.log(POST_DATA.ADD);
}
});
};
TdClick();
(3)临时预定取消数据处理
//取消临时预订
else if($(this).hasClass("td_active")){
$(this).removeClass("td_active");
//点击删除临时预订的数据
var index=$.inArray(time_id,POST_DATA.ADD[room_id]);
POST_DATA.ADD[room_id].splice(index,1); }
(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"
网络编程本质是浏览器和服务器之间发送字符串,以POST请求为例
POST: 浏览器-------------------->server
"请求首行\r\nContent-Type:url_encode\r\n\r\na=1&b=2"
"请求首行\r\nContent-Type: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
2、日历插件(datetimepicker)官方文档:http://eonasdan.github.io/bootstrap-datetimepicker/
日历html
<div class="calender pull-right">
<div class='input-group' style="width: 230px;">
<span class="text-warning">注意:当前日期高亮显示</span>
<input type='text' autocomplete="off" class="form-control" id='datetimepicker11' placeholder="请选择日期"/>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar">
</span>
</span> </div>
</div>
日历js代码
// 日期格式化方法
Date.prototype.yun = function (fmt) { //author:yun
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;
};
TODAY_DATE=new Date().yun("yyyy-MM-dd");//获取当前日期 // 日期 if (location.search.slice(11)){
CHOOSE_DATE = location.search.slice(11)
}
else {
CHOOSE_DATE = new Date().yun('yyyy-MM-dd'); } // 日历插件
function book_query(e) {
CHOOSE_DATE=e.date.yun("yyyy-MM-dd");
location.href="/index/?book_date="+CHOOSE_DATE;
}; /**
判断输入框中输入的日期格式为yyyy-mm-dd和正确的日期
*/
function isDate(data){
var filter = /((^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(10|12|0?[13578])([-\/\._])(3[01]|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(11|0?[469])([-\/\._])(30|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(0?2)([-\/\._])(2[0-8]|1[0-9]|0?[1-9])$)|(^([2468][048]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([3579][26]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][13579][26])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][13579][26])([-\/\._])(0?2)([-\/\._])(29)$))/;
if (filter.test(data)){
return true;
}else {
return false;
}
}
$("#datetimepicker11").change(function () {
var test = $(this).val();
if(isDate(test)){
if(test<TODAY_DATE){
alert("注意:日期不能小于当前日期!")
}
CHOOSE_DATE=test;
location.href="/index/?book_date="+CHOOSE_DATE;
}else {
alert("日期格式错误!");
location.href='';
}
}); //初始化日历
$('#datetimepicker11').datetimepicker({
minView : 2,
startView:2,
language: "zh-CN",
sideBySide: true,
format: 'yyyy-mm-dd',
startDate: TODAY_DATE,
todayBtn:true,
todayHighlight: 1,//当天日期高亮
enterLikeTab: false,
bootcssVer:3,
autoclose:true,
}).on('changeDate',book_query).val(CHOOSE_DATE).css('font-weight','bold');
$(".datetimepicker.datetimepicker-dropdown-bottom-right.dropdown-menu").attr("id" ,"my_div");
3、发送AJAX
// 通过ajax发送数据到后端
$(".book_btn").click(function () {
$.ajax({
url:"/book/",
type:"post",
data:{
choose_date:CHOOSE_DATE,
csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(),
post_data:JSON.stringify(POST_DATA),
},
dataType:"json",
success:function (data) {
console.log(data);
if(data.status==1){ alert("预订成功");
location.href="";
}else if (data.status==2){
alert("未修改信息");
location.href="";
}
else {
alert("已经被预定")
location.href=""
}
},
});
});
六、视图处理图书预定和取消
def book(request):
if request.method == "POST":
choose_date = request.POST.get("choose_date")
print("choose_date:", choose_date)
# 获取会议室时间段列表
time_choice = models.Book.time_choice
try:
# 向数据库修改会议室预订记录
post_data = json.loads(request.POST.get("post_data"))
if not post_data["ADD"] and not post_data["DEL"]:
res = {"status":2, "msg":""}
return HttpResponse(json.dumps(res))
user = request.user
print(type(post_data), post_data)
# 添加新的预订信息
book_list = []
for room_id, time_id_list in post_data["ADD"].items():
for time_id in time_id_list:
book_obj = models.Book(user=user, room_id=room_id, time_id=time_id, date=choose_date)
book_list.append(book_obj)
models.Book.objects.bulk_create(book_list) # 删除旧的预订信息
from django.db.models import Q
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:
models.Book.objects.filter(remove_book).delete()
for time in post_data["DEL"][room_id]:
models.Book.objects.filter(user=user, room_id=room_id, time_id=time, date=choose_date).delete()
res = {"status": 1, "msg": ''}
except Exception as e:
res = {"status": 0, "msg": str(e)}
return HttpResponse(json.dumps(res))
七、用户注册
注册前端页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>欢迎注册</title>
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
<style>
html, body { width: 100%; height: 100%; } #avatar-img { width: 80px;
height: 80px;
} .mui-content { background: url("/static/img/reg_bak.jpg") bottom center no-repeat #efeff4; background-size: 100% 100%; width: 100%; height: 100%; }
</style>
</head>
<body> <div class="container mui-content">
<h3 class="text-center" style="color: orangered">欢迎注册会议室预订系统</h3>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<form novalidate action="/reg/" method="post" autocomplete="off" class="form-horizontal reg-form" enctype="multipart/form-data">
{% csrf_token %}
<!--头像-->
<div class="form-group text-center" style="margin-top: 80px">
<label class="col-sm-2 control-label">头像</label>
<div class="col-sm-8">
<label for="id_avatar"><img id="avatar-img" src="/static/img/timg.jpg" alt=""></label>
<input accept="image/*" type="file" name="avatar" id="id_avatar" style="display: none">
<span class="help-block"></span>
</div>
</div>
<!--用户名-->
<div class="form-group">
<label for="{{ form_obj.username.id_for_label }}"
class="col-sm-2 control-label">{{ form_obj.username.label }}</label>
<div class="col-sm-8">
{{ form_obj.username }}
<span class="help-block">{{ form_obj.username.errors.0 }}</span>
</div>
</div>
<!--邮箱-->
<div class="form-group">
<label for="{{ form_obj.email.id_for_label }}"
class="col-sm-2 control-label">{{ form_obj.email.label }}</label>
<div class="col-sm-8">
{{ form_obj.email }}
<span class="help-block">{{ form_obj.email.errors.0 }}</span>
</div>
</div>
<!--密码-->
<div class="form-group">
<label for="{{ form_obj.password.id_for_label }}"
class="col-sm-2 control-label">{{ form_obj.password.label }}</label>
<div class="col-sm-8">
{{ form_obj.password }}
<span class="help-block">{{ form_obj.password.errors.0 }}</span>
</div>
</div>
<!--确认密码-->
<div class="form-group">
<label for="{{ form_obj.re_password.id_for_label }}"
class="col-sm-2 control-label">{{ form_obj.re_password.label }}</label>
<div class="col-sm-8">
{{ form_obj.re_password }}
<span class="help-block">{{ form_obj.re_password.errors.0 }}</span>
</div>
</div>
<!--注册-->
<div class="form-group">
<div class="col-sm-offset-4 col-sm-12">
<button type="button" class="btn btn-success" id="reg-submit">注册</button>
<input type="reset" class="btn btn-danger" value="重置">
<a class="panel-warning" href="/home/">返回首页</a>
</div> </div>
</form>
</div>
</div>
</div> <script src="/static/js/jquery-3.3.1.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
<script>
// 找到头像的input标签绑定change事件
$("#id_avatar").change(function () {
// 1. 创建一个读取文件的对象
var fileReader = new FileReader();
// 取到当前选中的头像文件
// console.log(this.files[0]);
// 读取你选中的那个文件
fileReader.readAsDataURL(this.files[0]); // 读取文件是需要时间的
fileReader.onload = function () {
// 2. 等上一步读完文件之后才 把图片加载到img标签中
$("#avatar-img").attr("src", fileReader.result);
};
});
// AJAX提交注册的数据
$("#reg-submit").click(function () {
// 取到用户填写的注册数据,向后端发送AJAX请求
var formData = new FormData();
formData.append("username", $("#id_username").val());
formData.append("password", $("#id_password").val());
formData.append("re_password", $("#id_re_password").val());
formData.append("email", $("#id_email").val());
formData.append("avatar", $("#id_avatar")[0].files[0]);
formData.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val()); $.ajax({
url: "/reg/",
type: "post",
processData: false, //告诉Jquery不要处理我的数据
contentType: false, //告诉jQuery不要设置content类型
data: formData,
success:function (data) {
if (data.status){
// 有错误就展示错误
// console.log(data.msg);
// 将报错信息填写到页面上
$.each(data.msg, function (k,v) {
// console.log("id_"+k, v[0]);
// console.log($("#id_"+k));
$("#id_"+k).next("span").text(v[0]).parent().parent().addClass("has-error");
}) }else {
// 没有错误就跳转到指定页面
location.href = data.msg;
}
}
})
}); // 将所有的input框绑定获取焦点的事件,将所有的错误信息清空
$("form input").focus(function () {
$(this).next().text("").parent().parent().removeClass("has-error");
});
//给username的input输入框,绑定失去焦点事件,失去焦点后检测用户名是否存在
$("#id_username").blur(function () {
// 取到用户填写的值
var username = $(this).val();
//发ajax请求
$.ajax({
url:"/check_username_exist/",
type:"get",
data:{username:username,},
success:function (data) {
if(data.status){
//有错误,用户名已被注册
$("#id_username").next().text(data.msg).parent().parent().addClass("has-error");
} }
})
})
</script>
</body>
</html>
reg.html
注册后端页面
def reg(request):
if request.method == "POST":
ret = {"status": 0, "msg": ""}
form_obj = forms.RegForm(request.POST)
print('request.POST'.center(80, '#'))
print(request.POST)
print('request.POST'.center(80, '#'))
avatar_img = request.FILES.get("avatar")
print(avatar_img)
# 帮我做校验
if form_obj.is_valid():
# 校验通过,去数据库创建一个新的用户
form_obj.cleaned_data.pop("re_password")
print(form_obj.cleaned_data)
try:
models.UserInfo.objects.create_user(**form_obj.cleaned_data,avatar=avatar_img)
except Exception as e:
print(e)
ret["msg"] = "/login/"
return JsonResponse(ret)
else:
print(form_obj.errors)
ret["status"] = 1
ret["msg"] = form_obj.errors
print(ret)
print("=" * 120)
return JsonResponse(ret)
# 生成一个form对象
form_obj = forms.RegForm()
print(form_obj.fields)
return render(request,'reg.html',{"form_obj": form_obj})
八、项目源码
https://github.com/Yun-Wangjun/BookSystem
基于django的会议室预订系统的更多相关文章
- 基于Django的在线考试系统
概述 基于Django的在线考试系统,适配电脑端,可以实现出题,答题,排行榜,倒计时等等等功能 详细 代码下载:http://www.demodashi.com/demo/13923.html 项目目 ...
- 会议室预订系统(meeting room booking system)
一.mrbs mrbs:(meeting room booking system) 二.效果 三.models from django.db import models # Create your ...
- 基于django的博客系统
这是前段代码 达到的效果并不是太好,但我还是要发出来,有更好的建议可以和我讨论 后台还算可以 添加了分类和文章两个功能,还在优化,敬请期待....
- 会议室预订系统 td 宽度 php 浏览器 兼容性
w获取浏览器标识 <style> .w > td { <?php $wua=$_SERVER['HTTP_USER_AGENT']; if(strpos($wua, 'Chro ...
- MRBS开源会议室预订系统安装
MRBS系统官方网址 https://mrbs.sourceforge.io/ 最近在找一份开源的会议室预订系统,找了很多种,ASP,PHP的,测试发现MRBS无疑是最好的.开源社区对其介绍如下:M ...
- 启明星MRBS会议室预约系统V30.0发布
MRBS系统官方网址 https://www.dotnetcms.org/ 在线演示 http://demo.dotnetcms.org/mrbs 用户名admin,密码123456 Meeting ...
- 基于Django的Rest Framework框架的RESTful规范研究
一.什么是RESTful规范 总的来说:RESTful规范就是一个非技术,人为约定的一个面向资源的架构理念. REST与技术无关,代表的是一种软件架构风格,REST是Representational ...
- [系统开发] 一个基于Django和PureCSS的内容管理系统
这是我刚开发的一套基于Django和PureCSS的内容管理系统,目标是优雅.简洁.实用,目前功能还在完善中. 系统参考了网上的教程,除了文章管理.搜索.RSS,还增加了类别管理.用户管理,以及评论管 ...
- Django之会议室预预订
model表设计: from django.db import models from django.contrib.auth.models import AbstractUser # Create ...
随机推荐
- .net core 读取Excal文件数据及注意事项
添加ExcelDataReader.DataSet引用. 调用下列方法: public class XlsHelper { public static System.Data.DataSet GetX ...
- Linux基础:CentOS 6重置密码
1.开机,按"e"键,进入GNU GRUB引导界面,上下键选择中间行 2.按"e"键,进入编辑界面,末行quiet后空格,输入"1"或者&q ...
- $Poj1952\ $洛谷$1687\ Buy\ Low,Buy\ Lower$ 线性$DP+$方案计数
Luogu Description 求一个长度为n的序列a的最长下降子序列的长度,以及这个长度的子序列种数,注意相同的几个子序列只能算作一个子序列. n<=5000,a[i]不超过long范围 ...
- IDEA启动报错Internal error. Please report to http://jb.gg/ide/critical-startup-errors java.lang.NoClassDefFoundError: org/eclipse/xtext/xbase/lib/Exceptions
报错内容: IDEA 启动报错 Internal error. Please report to http://jb.gg/ide/critical-startup-errors 报错图为: 我尝试找 ...
- 对 Redux 一头雾水?看完这篇就懂了
首先,学习 Redux 可能会很困难 当你终于学会了如何使用 React,也有了自己去构建一些应用的信心,那会是一种非常棒的感觉.你学会了管理状态,一切看起来井井有条.但是,很有可能这就到了你该学习 ...
- 你确定你了解什么是linux系统?
1.什么是linux发行版 就Linux的本质来说,它只是操作系统的核心,负责控制硬件.管理文件系统.程序进程等,并不给用户提供各种工具和应用软件.所谓工欲善其事,被必先利其器,一套在优秀的操作系统核 ...
- Redis-NoSQL入门和概述(一)
NoSQL简史及定义 NoSQL 这个术语最早是在 1998 年被Carlo Strozzi命名在他的轻量的,开源的关系型数据库上的,但是该数据库没有提供标准的SQL接口:在2009 年再次被Eric ...
- Win10下设置默认输入法与默认中文输入
实现的效果: 把自己需要的一个或多个输入法软件添加到输入法列表中(一般就指定一个),避免了需要在打字时Ctrl + Shift等快捷键在多个输入法中不停切换的麻烦 首选语言默认为中文,毕竟作为一个中国 ...
- 2019 年百度之星 初赛一 1002 Game
传送门 Problem Description 度度熊在玩一个好玩的游戏.游戏的主人公站在一根数轴上,他可以在数轴上任意移动,对于每次移动,他可以选择往左或往右走一格或两格.现在他要依次完成 n 个任 ...
- GXOI&GZOI
T1 与或和 2s&&512MB 简明题意:求一个矩阵的所有子序列的 \(and\)和 和\(or\)和: 子矩阵的\(and\)和就是所有值\(and\)起来:\(or\)类 ...