本文转载自qimi博客,cnblog.liwenzhou.com
概要:
我们的cookie是保存在浏览器中的键值对
为什么要有cookie?
我们在访问浏览器的时候,千万个人访问同一个页面,我们只要拿着url地址就可以打开页面,但是因为我们的用户是不一样的,我们的权限也不一样,就类似于我们的管理权限,每个人有自己不同的
我们的浏览器在访问的时候会加上cookie,它就相当于一个标签,我们的每个人访问同一个网页的时候
会自带一些信息,让浏览器去识别用户的特定信息,从而把该用户的信息返回给浏览器页面,
就是在我们的浏览器一来一回的过程中实现的,这个cookie里面包含的就是我们的特殊信息,用以互相区分,cookie里面的信息是以键对值的方式保存的,我们的cookie里面那些键对值的方式保存下来的信息是有时效性的,自定义设定时间
cookie应用的地方:
登录
有效时间内可以免登录(我们的一些购物网站里面会有提示信息,几天之内是可以免登陆,都是在cookie里面实现的这些功能)
记住用户的某些浏览习惯
简单的请求限制(例如在投票系统中,我之前在微博上面给中国有嘻哈的选手投票的时候浏览器是会有限制的,不可以无限次的在一定时间内反复提交,系统提示一天只能投一次票,这种情况就是cookie里面可以设置的操作)
我们的cookie里面保存的信息在页面上是可以显示出来的,打开页面,按住键盘上行的f12键,就可以打开network,里面可以看到请求头,在里面可以查看到我们的cookie信息,这里就存在隐私信息泄露的风险,我们一般都是会有加盐操作,就跟我们学md5的时候,那个加盐是一样的概念,在我们的明文信息的基础上加一层密,同样,我们在后端加密之后需要解密,完成一来一回的操作,cookie里面的加盐也是salt
不加盐的cookie
rep.set_cookie("k1", "v1", max_age=10) # 这里是设置cookie
rep.cookie.get('k1',None) # 这里是获取cookie
* default:默认值
* salt:加盐
* max_age:后台控制过期时间
- expires=None, 超时时间(IE requires expires, so set it if hasn't been already.)
- path='/', Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问
- domain=None, Cookie生效的域名
- secure=False, https传输
- httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
我们在views视图函数里面设置cookie,
设置在响应对象这里,我们的响应对象就是我们给浏览器提交一个请求,然后浏览器做出响应,这个给我们回响应的函数就是我们的响应对象,说白了即是我们在url路由配置里面的地址,提交它的时候给我们回复响应的视图函数,它就是响应对象
它里面加上cookie设置(加盐)
def login(request): if request.method == 'POST': user = request.POST.get('user') pwd = request.POST.get('pwd') if user == 'alex' and pwd == 'who': rep = redirect('/index1/') # 这里把回复的响应赋值给一个变量 #然后我们下面方便对它进行操作 # rep.set_cookie('user',user) # import datetime # now=datetime.timedelta(seconds=19) # 这里下面就是我们的cookie设置,固定用法,关键字要写上,然后就是传参,参数也是有固定用法的, # rep.set_signed_cookie('user3',user, salt='s3',expires=now+d) rep.set_signed_cookie('user3', user, salt='s3', max_age=100, path='/index1/') return rep return render(request, 'cookie/login.html')
一端设置了cookie,另一端需要去接收它
获取cookie:
def index1(request): user = request.get_signed_cookie('user3', None, salt='s3')
# 这里是获取参数,我们的参数需要跟上面对应上, if not user: return redirect('/login/') return render(request, 'cookie/index1.html', {"username": user})
删除cookie:
rep.delete_cookie('k')
cookie版的登录校验:

def check_login(func): @wraps(func) def inner(request, *args,**kwargs): next_url=request.get_full_path() if request.get_signed_cookie('login',salt='sss',default=None)=='yes': # 已经登录的用户... return func(request,*args,**kwargs) else: # 没有登录的用户跳转到刚登陆的页面 return redirect('/login/?next={}'.format(next_url)) return inner def login(request): if request.method=='POST': username=request.POST.get("username") passwd=request.POST.get("password") if username=="XXX" and passwd=='okok': next_url=request.GET.get('next') if next_url and next_url !='/logout/': response=redirect(next_url) else: response=redirect('/class_list/') response.set_signed_cookie('login','yes',salt='SSS') return response return render(request,'login.html')
浏览器里面是有专门的设置选项,可以选择不保存cookie,但是我们设置了不保存cookie之后,
登录一些页面的时候就无法登录成功,会有系统提示cookie没有开启,需要开启之后才能登录上
我们的cookie本质是在url上面添加的键对值,它的主要用途就是做登录校验用,
我们的市面上主要的登录校验有两种方式:
1.加盐的cookie
数据都是保存在客户的浏览器上,服务端是没有什么压力的,
2.session
django中默认支持session,其内部提供了5种类型的session供开发者使用:
数据库(默认)
缓存
文件
缓存+数据库
加密cookie
数据库session
SESSION_ENGINE='django.contrib.sessions.backends.db' # 引擎(默认)
缓存session
SESSION_ENGINE='django.contrib.sessions.backends.cache'
SESSION_CACHE_ALLAS='default'
# 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
文件Session
SESSION_ENGINE='django.contrib.sessions.backends.file'
SESSION_FILE_PATH=None
# 缓存文件路径,如果为None,则使用tempfile模块获取了一个临时地址tempfile.gettempdir()
缓存+数据库
SESSION_ENNGINE='django.contrib.sessions.backends.cached-db'
加密cookie Session
SESSION_ENGINE='django.contrib.sessions.backends.signed_cookies'
其他的功用设置项:

SESSION_COOKIE_NAME='sessionid' # session的cookie保存在浏览器上是的key,即sessionid=随机字符串(默认) SESSION_COOKIE_PATH='/' # session的bookie保存路径(默认) SESSION_COOKIE_DAMAIN=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,默认修改之后才保存(默认)
不论你怎么设置session,使用方式都一样:
def index(request): # 获取,设置,删除session中数据 request.session['k1'] request.session.get('k1',None) request.session['k1']=123 request.session.setdefault('k1',123) # 存在则不设置 del request.session['k1'] # 所有键,值,键值对 request.sessin.keys() request.session.values() request.session.items() request.session.iterkeys() request.session.itervalues() request.session.iteritems() # 用户session的随机字符串 request.session.session_key #将所有session失效日期小雨当前日期的数据删除 request.session.clear_expired() #检查用户session的随机字符串在数据库中是否存在 request.session.exists('session_key') # 删除当前用户的所有sessison数据 request.session.delete() request.session.set_expiry(value) * 如果value是个整数,session会在些秒数后失效 *如果value是个datatime或timedelta.session就会在这个时间之后失效 *如果value是0用户关闭浏览器session就会失效 *如果value是None,session会依赖全局session失效策略
django 操作session的时候,都是request.xxx
session
是保存在服务端的'键对值'数据
session必须要依赖于cookie
我们的session里面的键对值数据是在我们创建django项目的时候,自动生成的django_session的数据库表格里面,它会系统自动保存进去,
表格里面的字段有session_key(键)
session_data(值)
expire_date(失效时间)这里一般是默认值14天之后就会自动清空,为了缓解数据库的压力,这些数据不会永久保存下去的,
我们使用session的时候,它内部需要做的几件事,:
1生成随机字符串
2回给浏览器,让它写到cookie
3自己保留一份,作为一个key,存到一个地方,key后面对应一个保存用户相关信息的键值对
我们这里补充一点session的知识点
我们要验证我们的浏览器是否带有cookie或session,如果有name就在我们的auth_session表格里面填入一条数据,如果没有那么就新增一条数据,这是浏览器级别的,
我们的一个浏览器里面就保存一组键值对,key是浏览器级别的cookie和session,那么value就是我们的用户信息,我们登录之后我们的用户级别的cookie和session就会以data的字典形式保存在auth_user里面,如果在同一个浏览器里面有两个用户登录了,那么,我们后登陆的用户信息就会覆盖前面的用户信息,然后替换掉前面的数据存入我们的键值对里面的值里面.
Session版登录验证
from functools import wraps # 这里是引入一个修复装饰器的模块或者是内置方法 def check_login(func): @wraps(func) def inner(request,*args,**kwargs): next_url=request.get_full_path() if request.session.get('user'): return func(request,*args,**kwargs) else: return redirect('/login/?next={}'.format(next_url)) return inner def login(request): if request.method=='POST': user=request.POST.get('user') pwd=request.POST.get('pwd') if user=='alex' and pwd='123': # 设置session request.session['uesr']=user # 获取调到登录页面之前的url (我们在浏览一个网页的时候有的内容是需要登录之后才可以查看的,那个时候我们可能正停留在F页面,然后我们登录完之后,需要系统自动捕获到我们当时停留的页面,然后我们遇到需要登录提示之后我们去进行登录,登录完之后系统要把捕获到的我们当时在登录之前停留的 页面提交给我们,然后我们就可以继续做我们的事.如果没有这个捕获当前停留页面的功能的话,我们登录完成之后就给我们返回到了A页,然后我们要继续作业的话就要从A页一直一直刷到F页才能继续我们接下来的作业,那样的效率就太低下了,所以这个功能是很必要的) next_url=request.GET.get('next') if next_rul: return redirect(next_url) else: return redirect('/index/') return render(request.'login.html') @check_login def logout(request): #删除所有当前请求相关的session request.session.delete() return redirect('/login/') @ched_login def index(request): current_user=request.session.get('user',None) return render(request,'index.html',{'user':current_user})
写一个登陆校验的中间件,任何数据访问都需要基于已登陆状态,在session中放入一个字符串,每一次数据访问都检查一下该字符串,否则就无法访问,
那么,如果写入到中间件中,要写到中间件里面的response方法中,这里就需要了解中间件的整个生命周期,那么就所有数据访问都会做一层判断,
是否携带这个字符串,只有登陆过才会携带它,所以不论要携带的是这个字符串,还是是携带已经登陆的用户ID,或者是已经登陆用户的用户名,并无大碍
最后,不论是写成中间件,还是装饰器函数,或者是装饰器类,都是一样的逻辑
CBV实现的登录视图
class LoginView(View): def get(self,request): """处理get请求""" return render(request,'login.html') def post(self,request): """处理post请求""" user=request.POST.get('user') pwd=request.POST.get('pwd') if uesr=='alex' and pwd=='123': next_url=request.GET.get('next') # 生成随机字符串 #写浏览器cookie>session_id:随机字符串 #写到服务端session: #{ #'随机字符串':{'user':'alex'} #} request.session['user']=user if next_url: return redirect(next_url) else: return redirect('/index/') return render(request,'login.html')
在CBV视图中使用我们的上面的check_login装饰器,有以下三种方式:
from django.utils.decorators import method_decorator
1加在CBV视图的get或post方法上
from django.utils.decorators import method_decorator class HomeView(View): def dispatch(self,request,*args,**kwargs): return super(HoneVies,self).dispatch(request,*args,**kwargs) def get(self,request): return render(request,'home.html') @method_decorator(check_login) def post(self,request): print('home view post method...') return redirect('/index/')
加在dispatch方法上
from django.utils.decorators import method_decorator class HomeView(View): @method_decorator(check_login) def dispatch(self,request,*args,**kwargs): return super(HomeView,self).dispatch(request,*args,**kwargs) def get(self,request): return render(request,'home.html') def post(self,request): print('home view post method...') return redirect('/index/')
如果get方法和post方法都需要登录校验就写两个装饰器
from django.utils.decorators import method_decorator @method_decorator(check_login,name='get') @method_decorator(check_login,name='post') class HomeView(View): def dispatch(self,request,*args,**kwargs): return super(HomeView,self).dispatch(request,*args,**kwargs) def get(self,request): return render(request,'home.html') def post(self,request): print('home view post method...') return redirect('/index/')
补充:
CSRF Token 相关装饰器在CBV只能加到dispatch方法上
备注:csrf_tprotect,为当前函数强制设置防跨站请求伪造功能,即便setting中没有设置全局中间件
csrf_exempt,取消当前函数防跨站请求伪造功能,即便setting中设置了全局中间件
from django,views.decorators.csrf import csrf_exempt, csrf_protect class HomeView(View): @method_decorator(csrf_exempt) def dispatch(self,request,*args,**kwargs): return super(HomeView,self).dispatch(request,*args,**kwargs) def get(self,request): return render(request,'home.html') def post(self,request): print('home view post method...') return redirect('/index/')
我们的分页显示效果:
按照我们的django框架的步骤,先从我们的url配置里面开始,
url配置:
url(r'^list/$',views.user_list,),
然后跳转到views里面的视图函数:
# 这里我们是伪造的一个数据组,模拟数据库里面的数据取值,
data=[]
for i in range(i,303):
tmp={'id':i,'name':'alex-{}'.format(i)}
data.append(tmp)
def user_list(request): page_num=request.GET.get('page') path=request.path_info from .tool import MyPage page_html=page.page_html() return render(request,'user_list.html',{'user_list':data[page.start:page.end],'page_html':page_html})
引入的类的文件:
简单版:views

1 # 分页封装方法 2 class MyPage(object): 3 4 def __init__(self, page_num, total_count, base_url, per_page_num=3, max_show=5): 5 """ 6 :param page_num: 当前页面 7 :param total_count: 数据总个数 8 :param base_url: 分页页码跳转的url 9 :param per_page_num: 每一页显示多少条数据 10 :param max_show: 页面上最多显示多少页码 11 """ 12 # 实例化时传进来的参数 13 # 我们在这里捕捉一下异常,把传进去的参数改成数字类型,否则就返回第一页 14 try: 15 page_num = int(page_num) 16 except Exception as e: 17 page_num = 1 18 if page_num < 1: # 如果我们的当前页码出现负数的时候,这里就直接返回第一页,避免出现负数的情况 19 page_num = 1 20 self.page_num = page_num 21 self.total_count = total_count 22 self.base_url = base_url 23 self.per_page_num = per_page_num 24 self.max_show = max_show 25 self.half_show = int((self.max_show-1)/2) 26 total_page_num, more = divmod(total_count, per_page_num) 27 """ 28 我们使用总数据的个数对每页显示的数据个数取余,得到的商是页码数,如果有余数就在商的页码数上加一 29 """ 30 if more: 31 total_page_num += 1 32 self.total_page_num = total_page_num 33 34 # 首页 35 @property 36 def start(self): 37 return (self.page_num-1)*self.per_page_num 38 39 # 尾页 40 @property 41 def end(self): 42 return self.page_num*self.per_page_num 43 44 def page_html(self): 45 """ 46 返回页面上可以用的一段HTML 47 一段可用的分页页码的HTML 48 :return: 49 """ 50 # 如果总页码数<=最大显示页码数,那么起始页码数分别是什么 51 if self.total_page_num <= self.max_show: 52 page_start = 1 53 page_end = self.total_page_num 54 else: # 如果当前页<=最多显示的页码数的一半,那么起始页码分别是什么 55 """ 56 假设,我们最多显示7个分页,那么第一页就是1,最后一页就是7,但是我们的当前页是2, 57 它往前倒推7/2商3余1,2-3得到当前页的首页-1,就是负数了,所以这个时候需要做限制,让首页等于1, 58 """ 59 if self.page_num <= self.half_show: 60 page_start = 1 61 page_end = self.max_show 62 else: 63 # 如果当前页>=总页码数-最多显示的页码数的一半,那么起始页码分别是什么 64 """ 65 如果我们在第6页,我们的页面最多显示7个页码,而我们的总数据就只能显示8页的话, 66 倒推的6+7/2商3余1,就是(6+3)>8,我们的第9页就是空白页,这个时候就需要加以限制, 67 此时第一页就是8-6+1,也就是从第二页开始,一直到第八页,就刚好是7页,这样就完美了 68 此时最后一页就是数据最多所展示的页码第8页 69 """ 70 if self.page_num >= self.total_page_num - self.half_show: 71 page_start = self.total_page_num - self.max_show + 1 72 page_end = self.total_page_num 73 else: # 最后到这里我们判断了数据所在的页码出现在最前面把负数页码排除了,也判断了数据所在的页码出现最后面把空白页码排除了, 74 # 也判断了总页码数还不够我们所设置的最大页码显示,就剩下最后一种情况了,那就是当前页不在最后也不在最前, 75 # 直接用当前页加上或减去最大显示页的1/2,就得到了起始页面 76 page_start = self.page_num - self.half_show 77 page_end = self.page_num + self.half_show 78 79 # 生成前页码的HTML 80 page_html_list = [] 81 page_first_tmp = '<li><a href="{}?page=1">首页</a><li>'.format(self.base_url,) 82 page_html_list.append(page_first_tmp) 83 84 # 生成上一页 85 if self.page_num <= 1: 86 page_prev_tmp = '<li class=disabled><a href="#">上一页</a></li>' 87 else: 88 page_prev_tmp = '<li><a href="{}?{}">上一页</a></li>'.format(self.base_url, self.page_num-1,) 89 90 page_html_list.append(page_prev_tmp) 91 92 # 生成页码中间页数前半截 93 for i in range(page_start, page_end+1): 94 if i == self.page_num: 95 tmp = '<li class="active"><a href="{0}?{1}">{1}</a></li>'.format(self.base_url, i) 96 else: 97 tmp = '<li><a href="{0}?page={1}">{1}</a></li>'.format(self.base_url, i,) 98 99 page_html_list.append(tmp) 100 101 # 生成页码中间页数后半截 102 if self.page_num + 1 > self.total_page_num: 103 page_next_tmp = '<li class="disabled"><a href="#">下一页</a></li>' 104 else: 105 page_next_tmp = '<li><a href="{0}?page={1}">下一页</a></li>'.format(self.base_url, self.page_num+1,) 106 107 page_html_list.append(page_next_tmp) 108 109 # 生成最后一页 110 page_last_tmp = '<li><a href="{0}?page={1}">尾页</a></li>'.format(self.base_url, self.total_page_num,) 111 page_html_list.append(page_last_tmp) 112 113 return "".join(page_html_list)
我们的''{}_{}''.format(a,b) 使用方法上两种写法都可以:
这里没有page=这个键 tmp = '<li class="active"><a href="{0}?{1}">{1}</a></li>'.format(self.base_url, i) 这里有page=这个键 tmp = '<li><a href="{0}?page={1}">{1}</a></li>'.format(self.base_url, i,)
还有一个带url键值对的升级版:

1 # 分页封装方法 2 class MyPage(object): 3 4 def __init__(self, page_num, total_count, base_url, params, per_page_num=3, max_show=5): 5 """ 6 :param params: 当前页码所携带的url里面的键值对参数 7 :param page_num: 当前页面 8 :param total_count: 数据总个数 9 :param base_url: 分页页码跳转的url 10 :param per_page_num: 每一页显示多少条数据 11 :param max_show: 页面上最多显示多少页码 12 """ 13 # 实例化时传进来的参数 14 # 我们在这里捕捉一下异常,把传进去的参数改成数字类型,否则就返回第一页 15 try: 16 page_num = int(page_num) 17 except Exception as e: 18 page_num = 1 19 if page_num < 1: # 如果我们的当前页码出现负数的时候,这里就直接返回第一页,避免出现负数的情况 20 page_num = 1 21 self.params = params 22 self.page_num = page_num 23 self.total_count = total_count 24 self.base_url = base_url 25 self.per_page_num = per_page_num 26 self.max_show = max_show 27 self.half_show = int((self.max_show-1)/2) 28 total_page_num, more = divmod(total_count, per_page_num) 29 """ 30 我们使用总数据的个数对每页显示的数据个数取余,得到的商是页码数,如果有余数就在商的页码数上加一 31 """ 32 if more: 33 total_page_num += 1 34 self.total_page_num = total_page_num 35 36 import copy 37 params = copy.deepcopy(params) # 这里的QueryDict里面是有内置方法,它存储的是一个字典, 38 # 我们的url里面的参数都在这里面,我们要用它就需要对其进行赋值操作,然后它有一个参数是_mutable默认值False, 39 # 不能修改也不能被赋值,我们需要把params给copy一份,使用deepcopy,然后deepcopy的基础上进行赋值 40 params._mutable = True 41 self.params = params # self.params: {'page':23, 'title': python, 'nid': 3} 42 # 这里就是我们的url里面携带的键值对,都封装到params里面了 43 44 # 首页 45 @property 46 def start(self): 47 return (self.page_num-1)*self.per_page_num 48 49 # 尾页 50 @property 51 def end(self): 52 return self.page_num*self.per_page_num 53 54 def page_html(self): 55 """ 56 返回页面上可以用的一段HTML 57 一段可用的分页页码的HTML 58 :return: 59 """ 60 # 如果总页码数<=最大显示页码数,那么起始页码数分别是什么 61 if self.total_page_num <= self.max_show: 62 page_start = 1 63 page_end = self.total_page_num 64 else: # 如果当前页<=最多显示的页码数的一半,那么起始页码分别是什么 65 """ 66 假设,我们最多显示7个分页,那么第一页就是1,最后一页就是7,但是我们的当前页是2, 67 它往前倒推7/2商3余1,2-3得到当前页的首页-1,就是负数了,所以这个时候需要做限制,让首页等于1, 68 """ 69 if self.page_num <= self.half_show: 70 page_start = 1 71 page_end = self.max_show 72 else: 73 # 如果当前页>=总页码数-最多显示的页码数的一半,那么起始页码分别是什么 74 """ 75 如果我们在第6页,我们的页面最多显示7个页码,而我们的总数据就只能显示8页的话, 76 倒推的6+7/2商3余1,就是(6+3)>8,我们的第9页就是空白页,这个时候就需要加以限制, 77 此时第一页就是8-6+1,也就是从第二页开始,一直到第八页,就刚好是7页,这样就完美了 78 此时最后一页就是数据最多所展示的页码第8页 79 """ 80 if self.page_num >= self.total_page_num - self.half_show: 81 page_start = self.total_page_num - self.max_show + 1 82 page_end = self.total_page_num 83 else: # 最后到这里我们判断了数据所在的页码出现在最前面把负数页码排除了,也判断了数据所在的页码出现最后面把空白页码排除了, 84 # 也判断了总页码数还不够我们所设置的最大页码显示,就剩下最后一种情况了,那就是当前页不在最后也不在最前, 85 # 直接用当前页加上或减去最大显示页的1/2,就得到了起始页面 86 page_start = self.page_num - self.half_show 87 page_end = self.page_num + self.half_show 88 89 # 生成前页码的HTML 90 page_html_list = [] 91 92 # 生成第一页 93 self.params['page'] = 1 94 page_first_tmp = '<li><a href="{}?{}">首页</a><li>'.format(self.base_url, self.params.urlencode(),) 95 page_html_list.append(page_first_tmp) 96 97 # 生成上一页 98 if self.page_num <= 1: 99 page_prev_tmp = '<li class=disabled><a href="#">上一页</a></li>' 100 else: 101 self.params['page'] = self.page_num-1 102 page_prev_tmp = '<li><a href="{}?{}">上一页</a></li>'.format(self.base_url, self.params.urlencode(),) 103 104 page_html_list.append(page_prev_tmp) 105 106 # 生成页码中间页数前半截 107 for i in range(page_start, page_end+1): 108 self.params['page'] = i 109 if i == self.page_num: 110 tmp = '<li class="active"><a href="{}?{}">{}</a></li>'.format(self.base_url, self.params.urlencode(), i) 111 else: 112 tmp = '<li><a href="{}?{}">{}</a></li>'.format(self.base_url, self.params.urlencode(), i,) 113 114 page_html_list.append(tmp) 115 116 # 生成页码中间页数后半截 117 if self.page_num + 1 > self.total_page_num: 118 page_next_tmp = '<li class="disabled"><a href="#">下一页</a></li>' 119 else: 120 self.params['page'] = self.page_num+1 121 page_next_tmp = '<li><a href="{}?{}">下一页</a></li>'.format(self.base_url, self.params.urlencode(),) 122 123 page_html_list.append(page_next_tmp) 124 125 # 生成最后一页 126 self.params['page'] = self.total_page_num 127 page_last_tmp = '<li><a href="{}?{}">尾页</a></li>'.format(self.base_url, self.params.urlencode(),) 128 page_html_list.append(page_last_tmp) 129 130 return "".join(page_html_list)
html页面:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>用户列表</title> <link rel="stylesheet" href="/static/plugins/bootstrap-3.3.7/css/bootstrap.min.css"> </head> <body> {#我们使用bootstrap的时候需要用div标签class里面设置一个container属性去进行装饰它#} <div class="container"> <table class="table table-bordered"> <thead> <tr> <th>ID</th> <th>姓名</th> </tr> </thead> <tbody> {% for user in user_list %} {# 为什么我们这里取不到值呢#} <tr> <td>{{ user.id }}</td> <td>{{ user.name }}</td> </tr> {% endfor %} </tbody> </table> <div class="pull-right"> <!--分页开始--> <nav aria-label="Page navigation"> <ul class="pagination"> {# {% for i in total_page_num %}#} <li><a href="{{ base_url }}?page={{ i }}"></a></li> {# {% endfor %}#} {{ page_html|safe }} {# 这里我们使用了|safe参数,是把这里作为一个字符串跟我们的后端逻辑代码去关联,然后我们把后端的代码转成字符串传过来我们的前端就可以显示出来了#} </ul> </nav> <!--分页结束--> </div> </div> </body> </html>
这里需要强调的一点是:
def index(request): username = request.user article_list = Article.objects.all() page_num = request.GET.get('page') path = request.path_info from .tool import MyPage page = MyPage(page_num, len(article_list), path) page_html = page.page_html() return render(request, 'step1/index.html', {'list': article_list[page.start:page.end], 'page_html': page_html, 'username': username}) 这里的返回值里面,要严格按照上面的格式,上面的参数一个都不能少,len()括号里面的值就是我们的返回值里面的其中一个键值对的value,这个键值对就是 ''list'':article_list[page.start:page.end],这一句,必须要写上[page.start:page.end]否则的话,我们的页面上的数据是不会在浏览器上切分的,也就是说我们的article_list把所有的文章数据取出来了,就渲染出所有的数据,
在django的配置文件setting中,有两个配置参数是时间与时区有关的,分别是TIME_ZONE和USE_TZ
如果是USE_TZ设置为True时,django会使用系统默认设置的时区,即America/Chicago,此时的TIME_ZONE不管有没有设置都不起作用.
如果USE_TZ设置为False,而TIME_ZONE设置为None,则django还是会使用默认的America/Chicago时间,
若TIME_ZONE设置为其他时区的话,则还要分情况,如果是windows系统,则TIME_ZONE设置是没有用的,django会使用本机的时间,如果为其他系统,则使用该时区的时间,如设置USE_TZ=False,TIME_ZONE='Asia/Shanghai'',则使用上海的UTC时间