对于嘉宾管理页面来说,当前需要一个分页功能,一场发布会需要由几千位嘉宾参加,如果将所有的嘉宾信息不做分页地显示在页面上,不仅页面的加载速度会受到严重影响,而且页面一次显示几千条甚至几万条并不方便查看,Django提供了Paginator类来实现分页功能,分页功能略为复杂,首先进入Django的shell模式,练习Paginator类的基本使用,请在嘉宾表至少添加5名嘉宾信息,以便接下来的练习,sign_guest表里已创建5条数据
from django.core.paginator import Paginator # 导入Paginator类
from sign.models import Guest # Guest下面所有表
guest_list = Guest.objects.all() # 查询Guest表得所有数据
p = Paginator(guest_list, 2) # 创建每页2条数据的分页器
p.count # 查看共有多少条数据
p.page_range # 查看共分多少页(每页2条数据),循环结果为1,2,3(共3页)
######第一页######
page1 = p.page(1) # 获取第1页数据
page1 # 当前第几页,打印出<Page 1 of 3>
page1.object_list # 当前页的对象,打印出<QuerySet [<Guest:马努1>,<Guest:马努2>]
for g in page1: # 循环打印第1页嘉宾的realname
g.realname
######第二页######
page2 = p.page(2) # 获取第2页数据
page2.start_index() # 本页第一条数据,打印3
page2.end_index() # 本页最后一条数据,打印4
page2.has_previous() # 是否有上一页,打印True
page2.has_next() # 是否有下一页,打印True
page2.previous_page_number() # 上一页是第几页
page2.next_page_number() # 下一页是第几页
######第三页######
page3 = p.page(3) # 获取第3页数据
page3.has_next() # 是否有下一页,打印False
page3.has_previous() # 是否有上一页,打印True
page3.has_other_pages() # 是否有其他页,打印True
page3.previous_page_number() # 上一页是第几页,打印2
下面就来实现分页吧,打开/sign/views.py文件,修改guest_manage()视图函数
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
# 嘉宾管理
@login_required
def guest_manage(request):
username = request.session.get('user', '')
guest_list = Guest.objects.all()
paginator = Paginator(guest_list, 2)
page = request.GET.get('page')
try:
contacts = paginator.page(page)
except PageNotAnInteger:
contacts = paginator.page(1) # 如果page不是整数,取第一页面数据
except EmptyPage:
contacts = paginator.page(paginator.num_pages) # 如果page不在范围,取最后一页面
return render(request, 'guest_manage.html', {'user': username, 'guests': contacts})
paginator = Paginator(guest_list, 2) 把查询出来的所有嘉宾列表guest_list放到Paginator类中,划分每页显示2条数据,一般情况下,一页会显示10条数据,由于我们的测试数据较少,所以这里划分为每页2条
page = request.GET.get('page') 通过GET请求得到当前要显示第几页的数据
contacts = paginator.page(page) 获取第page页的数据,如果没有第page页,抛出PageNotAnInteger异常,返回第一页的数据,如果超出页数范围,则抛出EmptyPage异常,返回最后一页的数据,最后将得到的某一页数据返回至嘉宾管理页面上
在/templates/guest_manage.html文件中添加分页器的代码:
<!-- 列表分页器 -->
<div class="pagination">
<span class="step-links">
{% if guests.has_previous %}
<a href="?page={{ guests.previous_page_number }}">previous</a>
{% endif %}
<span class="current">
Page{{ guests.number }} of {{ guests.paginator.num_pages }}.
</span>
{% if guests.has_next %}
<a href="?page={{ guests.next_page_number }}">next</a>
{% endif %}
</span>
</div>
最后运行guest_manage.html可以看到嘉宾管理分页功能,我的代码不知道哪里出问题了,没有看到
签到功能:
对于发布会签到系统来说,最重要的功能就是签到,而发布会管理功能和嘉宾管理功能都要服务于签到功能
添加签到链接:
对于签到功能页面来说,它应该所属于某一场发布会,在打开签到页面之前,我们应该知道这是属于哪一场发布会的签到,所以最好的方式是在发布会列表中,给每一条发布会都提供一个签到链接,用来打开对应的签到页面,在/templates/event_manage.html页面,增加一列签到链接,新增的代码如下:
运行event_manage.html,发布会管理列表多了一列签到链接,当点击sign链接时,路径会默认跳转到/sign_index/{{ event.id }}/路径,其中{{ event.id }}为发布会的id,target="{{ event.id }}_blank属性设置链接在新窗口打开,在/guest/urls.py文件中添加签到页面路径的路由,urls.py文件中新增的代码如下:
此处与之前添加的路径在匹配方式上略有不同,(?P<eid>[0-9]+)匹配发布会id,而且必须为数字,匹配的数字eid将会作为sign_index()视图函数的参数
签到页面:
打开/sign/views.py文件,创建sign_index()视图函数,views.py文件里新增的代码如下:
from django.shortcuts import render, get_object_or_404
# 签到页面
@login_required
def sign_index(request, eid):
event = get_object_or_404(Event, id=eid)
return render(request, 'sign_index.html', {'event': event})
sign_index()函数获取从url配置得到的eid,作为发布会的id的查询条件,这里又学到一个很有用的方法,get_object_or_404(),它默认调用django的table.objects.get()方法,如果查询的对象不存在,则会抛出一个HTTP404异常,这就省去了table.objects.get()方法的异常断言,创建/templates/sign_index.html签到页面,新增的代码如下:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
{% load bootstrap3 %}
{% bootstrap_css %}
{% bootstrap_javascript %}
<meta charset="UTF-8">
<title>Guest Manage</title>
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">{{ event.name }}</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li><a href="/event_manage/">发布会</a></li>
<li><a href="/guest_manage/">嘉宾</a></li>
</ul>
</div>
</div>
</nav>
<!-- 签到功能 -->
<div class="page-header" style="padding-top: 80px;">
<div id="navbar" class="navbar-collapse collapse">
<form class="navbar-form" method="post" action="/sign_index_action/{{ event.id }}/">
<div class="form-group">
<input name="phone" type="text" placeholder="输入手机号" class="form-control">
</div>
<button type="submit" class="btn btn-success">签到</button>
<font color="red">
<br>{{ hint }}
<br>{{ guest.realname }}
<br>{{ guest.phone }}
</font>
</form>
</div>
</div>
</body>
</html>
<a class="navbar-brand" href="#">{{ event.name }}</a> 将页面标题设置为发布会名称
<form class="navbar-form" method="post" action="/sign_index_action/{{ event.id }}/"> 设置发布会与嘉宾导航链接,签到表单通过post请求将签到手机号提交到/sign_index_action/{{ event.id }}/路径,{{ event.id }}为替换为具体的发布会id
<font color="red">
<br>{{ hint }}
<br>{{ guest.realname }}
<br>{{ guest.phone }}
</font>
{{ hint }}用于显示签到成功和失败的提示信息,当签到成功时,{{ guest.realname }}和{{ guest.phone }}将显示嘉宾的姓名和手机号,嘉宾签到页面正常显示,我这不知道是什么原因,没有出来页面
签到动作:
签到功能并未开发完成,当用户在签到页面输入手机号,单击签到按钮后,/sign_index_action/{{ event.id }}路径要如何处理?,打开/guest/urls.py文件,添加签到动作路径的路由,最后一行添加如下内容:
url(r'^sign_index_action/(?P<eid>[0-9]+)/$', views.sign_index_action),打开/sign/views.py文件,创建sign_index_action()视图函数,新增代码如下:
# 签到动作
@login_required
def sign_index_action(request, eid):
event = get_object_or_404(Event, id=eid)
phone = request.POST.get('phone', '')
print(phone)
result = Guest.objects.filter(phone=phone)
if not result:
return render(request, 'sign_index.html', {'event': event, 'hint': 'phone error.'})
result = Guest.objects.filter(phone=phone, event_id=eid)
if not result:
return render(request, 'sign_index.html', {'event': event, 'hint': 'event id or phone error.'})
result = Guest.objects.get(phone=phone, event_id=eid)
if not result:
return render(request, 'sign_index.html', {'event': event, 'hint': 'user has sign in.'})
else:
Guest.objects.filter(phone=phone, event_id=eid).update(sign='1')
return render(request, 'sign_index.html', {'event': event, 'hint': 'sign in success!', 'guest': result})
对于发布会的签到功能的验证,分别做了以下条件的判断:
result = Guest.objects.filter(phone=phone) 查询手机号在Guest表中是否存在,如果不存在则提示用户"phone error."
result = Guest.objects.filter(phone=phone, event_id=eid) 通过手机和发布会id两个条件来查询Guest表,如果结果为空,则说明手机号和发布会不匹配,将提示用户event id or phone error.
result = Guest.objects.get(phone=phone, event_id=eid)
if not result:
return render(request, 'sign_index.html', {'event': event, 'hint': 'user has sign in.'})
else:
Guest.objects.filter(phone=phone, event_id=eid).update(sign='1')
return render(request, 'sign_index.html', {'event': event, 'hint': 'sign in success!', 'guest': result})
判断嘉宾的签到状态是否为True(1),如果为True,则表示嘉宾已经签过到了,将提示用户user has sign in.,否则说明嘉宾未签到,修改签到状态为1(已签到),提示用户sign in success!,并且显示嘉宾的姓名和手机号,在弹出的页面输入手机号,点击签到按钮,签到成功会显示sign in success,显示realname和phone,我这不知道是什么原因,没有出来页面
退出系统:
在发布会管理页面和嘉宾管理页面的右上角都有退出按钮,但我们并没有实现退出功能,现在是时候填补它了,打开/urls.py文件,添加退出路径的路由,最后一行添加如下内容url(r'^logout/$', views.logout),打开/sign/views.py文件,创建logout()视图函数,新增代码如下:
# 退出登录
@login_required
def logout(request):
auth.logout(request)
response = HttpResponseRedirect('/index/')
return response
Django不但提供了auth.login()方法用于验证登录用户信息,同时也提供了auth.logout()方法用于系统的退出,它可以清楚浏览器保存的用户信息,所以不用再考虑如何删除浏览器cookie的问题了,当退出成功后默认跳转到/index/路径,即用户登录页面
关于发布会签到系统页面的开发到此为止,但是还有许多功能需要进一步完善,比如登录页面,需要增加样式,发布会与嘉宾的添加、修改、批量导入发布会与嘉宾数据等功能,以及签到页面,虽然功能已经实现了,但签到页的设计风格要与发布会的主体相匹配,如果你想让该系统用于实际生产中,这些都是接下来需要考虑的问题