有时候我们会听到会话一词,它不会受前面的请

作者: 编程  发布:2019-11-29

 Cookie和Session

Django中操作Cookie

 

session初步介绍

session:中文经常翻译为‘会话’,其本来的含义是指有始有终的一系列动作,

就比如我们打电话一直到挂断电话这这中间的一系列过程可以称作为一个session,

有时候我们会听到会话一词,这里的会话就指的是其本意,也就是一个浏览器打开

到关闭的一整个期间。

当session与网络协议相关联时,就会涉及到面向连接和、或保持状态,

“面向连接”指的是在通信之前要先建立一个通信的渠道,比如我们拿起电话开始给多方

拨电话,直到对方接了以后通信才开始,但是写信对发件人来说发出去的时候就应经开始

通信,,但是并不能确定收信人的地址是否绝对正确,“保持状态”指的是通信的一放能够把

消息关联起来,使得消息之间能互相依赖

Cookie

获取Cookie

request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)

参数:

  • default: 默认值
  • salt: 加密盐
  • max_age: 后台控制过期时间

本文转载自qimi博客,cnblog.liwenzhou.com

理解session机制

session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)

来保存信息。

当程式需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识

  • 称为session id,如果已包含一个session id则说明以前已为此客户端创建过session,服务器就按照session id把这个session检索出来使用(如果检索不到,可能会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个和此session相关联的session id,session id的值应该是一个不可能重复的唯一值,并且保存在一个类似于Map的集合中,session id将被在本次响应中返回给客户端保存。

  保存这个session id的方式能采用cookie,这样在交互过程中浏览器能自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于SEEESIONID,而。比如weblogic对于web应用程式生成的cookie,JSESSIONID=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,他的名字就是JSESSIONID。

  由于cookie能被人为的禁止,必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。*经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面,附加方式也有两种,一种是作为URL路径的附加信息,表现形式为http://…../xxx;jsessionid=ByOK … 99zWpBng!-145788764另一种是作为查询字符串附加在URL后面,表现形式为http://…../xxx?jsessionid=ByOK … 99zWpBng!-145788764 
这两种方式对于用户来说是没有差别的,只是服务器在解析的时候处理的方式不同,采用第一种方式也有利于把session id的信息和正常程式参数区分开来*。

Cookie的由来

大家都知道HTTP协议是无状态的。

无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应情况直接影响,也不会直接影响后面的请求响应情况。

一句有意思的话来描述就是人生只如初见,对服务器来说,每次的请求都是全新的。

状态可以理解为客户端和服务器在某次会话中产生的数据,那无状态的就以为这些数据不会被保留。会话中产生的数据又是我们需要保存的,也就是说要“保持状态”。因此Cookie就是在这样一个场景下诞生。

设置Cookie

rep = HttpResponse(...)
rep = render(request, ...)

rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密盐',...)

参数:

  • key, 键
  • value='', 值
  • max_age=None, 超时时间
  • 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获取(不是绝对,底层抓包可以获取到也可以被覆盖)

概要:

5种session类型

Django中默认支持session,其内部提供了5种类型的session供开发者使用

数据库(默认)

缓存

文件

缓存+数据库

加密cookie

什么是Cookie

Cookie具体指的是一段小信息,它是服务器发送出来存储在浏览器上的一组组键值对,下次访问服务器时浏览器会自动携带这些键值对,以便服务器提取有用信息。

删除Cookie

def logout(request):
    rep = redirect("/login/")
    rep.delete_cookie("user")  # 删除用户浏览器上之前设置的usercookie值
    return rep

Cookie版登陆校验

图片 1

图片 2

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 == "dashabi":
            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")

图片 3

我们的cookie是保存在浏览器中的键值对

session具体操作

缓存session

SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置

文件session

SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() 

缓存+数据库

SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎

加密cookie session

SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎

Cookie的原理

Cookie的工作原理是:由服务器产生内容,浏览器收到请求后保存在本地;当浏览器再次访问时,浏览器会自动带上Cookie,这样服务器就能通过Cookie的内容来判断这个是“谁”了。

Session

Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:

  • 数据库(默认)
  • 缓存
  • 文件
  • 缓存+数据库
  • 加密cookie

为什么要有cookie?

公用项设置

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,默认修改之后才保存(默认)

  

举例

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.session.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")

    # 删除当前用户的所有Session数据
    request.session.delete()

    request.session.set_expiry(value)
        * 如果value是个整数,session会在些秒数后失效。
        * 如果value是个datatime或timedelta,session就会在这个时间后失效。
        * 如果value是0,用户关闭浏览器session就会失效。
        * 如果value是None,session会依赖全局session失效策略。

查看Cookie

我们使用Chrome浏览器,打开开发者工具。

图片 4

数据库Session

SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)

我们在访问浏览器的时候,千万个人访问同一个页面,我们只要拿着url地址就可以打开页面,但是因为我们的用户是不一样的,我们的权限也不一样,就类似于我们的管理权限,每个人有自己不同的

 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 == "alex1234":
            # 设置session
            request.session["user"] = user
            # 获取跳到登陆页面之前的URL
            next_url = request.GET.get("next")
            # 如果有,就跳转回登陆之前的URL
            if next_url:
                return redirect(next_url)
            # 否则默认跳转到index页面
            else:
                return redirect("/index/")
    return render(request, "login.html")


@check_login
def logout(request):
    # 删除所有当前请求相关的session
    request.session.delete()
    return redirect("/login/")


@check_login
def index(request):
    current_user = request.session.get("user", None)
    return render(request, "index.html", {"user": current_user})

  

Django中操作Cookie

缓存Session

SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置

我们的浏览器在访问的时候会加上cookie,它就相当于一个标签,我们的每个人访问同一个网页的时候

CBV中加装饰器

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 user == 'alex' and pwd == "alex1234":
            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方法上

图片 5

from django.utils.decorators import method_decorator


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")

    @method_decorator(check_login)
    def post(self, request):
        print("Home View POST method...")
        return redirect("/index/")

图片 6

2. 加在dispatch方法上

图片 7

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/")

图片 8

因为CBV中首先执行的就是dispatch方法,所以这么写相当于给get和post方法都加上了登录校验。

3. 直接加在视图类上,但method_decorator必须传 name 关键字参数

如果get方法和post方法都需要登录校验的话就写两个装饰器。

图片 9

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/")

图片 10

获取Cookie

request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)

参数:

  • default: 默认值
  • salt: 加密盐
  • max_age: 后台控制过期时间

文件Session

SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() 

会自带一些信息,让浏览器去识别用户的特定信息,从而把该用户的信息返回给浏览器页面,

补充

CSRF Token相关装饰器在CBV只能加到dispatch方法上

备注:

  • csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
  • csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。

图片 11

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/")

 

 

设置Cookie

Django中设置Cookie:(针对的是响应对象)

rep = HttpResponse(...)
rep = render(request, ...)

rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密盐',...)

参数:

  • key, 键
  • value='', 值
  • max_age=None, 超时时间
  • 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获取(不是绝对,底层抓包可以获取到也可以被覆盖)

缓存+数据库

SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎

就是在我们的浏览器一来一回的过程中实现的,这个cookie里面包含的就是我们的特殊信息,用以互相区分,cookie里面的信息是以键对值的方式保存的,我们的cookie里面那些键对值的方式保存下来的信息是有时效性的,自定义设定时间

删除Cookie

def logout(request):
    rep = redirect("/login/")
    rep.delete_cookie("user")  # 删除用户浏览器上之前设置的usercookie值
    return rep

加密Cookie Session

SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎

其他公用设置项:

图片 12

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,默认修改之后才保存(默认)

图片 13

不管你怎么设置Session,使用方式都一样:

图片 14

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.session.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")

    # 删除当前用户的所有Session数据
    request.session.delete()

    request.session.set_expiry(value)
        * 如果value是个整数,session会在些秒数后失效。
        * 如果value是个datatime或timedelta,session就会在这个时间后失效。
        * 如果value是0,用户关闭浏览器session就会失效。
        * 如果value是None,session会依赖全局session失效策略。

图片 15

Session版登陆验证

图片 16

图片 17

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 == "alex1234":
            # 设置session
            request.session["user"] = user
            # 获取跳到登陆页面之前的URL
            next_url = request.GET.get("next")
            # 如果有,就跳转回登陆之前的URL
            if next_url:
                return redirect(next_url)
            # 否则默认跳转到index页面
            else:
                return redirect("/index/")
    return render(request, "login.html")


@check_login
def logout(request):
    # 删除所有当前请求相关的session
    request.session.delete()
    return redirect("/login/")


@check_login
def index(request):
    current_user = request.session.get("user", None)
    return render(request, "index.html", {"user": current_user})

图片 18

 

Session

CBV中加装饰器相关

CBV实现的登录视图

图片 19

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 user == 'alex' and pwd == "alex1234":
            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')

图片 20

要在CBV视图中使用我们上面的check_login装饰器,有以下三种方式:

from django.utils.decorators import method_decorator

1. 加在CBV视图的get或post方法上

图片 21

from django.utils.decorators import method_decorator


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")

    @method_decorator(check_login)
    def post(self, request):
        print("Home View POST method...")
        return redirect("/index/")

图片 22

2. 加在dispatch方法上

图片 23

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/")

图片 24

因为CBV中首先执行的就是dispatch方法,所以这么写相当于给get和post方法都加上了登录校验。

3. 直接加在视图类上,但method_decorator必须传 name 关键字参数

如果get方法和post方法都需要登录校验的话就写两个装饰器。

图片 25

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/")

图片 26

cookie应用的地方:

Session的由来

Cookie虽然在一定程度上解决了“保持状态”的需求,但是由于Cookie本身最大支持4096字节,以及Cookie本身保存在客户端,可能被拦截或窃取,因此就需要有一种新的东西,它能支持更多的字节,并且他保存在服务器,有较高的安全性。这就是Session。

问题来了,基于HTTP协议的无状态特征,服务器根本就不知道访问者是“谁”。那么上述的Cookie就起到桥接的作用。

我们可以给每个客户端的Cookie分配一个唯一的id,这样用户在访问时,通过Cookie,服务器就知道来的人是“谁”。然后我们再根据不同的Cookie的id,在服务器上保存一段时间的私密资料,如“账号密码”等等。

总结而言:Cookie弥补了HTTP无状态的不足,让服务器知道来的人是“谁”;但是Cookie以文本的形式保存在本地,自身安全性较差;所以我们就通过Cookie识别不同的用户,对应的在Session里保存私密的信息以及超过4096字节的文本。

另外,上述所说的Cookie和Session其实是共通性的东西,不限于语言和框架。

Session保存在服务端的键值对

补充

CSRF Token相关装饰器在CBV只能加到dispatch方法上

备注:

  • csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
  • csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。

图片 27

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/")

图片 28

登录

Django中Session相关方法

# 获取、设置、删除Session中数据
request.session['k1']
request.session.get('k1',None)
request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在则不设置
del request.session['k1']


# 所有 键、值、键值对
request.session.keys()
request.session.values()
request.session.items()
request.session.iterkeys()
request.session.itervalues()
request.session.iteritems()

# 会话session的key
request.session.session_key

# 将所有Session失效日期小于当前日期的数据删除
request.session.clear_expired()

# 检查会话session的key在数据库中是否存在
request.session.exists("session_key")

# 删除当前会话的所有Session数据
request.session.delete()
  
# 删除当前的会话数据并删除会话的Cookie。
request.session.flush() 
    这用于确保前面的会话数据不可以再次被用户的浏览器访问
    例如,django.contrib.auth.logout() 函数中就会调用它。

# 设置会话Session和Cookie的超时时间
request.session.set_expiry(value)
    * 如果value是个整数,session会在些秒数后失效。
    * 如果value是个datatime或timedelta,session就会在这个时间后失效。
    * 如果value是0,用户关闭浏览器session就会失效。
    * 如果value是None,session会依赖全局session失效策略。

自定义分页

图片 29

图片 30

data = []

for i in range(1, 302):
    tmp = {"id": i, "name": "alex-{}".format(i)}
    data.append(tmp)

print(data)


def user_list(request):

    # user_list = data[0:10]
    # user_list = data[10:20]
    try:
        current_page = int(request.GET.get("page"))
    except Exception as e:
        current_page = 1

    per_page = 10

    # 数据总条数
    total_count = len(data)
    # 总页码
    total_page, more = divmod(total_count, per_page)
    if more:
        total_page += 1

    # 页面最多显示多少个页码
    max_show = 11
    half_show = int((max_show-1)/2)

    if current_page <= half_show:
        show_start = 1
        show_end = max_show
    else:
        if current_page + half_show >= total_page:
            show_start = total_page - max_show
            show_end = total_page
        else:
            show_start = current_page - half_show
            show_end = current_page + half_show

    # 数据库中获取数据
    data_start = (current_page - 1) * per_page
    data_end = current_page * per_page

    user_list = data[data_start:data_end]

    # 生成页面上显示的页码
    page_html_list = []
    # 加首页
    first_li = '<li><a href="/user_list/?page=1">首页</a></li>'
    page_html_list.append(first_li)
    # 加上一页
    if current_page == 1:
        prev_li = '<li><a href="#">上一页</a></li>'
    else:
        prev_li = '<li><a href="/user_list/?page={}">上一页</a></li>'.format(current_page - 1)
    page_html_list.append(prev_li)
    for i in range(show_start, show_end+1):
        if i == current_page:
            li_tag = '<li class="active"><a href="/user_list/?page={0}">{0}</a></li>'.format(i)
        else:
            li_tag = '<li><a href="/user_list/?page={0}">{0}</a></li>'.format(i)
        page_html_list.append(li_tag)

    # 加下一页
    if current_page == total_page:
        next_li = '<li><a href="#">下一页</a></li>'
    else:
        next_li = '<li><a href="/user_list/?page={}">下一页</a></li>'.format(current_page+1)
    page_html_list.append(next_li)

    # 加尾页
    page_end_li = '<li><a href="/user_list/?page={}">尾页</a></li>'.format(total_page)
    page_html_list.append(page_end_li)

    page_html = "".join(page_html_list)

    return render(request, "user_list.html", {"user_list": user_list, "page_html": page_html})

图片 31

图片 32

图片 33

class Pagination(object):
    def __init__(self, current_page, total_count, base_url, per_page=10, max_show=11):
        """
        :param current_page: 当前页
        :param total_count: 数据库中数据总数
        :param per_page: 每页显示多少条数据
        :param max_show: 最多显示多少页
        """
        try:
            current_page = int(current_page)
        except Exception as e:
            current_page = 1

        self.current_page = current_page
        self.total_count = total_count
        self.base_url = base_url
        self.per_page = per_page
        self.max_show = max_show

        # 总页码
        total_page, more = divmod(total_count, per_page)
        if more:
            total_page += 1

        half_show = int((max_show - 1) / 2)
        self.half_show = half_show
        self.total_page = total_page

    @property
    def start(self):
        return (self.current_page - 1) * self.per_page

    @property
    def end(self):
        return self.current_page * self.per_page

    def page_html(self):

        if self.current_page <= self.half_show:
            show_start = 1
            show_end = self.max_show
        else:
            if self.current_page + self.half_show >= self.total_page:
                show_start = self.total_page - self.max_show
                show_end = self.total_page
            else:
                show_start = self.current_page - self.half_show
                show_end = self.current_page + self.half_show

                # 生成页面上显示的页码
        page_html_list = []
        # 加首页
        first_li = '<li><a href="{}?page=1">首页</a></li>'.format(self.base_url)
        page_html_list.append(first_li)
        # 加上一页
        if self.current_page == 1:
            prev_li = '<li><a href="#">上一页</a></li>'
        else:
            prev_li = '<li><a href="{0}?page={1}">上一页</a></li>'.format(self.base_url, self.current_page - 1)
        page_html_list.append(prev_li)
        for i in range(show_start, show_end + 1):
            if i == self.current_page:
                li_tag = '<li class="active"><a href="{0}?page={1}">{1}</a></li>'.format(self.base_url, i)
            else:
                li_tag = '<li><a href="{0}?page={1}">{1}</a></li>'.format(self.base_url, i)
            page_html_list.append(li_tag)

        # 加下一页
        if self.current_page == self.total_page:
            next_li = '<li><a href="#">下一页</a></li>'
        else:
            next_li = '<li><a href="{0}?page={1}">下一页</a></li>'.format(self.base_url, self.current_page + 1)
        page_html_list.append(next_li)

        # 加尾页
        page_end_li = '<li><a href="{0}?page={1}">尾页</a></li>'.format(self.base_url, self.total_page)
        page_html_list.append(page_end_li)

        return "".join(page_html_list)

图片 34

图片 35

图片 36

def user_list(request):
    pager = Pagination(request.GET.get("page"), len(data), request.path_info)
    user_list = data[pager.start:pager.end]
    page_html = pager.page_html()
    return render(request, "user_list.html", {"user_list": user_list, "page_html": page_html})

图片 37

扩展:

Django内置分页

图片 38

图片 39

from django.shortcuts import render
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

L = []
for i in range(999):
    L.append(i)

def index(request):
    current_page = request.GET.get('p')

    paginator = Paginator(L, 10)
    # per_page: 每页显示条目数量
    # count:    数据总个数
    # num_pages:总页数
    # page_range:总页数的索引范围,如: (1,10),(1,200)
    # page:     page对象
    try:
        posts = paginator.page(current_page)
        # has_next              是否有下一页
        # next_page_number      下一页页码
        # has_previous          是否有上一页
        # previous_page_number  上一页页码
        # object_list           分页之后的数据列表
        # number                当前页
        # paginator             paginator对象
    except PageNotAnInteger:
        posts = paginator.page(1)
    except EmptyPage:
        posts = paginator.page(paginator.num_pages)
    return render(request, 'index.html', {'posts': posts})

图片 40

图片 41

图片 42

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<ul>
    {% for item in posts %}
        <li>{{ item }}</li>
    {% endfor %}
</ul>

<div class="pagination">

        {% if posts.has_previous %}
            <a href="?p={{ posts.previous_page_number }}">Previous</a>
        {% endif %}

            Page {{ posts.number }} of {{ posts.paginator.num_pages }}.

          {% if posts.has_next %}
              <a href="?p={{ posts.next_page_number }}">Next</a>
          {% endif %}


</div>
</body>
</html>

图片 43

有效时间内可以免登录(我们的一些购物网站里面会有提示信息,几天之内是可以免登陆,都是在cookie里面实现的这些功能)

Session流程解析

图片 44

记住用户的某些浏览习惯

Django中的Session配置

Django中默认支持Session,其内部提供了5种类型的Session供开发者使用。settings.py文件中配置

1. 数据库Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)

2. 缓存Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置

3. 文件Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() 

4. 缓存+数据库
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎

5. 加密Cookie Session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎

其他公用设置项:
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,默认修改之后才保存(默认)

简单的请求限制(例如在投票系统中,我之前在微博上面给中国有嘻哈的选手投票的时候浏览器是会有限制的,不可以无限次的在一定时间内反复提交,系统提示一天只能投一次票,这种情况就是cookie里面可以设置的操作)

CBV中加装饰器相关

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 user == 'alex' and pwd == "alex1234":
            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(HomeView, 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/")

我们的cookie里面保存的信息在页面上是可以显示出来的,打开页面,按住键盘上行的f12键,就可以打开network,里面可以看到请求头,在里面可以查看到我们的cookie信息,这里就存在隐私信息泄露的风险,我们一般都是会有加盐操作,就跟我们学md5的时候,那个加盐是一样的概念,在我们的明文信息的基础上加一层密,同样,我们在后端加密之后需要解密,完成一来一回的操作,cookie里面的加盐也是salt

2. 加在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/")

因为CBV中首先执行的就是dispatch方法,所以这么写相当于给get和post方法都加上了登录校验。

不加盐的cookie

3. 直接加在视图类上,但method_decorator必须传 name 关键字参数

如果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/")

 

rep.set_cookie("k1", "v1", max_age=10)  # 这里是设置cookie

补充

CSRF Token相关装饰器在CBV只能加到dispatch方法上,或者加在视图类上然后name参数指定为dispatch方法。

备注:

  • csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
  • csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。

    from django.views.decorators.csrf import csrf_exempt, csrf_protect from django.utils.decorators import method_decorator

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/")

或者

from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator


@method_decorator(csrf_exempt, name='dispatch')
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/")

 

rep.cookie.get('k1',None)  # 这里是获取cookie

Cookie和Session练习示例

说明:app01使用Cookie,app02使用Session

*  default:默认值

urls路由

图片 45图片 46

from django.conf.urls import url
from django.contrib import admin

from app01 import views as v1
from app02 import views as v2
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^app01/login/$', v1.login),
    url(r'^app01/home/$', v1.home),
    url(r'^app01/index/$', v1.index),
    url(r'^app01/logout/$', v1.logout),

    url(r'^app02/login/$', v2.login),
    url(r'^app02/home/$', v2.home),
    url(r'^app02/index/$', v2.index),
    url(r'^app02/logout/$', v2.logout),

    url(r'^app02/userinfo/$', v2.userInfo.as_view()),
]

urls.py

*  salt:加盐

templates

app01目录下:

图片 47图片 48

# home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>home</title>
</head>
<body>
<h1>这是home页面</h1>
<a href="app01/logout">注销</a>
</body>
</html>


# index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
<h1>这是Index页面</h1>
</body>
</html>


# login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
<h1>这是login页面</h1>
<form action="{{ request.get_full_path }}" method="post">
    {% csrf_token %}
    <p>
        用户名:
        <input type="text" name="user">
    </p>
    <p>
        密码:
        <input type="text" name="pwd">
    </p>
    <input type="submit" value="提交">
</form>
</body>
</html>

app01目录下的html文件

app02目录下:

图片 49图片 50

# home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>home</title>
</head>
<body>
<h1>这是{{ user }}主页面</h1>
<a href="/app02/logout/">注销</a>
</body>
</html>


# index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
<h1>这是Index页面</h1>
</body>
</html>


# login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
<h1>这是login页面</h1>
<form action="{{ request.get_full_path }}" method="post">
    {% csrf_token %}
    <p>
        用户名:
        <input type="text" name="user">
    </p>
    <p>
        密码:
        <input type="text" name="pwd">
    </p>
    <input type="submit" value="提交">
</form>
</body>
</html>

app02目录下的html文件

*  max_age:后台控制过期时间

Cookie版登录验证

图片 51图片 52

from django.shortcuts import render,redirect

# Create your views here.
from functools import wraps

def check_login(func):
    @wraps(func)    # 装饰器修复技术
    def inner(request, *args, **kwargs):
        # 已经登录过的 继续执行
        ret = request.get_signed_cookie("is_login", default="0", salt="jiayan")
        if ret == "1":
            return func(request, *args, **kwargs)
        # 没有登录过的 跳转到登录页面
        else:
            # 获取当前访问的URL
            next_url = request.path_info
            print(next_url)
            return redirect("/app01/login/?next={}".format(next_url))
    return inner

def login(request):
    print(request.get_full_path())  #获取当前请求的路径和参数
    print(request.path_info)    # 获取当前请求的路径
    if request.method == "POST":
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")
        # 从URL里面取到 next参数
        next_url = request.GET.get("next")
        if user == "root" and pwd == "root":
            # 登录成功
            # 告诉浏览器保存一个键值对

            if next_url:
                rep = redirect(next_url)  # 得到一个响应对象
            else:
                rep = redirect("/app01/home/")

            # 设置加盐的cookie
            rep.set_signed_cookie("is_login", "1", salt="jiayan", max_age=30)   # 设置超时时间 默认单位是秒
            return rep
    return render(request, "app01/login.html")

def home(request):
    # 从请求的cookie中查找有没有cookie
    # 取加盐过的cookie
    ret = request.get_signed_cookie("is_login", default="0", salt="jiayan")
    # ret = request.COOKIES.get("is_login", 0)
    if ret == "1":
        # 表示已经登录过
        return render(request, "app01/home.html")
    else:
        return redirect("/app01/login/")

@check_login
def index(request):
    return render(request, "app01/index.html")

# 注销函数
def logout(request):
    # 如何删除cookie
    rep = redirect("/app01/login/")
    rep.delete_cookie("is_login")
    return rep

Cookie版登录验证

  • 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获取(不是绝对,底层抓包可以获取到也可以被覆盖)

Session版登录验证

图片 53图片 54

from django.shortcuts import render,redirect
from django.utils.decorators import method_decorator
from django import views
# Create your views here.
from functools import wraps

def check_login(func):
    @wraps(func)    # 装饰器修复技术
    def inner(request, *args, **kwargs):
        ret = request.session.get("is_login")
        # 1. 获取cookie中的随机字符串
        # 2. 更具随机字符串去数据库取 session_data ———> 解密 ———> 反序列化成字典
        # 3. 在字典里面 根据 is_login 取具体的数据
        if ret == "1":
            # 已经登录过的 继续执行
            return func(request, *args, **kwargs)
        # 没有登录过的 跳转到登录页面
        else:
            # 获取当前访问的URL
            next_url = request.path_info
            print(next_url)
            return redirect("/app02/login/?next={}".format(next_url))
    return inner

def login(request):
    if request.method == "POST":
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")
        # 从URL里面取到 next参数
        next_url = request.GET.get("next")
        if user == "root" and pwd == "root":
            # 登录成功
            # 告诉浏览器保存一个键值对

            if next_url:
                rep = redirect(next_url)  # 得到一个响应对象
            else:
                rep = redirect("/app02/home/")

            # 设置session
            request.session["is_login"] = "1"
            request.session["user"] = user
            request.session.set_expiry(7)
            return rep
    return render(request, "app02/login.html")

@check_login
def home(request):
    user = request.session.get("user")
    return render(request, "app02/home.html", {"user": user})

@check_login
def index(request):
    return render(request, "app02/index.html")

# 注销函数
def logout(request):
    # 只删除session数据
    request.session.delete()
    # 如何删除session数据和cookie
    request.session.flush()
    return redirect("/app02/login/")


# 给CBV视图加装饰器
class userInfo(views.View):
    @method_decorator(check_login)
    def get(self, request):
        return render(request, "app02/userinfo.html")

Session版本登录验证

 

 

我们在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版的登录校验:

图片 55图片 56

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')

View Code

 

 

 浏览器里面是有专门的设置选项,可以选择不保存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'

其他的功用设置项:

图片 57图片 58

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,默认修改之后才保存(默认)

View Code

 

不论你怎么设置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.sessionget('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})  

 

 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})

引入的类的文件:

图片 59图片 60

# 分页封装方法
class MyPage(object):

    def __init__(self, page_num, total_count, base_url, per_page_num=10, max_show=11):
        """

        :param page_num: 当前页面
        :param total_count: 数据总个数
        :param base_url: 分页页码跳转的url
        :param per_page_num: 每一页显示多少条数据
        :param max_show: 页面上最多显示多少页码
        """
        # 实例化时传进来的参数
        try:
            self.page_num = int(page_num)

        except Exception as e:
            self.page_num = 1
        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)
        self.total_page_num, more = divmod(self.total_count, self.per_page_num)
        if more:
            self.total_page_num += 1

    @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:
        """
        # 页面上页码从哪儿开始
        page_start = self.page_num-self.half_show
        page_end = self.page_num+self.half_show
        if self.page_num <= self.half_show:
            page_start = 1
            page_end = self.max_show
        if self.page_num >= self.total_page_num-self.half_show:
            page_end = self.total_page_num
            page_start = self.total_page_num-self.max_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 <= 0:
            page_prev_tmp = '<li><a href="#">上一页</a></li>'
        else:
            page_prev_tmp = '<li><a href="{0}?page={1}">上一页</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}?page={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)

View Code

html页面:

图片 61图片 62

<!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>

View Code

 

 

在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时间

 

本文由9159.com发布于编程,转载请注明出处:有时候我们会听到会话一词,它不会受前面的请

关键词: