本文转载自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='':
# 设置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=='':
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
# 分页封装方法
class MyPage(object): def __init__(self, page_num, total_count, base_url, per_page_num=3, max_show=5):
"""
:param page_num: 当前页面
:param total_count: 数据总个数
:param base_url: 分页页码跳转的url
:param per_page_num: 每一页显示多少条数据
:param max_show: 页面上最多显示多少页码
"""
# 实例化时传进来的参数
# 我们在这里捕捉一下异常,把传进去的参数改成数字类型,否则就返回第一页
try:
page_num = int(page_num)
except Exception as e:
page_num = 1
if page_num < 1: # 如果我们的当前页码出现负数的时候,这里就直接返回第一页,避免出现负数的情况
page_num = 1
self.page_num = page_num
self.total_count = total_count
self.base_url = base_url
self.per_page_num = per_page_num
self.max_show = max_show
self.half_show = int((self.max_show-1)/2)
total_page_num, more = divmod(total_count, per_page_num)
"""
我们使用总数据的个数对每页显示的数据个数取余,得到的商是页码数,如果有余数就在商的页码数上加一
"""
if more:
total_page_num += 1
self.total_page_num = total_page_num # 首页
@property
def start(self):
return (self.page_num-1)*self.per_page_num # 尾页
@property
def end(self):
return self.page_num*self.per_page_num def page_html(self):
"""
返回页面上可以用的一段HTML
一段可用的分页页码的HTML
:return:
"""
# 如果总页码数<=最大显示页码数,那么起始页码数分别是什么
if self.total_page_num <= self.max_show:
page_start = 1
page_end = self.total_page_num
else: # 如果当前页<=最多显示的页码数的一半,那么起始页码分别是什么
"""
假设,我们最多显示7个分页,那么第一页就是1,最后一页就是7,但是我们的当前页是2,
它往前倒推7/2商3余1,2-3得到当前页的首页-1,就是负数了,所以这个时候需要做限制,让首页等于1,
"""
if self.page_num <= self.half_show:
page_start = 1
page_end = self.max_show
else:
# 如果当前页>=总页码数-最多显示的页码数的一半,那么起始页码分别是什么
"""
如果我们在第6页,我们的页面最多显示7个页码,而我们的总数据就只能显示8页的话,
倒推的6+7/2商3余1,就是(6+3)>8,我们的第9页就是空白页,这个时候就需要加以限制,
此时第一页就是8-6+1,也就是从第二页开始,一直到第八页,就刚好是7页,这样就完美了
此时最后一页就是数据最多所展示的页码第8页
"""
if self.page_num >= self.total_page_num - self.half_show:
page_start = self.total_page_num - self.max_show + 1
page_end = self.total_page_num
else: # 最后到这里我们判断了数据所在的页码出现在最前面把负数页码排除了,也判断了数据所在的页码出现最后面把空白页码排除了,
# 也判断了总页码数还不够我们所设置的最大页码显示,就剩下最后一种情况了,那就是当前页不在最后也不在最前,
# 直接用当前页加上或减去最大显示页的1/2,就得到了起始页面
page_start = self.page_num - self.half_show
page_end = self.page_num + self.half_show # 生成前页码的HTML
page_html_list = []
page_first_tmp = '<li><a href="{}?page=1">首页</a><li>'.format(self.base_url,)
page_html_list.append(page_first_tmp) # 生成上一页
if self.page_num <= 1:
page_prev_tmp = '<li class=disabled><a href="#">上一页</a></li>'
else:
page_prev_tmp = '<li><a href="{}?{}">上一页</a></li>'.format(self.base_url, self.page_num-1,) page_html_list.append(page_prev_tmp) # 生成页码中间页数前半截
for i in range(page_start, page_end+1):
if i == self.page_num:
tmp = '<li class="active"><a href="{0}?{1}">{1}</a></li>'.format(self.base_url, i)
else:
tmp = '<li><a href="{0}?page={1}">{1}</a></li>'.format(self.base_url, i,) page_html_list.append(tmp) # 生成页码中间页数后半截
if self.page_num + 1 > self.total_page_num:
page_next_tmp = '<li class="disabled"><a href="#">下一页</a></li>'
else:
page_next_tmp = '<li><a href="{0}?page={1}">下一页</a></li>'.format(self.base_url, self.page_num+1,) page_html_list.append(page_next_tmp) # 生成最后一页
page_last_tmp = '<li><a href="{0}?page={1}">尾页</a></li>'.format(self.base_url, self.total_page_num,)
page_html_list.append(page_last_tmp) 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键值对的升级版:
# 分页封装方法
class MyPage(object): def __init__(self, page_num, total_count, base_url, params, per_page_num=3, max_show=5):
"""
:param params: 当前页码所携带的url里面的键值对参数
:param page_num: 当前页面
:param total_count: 数据总个数
:param base_url: 分页页码跳转的url
:param per_page_num: 每一页显示多少条数据
:param max_show: 页面上最多显示多少页码
"""
# 实例化时传进来的参数
# 我们在这里捕捉一下异常,把传进去的参数改成数字类型,否则就返回第一页
try:
page_num = int(page_num)
except Exception as e:
page_num = 1
if page_num < 1: # 如果我们的当前页码出现负数的时候,这里就直接返回第一页,避免出现负数的情况
page_num = 1
self.params = params
self.page_num = page_num
self.total_count = total_count
self.base_url = base_url
self.per_page_num = per_page_num
self.max_show = max_show
self.half_show = int((self.max_show-1)/2)
total_page_num, more = divmod(total_count, per_page_num)
"""
我们使用总数据的个数对每页显示的数据个数取余,得到的商是页码数,如果有余数就在商的页码数上加一
"""
if more:
total_page_num += 1
self.total_page_num = total_page_num import copy
params = copy.deepcopy(params) # 这里的QueryDict里面是有内置方法,它存储的是一个字典,
# 我们的url里面的参数都在这里面,我们要用它就需要对其进行赋值操作,然后它有一个参数是_mutable默认值False,
# 不能修改也不能被赋值,我们需要把params给copy一份,使用deepcopy,然后deepcopy的基础上进行赋值
params._mutable = True
self.params = params # self.params: {'page':23, 'title': python, 'nid': 3}
# 这里就是我们的url里面携带的键值对,都封装到params里面了 # 首页
@property
def start(self):
return (self.page_num-1)*self.per_page_num # 尾页
@property
def end(self):
return self.page_num*self.per_page_num def page_html(self):
"""
返回页面上可以用的一段HTML
一段可用的分页页码的HTML
:return:
"""
# 如果总页码数<=最大显示页码数,那么起始页码数分别是什么
if self.total_page_num <= self.max_show:
page_start = 1
page_end = self.total_page_num
else: # 如果当前页<=最多显示的页码数的一半,那么起始页码分别是什么
"""
假设,我们最多显示7个分页,那么第一页就是1,最后一页就是7,但是我们的当前页是2,
它往前倒推7/2商3余1,2-3得到当前页的首页-1,就是负数了,所以这个时候需要做限制,让首页等于1,
"""
if self.page_num <= self.half_show:
page_start = 1
page_end = self.max_show
else:
# 如果当前页>=总页码数-最多显示的页码数的一半,那么起始页码分别是什么
"""
如果我们在第6页,我们的页面最多显示7个页码,而我们的总数据就只能显示8页的话,
倒推的6+7/2商3余1,就是(6+3)>8,我们的第9页就是空白页,这个时候就需要加以限制,
此时第一页就是8-6+1,也就是从第二页开始,一直到第八页,就刚好是7页,这样就完美了
此时最后一页就是数据最多所展示的页码第8页
"""
if self.page_num >= self.total_page_num - self.half_show:
page_start = self.total_page_num - self.max_show + 1
page_end = self.total_page_num
else: # 最后到这里我们判断了数据所在的页码出现在最前面把负数页码排除了,也判断了数据所在的页码出现最后面把空白页码排除了,
# 也判断了总页码数还不够我们所设置的最大页码显示,就剩下最后一种情况了,那就是当前页不在最后也不在最前,
# 直接用当前页加上或减去最大显示页的1/2,就得到了起始页面
page_start = self.page_num - self.half_show
page_end = self.page_num + self.half_show # 生成前页码的HTML
page_html_list = [] # 生成第一页
self.params['page'] = 1
page_first_tmp = '<li><a href="{}?{}">首页</a><li>'.format(self.base_url, self.params.urlencode(),)
page_html_list.append(page_first_tmp) # 生成上一页
if self.page_num <= 1:
page_prev_tmp = '<li class=disabled><a href="#">上一页</a></li>'
else:
self.params['page'] = self.page_num-1
page_prev_tmp = '<li><a href="{}?{}">上一页</a></li>'.format(self.base_url, self.params.urlencode(),) page_html_list.append(page_prev_tmp) # 生成页码中间页数前半截
for i in range(page_start, page_end+1):
self.params['page'] = i
if i == self.page_num:
tmp = '<li class="active"><a href="{}?{}">{}</a></li>'.format(self.base_url, self.params.urlencode(), i)
else:
tmp = '<li><a href="{}?{}">{}</a></li>'.format(self.base_url, self.params.urlencode(), i,) page_html_list.append(tmp) # 生成页码中间页数后半截
if self.page_num + 1 > self.total_page_num:
page_next_tmp = '<li class="disabled"><a href="#">下一页</a></li>'
else:
self.params['page'] = self.page_num+1
page_next_tmp = '<li><a href="{}?{}">下一页</a></li>'.format(self.base_url, self.params.urlencode(),) page_html_list.append(page_next_tmp) # 生成最后一页
self.params['page'] = self.total_page_num
page_last_tmp = '<li><a href="{}?{}">尾页</a></li>'.format(self.base_url, self.params.urlencode(),)
page_html_list.append(page_last_tmp) 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时间
day70 cookie & session 前后端交互分页显示的更多相关文章
-
前后端交互实现(nginx,json,以及datatable的问题相关)
1.同源问题解决 首先,在同一个域下搭建网络域名访问,需要nginx软件,下载之后修改部分配置 然后再终端下cmd nginx.exe命令,或者打开nginx.exe文件,会运行nginx一闪而过, ...
-
Python 利用三个简易模块熟悉前后端交互流程
准备工作 在学习Django之前,先动手撸一个简单的WEB框架来熟悉一下前后端交互的整体流程 本次用到的模块: 1.wsgiref,这是一个Python自带的模块,用于构建路由与视图 2.pymysq ...
-
springboot+mybatis+thymeleaf项目搭建及前后端交互
前言 spring boot简化了spring的开发, 开发人员在开发过程中省去了大量的配置, 方便开发人员后期维护. 使用spring boot可以快速的开发出restful风格微服务架构. 本文将 ...
-
百度ueditor的图片上传,前后端交互使用
百度ueditor的使用 一个文本编辑器,看了网上很多文档写的很乱,这里拾人牙慧,整理下怎么使用. 这个东西如果不涉及到图片附件上传,其实很简单,就是几个前端文件,直接引用,然后配置下ueditor. ...
-
【开源.NET】 轻量级内容管理框架Grissom.CMS(第二篇前后端交互数据结构分析)
这是 CMS 框架系列文章的第二篇,第一篇开源了该框架的代码和简要介绍了框架的目的.作用和思想,这篇主要解析如何把sql 转成标准 xml 配置文件和把前端post的增删改数据规范成方便后台解析的结构 ...
-
thinkphp+jquery+ajax前后端交互注册验证
thinkphp+jquery+ajax前后端交互注册验证,界面如下 register.html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1. ...
-
web前后端交互,nodejs
手机赚钱怎么赚,给大家推荐一个手机赚钱APP汇总平台:手指乐(http://www.szhile.com/),辛苦搬砖之余用闲余时间动动手指,就可以日赚数百元 web前后端交互 前后端交互可以采用混合 ...
-
三、vue前后端交互(轻松入门vue)
轻松入门vue系列 Vue前后端交互 六.Vue前后端交互 1. 前后端交互模式 2. Promise的相关概念和用法 Promise基本用法 then参数中的函数返回值 基于Promise处理多个A ...
-
Node之简单的前后端交互
node是前端必学的一门技能,我们都知道node是用的js做后端,在学习node之前我们有必要明白node是如何实现前后端交互的. 这里写了一个简单的通过原生ajax与node实现的一个交互,刚刚学n ...
随机推荐
-
3 3Sum closest_Leetcode
Given an array S of n integers, find three integers in S such that the sum is closest to a given num ...
-
synchronized关键字,Lock接口以及可重入锁ReentrantLock
多线程环境下,必须考虑线程同步的问题,这是因为多个线程同时访问变量或者资源时会有线程争用,比如A线程读取了一个变量,B线程也读取了这个变量,然后他们同时对这个变量做了修改,写回到内存中,由于是同时做修 ...
-
由单例模式学到:Lazy<;T>;
http://www.cnblogs.com/zhangpengshou/archive/2012/12/10/2811765.html http://www.cnblogs.com/anytao/a ...
-
ecshop 点购物车弹出提示框
1.找到common.js在最下面输入以下代码 * 点击购物后弹出提示层 * Chen 2010.7.28 * 参数 cartinfo:购物车信息 */function openDiv_chen(ca ...
-
78. Subsets
题目: Given a set of distinct integers, S, return all possible subsets. Note: Elements in a subset mus ...
-
opengl 杂记
函数原型: void glClear(GLbitfield mask); 参数说明: GLbitfield:可以使用 | 运算符组合不同的缓冲标志位,表明需要清除的缓冲,例如glClear(GL_CO ...
-
Excel里面将头尾第一个字母保留,去除中间的用*号代替,主要是REPT函数的应用,一开始我还以为要自己写个自定义函数
Excel里面将头尾第一个字母保留,去除中间的用*号代替,主要是REPT函数的应用,一开始我还以为要自己写个自定义函数 =LEFT(A1,1)&REPT("*",(LEN( ...
-
Java代码质量改进之:同步对象的选择
在Java中,让线程同步的一种方式是使用synchronized关键字,它可以被用来修饰一段代码块,如下: synchronized(被锁的同步对象) { // 代码块:业务代码 } 当synchro ...
-
MT【12】三点坐标求面积
$L_1,L_2$是O发出的两条射线,C是一个常数,一条动直线$l$分别与$L_1,L_2$交于A,B两点.$S_{\Delta ABC}=C$,求A,B的中点D的轨迹方程.(2012北大自主招生) ...
-
Java:单例模式的七种写法<;转>;
第一种(懒汉,线程不安全): 1 public class Singleton { 2 private static Singleton instance; 3 privat ...