Django的分页器(paginator)
demo
视图
from django.shortcuts import render,HttpResponse # Create your views here. from app01.models import * from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger def index(request): ''' 批量导入数据: Booklist=[] for i in range(100): Booklist.append(Book(title="book"+str(i),price=30+i*i)) Book.objects.bulk_create(Booklist) ''' ''' 分页器的使用: book_list=Book.objects.all() paginator = Paginator(book_list, 10) print("count:",paginator.count) #数据总数 print("num_pages",paginator.num_pages) #总页数 print("page_range",paginator.page_range) #页码的列表 page1=paginator.page(1) #第1页的page对象 for i in page1: #遍历第1页的所有数据对象 print(i) print(page1.object_list) #第1页的所有数据 page2=paginator.page(2) print(page2.has_next()) #是否有下一页 print(page2.next_page_number()) #下一页的页码 print(page2.has_previous()) #是否有上一页 print(page2.previous_page_number()) #上一页的页码 # 抛错 #page=paginator.page(12) # error:EmptyPage #page=paginator.page("z") # error:PageNotAnInteger ''' book_list=Book.objects.all() paginator = Paginator(book_list, 10) page = request.GET.get('page',1) currentPage=int(page) try: print(page) book_list = paginator.page(page) except PageNotAnInteger: book_list = paginator.page(1) except EmptyPage: book_list = paginator.page(paginator.num_pages) return render(request,"index.html",{"book_list":book_list,"paginator":paginator,"currentPage":currentPage})
模板
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <div class="container"> <h4>分页器</h4> <ul> {% for book in book_list %} <li>{{ book.title }} -----{{ book.price }}</li> {% endfor %} </ul> <ul class="pagination" id="pager"> {% if book_list.has_previous %} <li class="previous"><a href="/index/?page={{ book_list.previous_page_number }}">上一页</a></li> {% else %} <li class="previous disabled"><a href="#">上一页</a></li> {% endif %} {% for num in paginator.page_range %} {% if num == currentPage %} <li class="item active"><a href="/index/?page={{ num }}">{{ num }}</a></li> {% else %} <li class="item"><a href="/index/?page={{ num }}">{{ num }}</a></li> {% endif %} {% endfor %} {% if book_list.has_next %} <li class="next"><a href="/index/?page={{ book_list.next_page_number }}">下一页</a></li> {% else %} <li class="next disabled"><a href="#">下一页</a></li> {% endif %} </ul> </div> </body> </html>
进阶限制所显示的分页数量
1 def index(request): 2 3 4 book_list=Book.objects.all() 5 6 paginator = Paginator(book_list, 15) 7 page = request.GET.get('page',1) 8 currentPage=int(page) 9 10 # 如果页数十分多时,换另外一种显示方式 11 if paginator.num_pages>11: 12 13 if currentPage-5<1: 14 pageRange=range(1,11) 15 elif currentPage+5>paginator.num_pages: 16 pageRange=range(currentPage-5,paginator.num_pages+1) 17 18 else: 19 pageRange=range(currentPage-5,currentPage+5) 20 21 else: 22 pageRange=paginator.page_range 23 24 25 try: 26 print(page) 27 book_list = paginator.page(page) 28 except PageNotAnInteger: 29 book_list = paginator.page(1) 30 except EmptyPage: 31 book_list = paginator.page(paginator.num_pages) 32 33 34 return render(request,"index.html",locals())
自定义分页
自定义分页组件
import copy """ 自定义分页 """ class Pagination(object): def __init__(self, current_page_num, all_count, request, per_page_num=10, max_page_num=11): """ 封装分页相关数据 :param current_page_num: 当前页码 :param all_count: 数据总数 :param per_page_num: 每页显示的数据条数 :param max_page_num: 最大显示的页数 """ # 捕捉异常页码,异常则显示第一页 try: current_page_num = int(current_page_num) except Exception as e: current_page_num = 1 if current_page_num < 1: current_page_num = 1 if current_page_num > all_count/per_page_num: all_pager, tmp = divmod(all_count, per_page_num) if tmp: current_page_num = all_pager+1 else: current_page_num = all_pager self.current_page_num = current_page_num self.all_count = all_count self.per_page_num = per_page_num self.max_page_num = max_page_num # 计算总页数 all_pager, tmp = divmod(all_count, per_page_num) if tmp: all_pager += 1 self.all_pager = all_pager # 计算最多显示页数的一半,为了页数过多时,只实现一定的数量的实现做准备 self.max_page_num_half = int((max_page_num-1)/2) # 保存搜索条件 获取{"a":"1","b":"2"} self.params = copy.deepcopy(request.GET) # 计算当前页所显示数据的起始索引 @property def start(self): return (self.current_page_num - 1)*self.per_page_num # 计算当前页所显示数据的结束索引 @property def end(self): return self.current_page_num * self.per_page_num # 自定义分页的逻辑函数 def page_html(self): # 总页码数 < 最大显示页码数,即全部显示 if self.all_pager < self.max_page_num: page_start = 1 page_end = self.all_pager + 1 # 总页码数 > 最大显示页码数,即显示最大页码数 else: # 当前页 <= 最多显示页数的一半,即显示前max_page_num条 if self.current_page_num <= self.max_page_num_half: page_start = 1 page_end = self.max_page_num + 1 # 当前页 > 最多显示页数的一半 else: # 显示最后的max_page_num条 if (self.current_page_num + self.max_page_num_half) > self.all_pager: page_start = self.all_pager - self.max_page_num_half + 1 page_end = self.all_pager + 1 # 显示中间的max_page_num条 else: page_start = self.current_page_num - self.max_page_num_half page_end = self.current_page_num + self.max_page_num_half + 1 # 上一页 首页 页码 尾页 下一页 page_html_list = [] # 当前页为第一页时不可点击,当前页大于第一页时点击时当前页码-1 if self.current_page_num <= 1: prev_page = '<li class="disabled paginate_button"><a href="#">上一页</a></li>' else: self.params['page'] = self.current_page_num - 1 prev_page = '<li class="paginate_button"><a href="?%s">上一页</a></li>' % (self.params.urlencode(),) page_html_list.append(prev_page) # 首页 first_page = '<li class="paginate_button"><a href="?page=%s">首页</a></li>' % (1,) page_html_list.append(first_page) # 页码 for i in range(page_start, page_end): self.params['page'] = i if i == self.current_page_num: temp = '<li class="active paginate_button"><a href="%s">%s</a></li>' % (self.params.urlencode(), i) else: temp = '<li class="paginate_button"><a href="?%s">%s</a></li>' % (self.params.urlencode(), i) page_html_list.append(temp) # 尾页 last_page = '<li class="paginate_button"><a href="?page=%s">尾页</a></li>' % (self.all_pager,) page_html_list.append(last_page) # 当前页为尾页时不可点击,当前页小于尾页时点击时当前页码+1 if self.current_page_num >= self.all_pager: next_page = '<li class="disabled paginate_button"><a href="#">下一页</a></li>' else: self.params['page'] = self.current_page_num + 1 next_page = '<li class="paginate_button"><a href="?%s">下一页</a></li>' % (self.params.urlencode(),) page_html_list.append(next_page) return ''.join(page_html_list)
视图函数中的使用
# 分页 注意分页应用了切片,但是一旦切片就不能再filter current_page_num = request.GET.get("page") pagination = Pagination( current_page_num, customer_list.count(), request, per_page_num=5) customer_list = customer_list[pagination.start:pagination.end]
模板中的使用
{{ pagination.page_html|safe }}
页码渲染
表单渲染时使用切片后的数据,即可实现分页
Cookie与Session
我们知道HTTP协议是无状态协议,也就是说每个请求都是独立的!无法记录前一次请求的状态。但HTTP协议中可以使用Cookie来完成会话跟踪!在Web开发中,使用session来完成会话跟踪,session底层依赖Cookie技术。
cookie
其实Cookie是key-value结构,类似于一个python中的字典。随着服务器端的响应发送给客户端浏览器。然后客户端浏览器会把Cookie保存起来,当下一次再访问服务器时把Cookie再发送给服务器。 Cookie是由服务器创建,然后通过响应发送给客户端的一个键值对。客户端会保存Cookie,并会标注出Cookie的来源(哪个服务器的Cookie)。当客户端向服务器发出请求时会把所有这个服务器Cookie包含在请求中发送给服务器,这样服务器就可以识别客户端了!
- Cookie大小上限为4KB;
- 一个服务器最多在客户端浏览器上保存20个Cookie;
- 一个浏览器最多保存300个Cookie;
cookie与HTTP头
Cookie是通过HTTP请求和响应头在客户端和服务器端传递的:
- Cookie:请求头,客户端发送给服务器端;
- 格式:Cookie: a=A; b=B; c=C。即多个Cookie用分号离开; Set-Cookie:响应头,服务器端发送给客户端;
- 一个Cookie对象一个Set-Cookie: Set-Cookie: a=A Set-Cookie: b=B Set-Cookie: c=C
cookie的覆盖
如果服务器端发送重复的Cookie那么会覆盖原有的Cookie,例如客户端的第一个请求服务器端发送的Cookie是:Set-Cookie: a=A;第二请求服务器端发送的是:Set-Cookie: a=AA,那么客户端只留下一个Cookie,即:a=AA。
django中的cookie语法
设置cookie:
rep = HttpResponse(...) 或 rep = render(request, ...) 或 rep = redirect() rep.set_cookie(key,value,...) rep.set_signed_cookie(key,value,salt='加密盐',...)
获取cookie:
request.COOKIES
删除cookie:
response.delete_cookie("cookie_key",path="/",domain=name)
session
Session是服务器端技术,利用这个技术,服务器在运行时可以 为每一个用户的浏览器创建一个其独享的session对象,由于 session为用户浏览器独享,所以用户在访问服务器的web资源时 ,可以把各自的数据放在各自的session中,当用户再去访问该服务器中的其它web资源时,其它web资源再从用户各自的session中 取出数据为用户服务。
django中的session语法
1、设置Sessions值 request.session['session_name'] ="admin" 2、获取Sessions值 session_name = request.session["session_name"] 3、删除Sessions值 del request.session["session_name"] 4、flush() 删除当前的会话数据并删除会话的Cookie。 这用于确保前面的会话数据不可以再次被用户的浏览器访问
5、get(key, default=None) fav_color = request.session.get('fav_color', 'red') 6、pop(key) fav_color = request.session.pop('fav_color') 7、keys() 8、items() 9、setdefault() 用户session的随机字符串 request.session.session_key # 将所有Session失效日期小于当前日期的数据删除 request.session.clear_expired() # 检查 用户session的随机字符串 在数据库中是否 request.session.exists("session_key") # 删除当前用户的所有Session数据 request.session.delete("session_key") request.session.set_expiry(value) * 如果value是个整数,session会在些秒数后失效。 * 如果value是个datatime或timedelta,session就会在这个时间后失效。 * 如果value是0,用户关闭浏览器session就会失效。 * 如果value是None,session会依赖全局session失效策略。 补充语法
session配置
Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。 a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认) SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
用户认证组件auth
auth模块
from django.contrib import auth
django.contrib.auth中提供了许多方法,这里主要介绍其中的三个:
authenticate()
提供了用户认证,即验证用户名以及密码是否正确,一般需要username password两个关键字参数
如果认证信息有效,会返回一个 User 对象。authenticate()会在User 对象上设置一个属性标识那种认证后端认证了该用户,且该信息在后面的登录过程中是需要的。当我们试图登陆一个从数据库中直接取出来不经过authenticate()的User对象会报错的!!
user = authenticate(username='someone',password='somepassword')
login(HttpRequest,user)
该函数接受一个HttpRequest对象,以及一个认证了的User对象 此函数使用django的session框架给某个已认证的用户附加上session id等信息。
from django.contrib.auth import authenticate, login def my_view(request): username = request.POST['username'] password = request.POST['password'] user = authenticate(username=username, password=password) if user is not None: login(request, user) # Redirect to a success page. ... else: # Return an 'invalid login' error message. ...
logout(request) 注销用户
from django.contrib.auth import logout def logout_view(request): logout(request) # Redirect to a success page.
该函数接受一个HttpRequest对象,无返回值。当调用该函数时,当前请求的session信息会全部清除。该用户即使没有登录,使用该函数也不会报错。
User对象
User 对象属性:username, password(必填项)password用哈希算法保存到数据库
user对象的is_authenticated()
如果是真正的user 对象,返回值恒为True.用于检查用户是否已经通过了验证.
通过认证并不意味着用户拥有任何权限,甚至也不检查该用户是否处于激活状态,这只是表明用户成功的通过了验证,这个方法很重要,在后台用request.
user.is_authenticated()判断用户是否已经登录,如果true则可以向前台展示request.user.name
要求:
1 用户登陆后才能访问某些页面,
2 如果用户没有登录就访问该页面的话直接跳到登录页面
3 用户在跳转的登陆界面中完成登陆后,自动访问跳转到之前访问的地址
方法1:
def my_view(request): if not request.user.is_authenticated(): return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
方法2:
django已经为我们设计好了一个用于此种情况的装饰器:login_requierd()
from django.contrib.auth.decorators import login_required @login_required def my_view(request): '''
若用户没有登录,则会跳转到django默认的 登录URL '/accounts/login/ ' (这个值可以在settings文件中通过LOGIN_URL进行修改)。并传递 当前访问url的绝对路径 (登陆成功后,会重定向到该路径)。
2.2创建用户
使用 create_user 辅助函数创建用户:
from django.contrib.auth.models import User user = User.objects.create_user(username='',password='',email='')
2.3 check_password(password)
用户需要修改密码的时候 首先要让他输入原来的密码 ,如果给定的字符串通过了密码检查,返回 True
2.4 修改密码
使用 set_password() 来修改密码
user = User.objects.get(username='') user.set_password(password='') user.save
def sign_up(request): state = None if request.method == 'POST': password = request.POST.get('password', '') repeat_password = request.POST.get('repeat_password', '') email=request.POST.get('email', '') username = request.POST.get('username', '') if User.objects.filter(username=username): state = 'user_exist' else: new_user = User.objects.create_user(username=username, password=password,email=email) new_user.save() return redirect('/book/') content = { 'state': state, 'user': None, } return render(request, 'sign_up.html', content) 注册代码
@login_required def set_password(request): user = request.user state = None if request.method == 'POST': old_password = request.POST.get('old_password', '') new_password = request.POST.get('new_password', '') repeat_password = request.POST.get('repeat_password', '') if user.check_password(old_password): if not new_password: state = 'empty' elif new_password != repeat_password: state = 'repeat_error' else: user.set_password(new_password) user.save() return redirect("/log_in/") else: state = 'password_error' content = { 'user': user, 'state': state, } return render(request, 'set_password.html', content) 修改密码的代码