django 2 ORM操做 ORM进阶 cookie和session 中间件

ORM操做

ORM概念 

对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。html

简单的说,ORM是经过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。前端

ORM在业务逻辑层和数据库层之间充当了桥梁的做用。python

ORM由来

让咱们从O/R开始。字母O起源于"对象"(Object),而R则来自于"关系"(Relational)。mysql

几乎全部的软件开发过程当中都会涉及到对象和关系数据库。在用户层面和业务逻辑层面,咱们是面向对象的。当对象的信息发生变化的时候,咱们就须要把对象的信息保存在关系数据库中。git

按照以前的方式来进行开发就会出现程序员会在本身的业务逻辑代码中夹杂不少SQL语句用来增长、读取、修改、删除相关数据,而这些代码一般都是极其类似或者重复的。程序员

ORM的优点

ORM解决的主要问题是对象和关系的映射。它一般将一个类和一张表一一对应,类的每一个实例对应表中的一条记录,类的每一个属性对应表中的每一个字段。 redis

ORM提供了对数据库的映射,不用直接编写SQL代码,只需操做对象就能对数据库操做数据。sql

让软件开发人员专一于业务逻辑的处理,提升了开发效率。数据库

ORM的劣势

ORM的缺点是会在必定程度上牺牲程序的执行效率。django

ORM的操做是有限的,也就是ORM定义好的操做是能够完成的,一些复杂的查询操做是完成不了。

ORM用多了SQL语句就不会写了,关系数据库相关技能退化...

ORM总结

ORM只是一种工具,工具确实能解决一些重复,简单的劳动。这是不能否认的。

但咱们不能期望某个工具能一劳永逸地解决全部问题,一些特殊问题仍是须要特殊处理的。

可是在整个软件开发过程当中须要特殊处理的状况应该都是不多的,不然所谓的工具也就失去了它存在的意义。

Django中的ORM

Django项目使用MySQL数据库

1. 在Django项目的settings.py文件中,配置数据库链接信息:

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",
        "NAME": "你的数据库名称",  # 须要本身手动建立数据库
        "USER": "数据库用户名",
        "PASSWORD": "数据库密码",
        "HOST": "数据库IP",
        "POST": 3306
    }
}

2. 在与Django项目同名的目录下的__init__.py文件中写以下代码,告诉Django使用pymysql模块链接MySQL数据库

import pymysql
 
pymysql.install_as_MySQLdb()

注:数据库迁移的时候出现一个警告

WARNINGS: 
?: (mysql.W002) MySQL Strict Mode is not set for database connection 'default'
HINT: MySQL's Strict Mode fixes many data integrity problems in MySQL, such as data truncation upon insertion, by escalating warnings into errors. It is strongly recommended you activate it.

在配置中多加一个OPTIONS参数:Django官网解释

 'OPTIONS': {
    'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"},

Model

字段 

经常使用字段 

AutoField

自增的整形字段,必填参数primary_key=True,则成为数据库的主键。无该字段时,django自动建立。

一个model不能有两个AutoField字段。

IntegerField

一个整数类型。数值的范围是 -2147483648 ~ 2147483647。

CharField

字符类型,必须提供max_length参数。max_length表示字符的长度。

DateField

日期类型,日期格式为YYYY-MM-DD,至关于Python中的datetime.date的实例。

参数:

  • auto_now:每次修改时修改成当前日期时间。
  • auto_now_add:新建立对象时自动添加当前日期时间。

auto_now和auto_now_add和default参数是互斥的,不能同时设置。

DatetimeField

日期时间字段,格式为YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],至关于Python中的datetime.datetime的实例。

字段类型,详情可点击查询官网

AutoField(Field)
        - int自增列,必须填入参数 primary_key=True

    BigAutoField(AutoField)
        - bigint自增列,必须填入参数 primary_key=True

        注:当model中若是没有自增列,则自动会建立一个列名为id的列
        from django.db import models

        class UserInfo(models.Model):
            # 自动建立一个列名为id的且为自增的整数列
            username = models.CharField(max_length=32)

        class Group(models.Model):
            # 自定义自增列
            nid = models.AutoField(primary_key=True)
            name = models.CharField(max_length=32)

    SmallIntegerField(IntegerField):
        - 小整数 -3276832767

    PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正小整数 032767

    IntegerField(Field)
        - 整数列(有符号的) -21474836482147483647

    PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正整数 02147483647

    BigIntegerField(IntegerField):
        - 长整型(有符号的) -92233720368547758089223372036854775807

    BooleanField(Field)
        - 布尔值类型

    NullBooleanField(Field):
        - 能够为空的布尔值

    CharField(Field)
        - 字符类型
        - 必须提供max_length参数, max_length表示字符长度

    TextField(Field)
        - 文本类型

    EmailField(CharField):
        - 字符串类型,Django Admin以及ModelForm中提供验证机制

    IPAddressField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制

    GenericIPAddressField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
        - 参数:
            protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
            unpack_ipv4, 若是指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,须要protocol="both"

    URLField(CharField)
        - 字符串类型,Django Admin以及ModelForm中提供验证 URL

    SlugField(CharField)
        - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、链接符(减号)

    CommaSeparatedIntegerField(CharField)
        - 字符串类型,格式必须为逗号分割的数字

    UUIDField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证

    FilePathField(Field)
        - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
        - 参数:
                path,                      文件夹路径
                match=None,                正则匹配
                recursive=False,           递归下面的文件夹
                allow_files=True,          容许文件
                allow_folders=False,       容许文件夹

    FileField(Field)
        - 字符串,路径保存在数据库,文件上传到指定目录
        - 参数:
            upload_to = ""      上传文件的保存路径
            storage = None      存储组件,默认django.core.files.storage.FileSystemStorage

    ImageField(FileField)
        - 字符串,路径保存在数据库,文件上传到指定目录
        - 参数:
            upload_to = ""      上传文件的保存路径
            storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
            width_field=None,   上传图片的高度保存的数据库字段名(字符串)
            height_field=None   上传图片的宽度保存的数据库字段名(字符串)

    DateTimeField(DateField)
        - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]

    DateField(DateTimeCheckMixin, Field)
        - 日期格式      YYYY-MM-DD

    TimeField(DateTimeCheckMixin, Field)
        - 时间格式      HH:MM[:ss[.uuuuuu]]

    DurationField(Field)
        - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型

    FloatField(Field)
        - 浮点型

    DecimalField(Field)
        - 10进制小数
        - 参数:
            max_digits,小数总长度
            decimal_places,小数位长度

    BinaryField(Field)
        - 二进制类型

字段类型

建立数据库

models.py

from django.db import models

# Create your models here.
class Biao(models.Model):
    nid = models.AutoField(primary_key=True)     #设置主键
    name = models.CharField(max_length=32)       
    age = models.IntegerField()
    birth = models.DateTimeField(auto_now_add=True)       #时间为当前时间

 建库

python3 manage.py makemigrations

python3 manage.py migrate

 字段参数

 null                数据库中字段是否能够为空
    db_column           数据库中字段的列名
    default             数据库中字段的默认值
    primary_key         数据库中字段是否为主键
    db_index            数据库中字段是否能够创建索引
    unique              数据库中字段是否能够创建惟一索引
    unique_for_date     数据库中字段【日期】部分是否能够创建惟一索引
    unique_for_month    数据库中字段【月】部分是否能够创建惟一索引
    unique_for_year     数据库中字段【年】部分是否能够创建惟一索引
 
    verbose_name        Admin中显示的字段名称
    blank               Admin中是否容许用户输入为空
    editable            Admin中是否能够编辑
    help_text           Admin中该字段的提示信息
    choices             Admin中显示选择框的内容,用不变更的数据放在内存中从而避免跨表操做
                        如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)
 
    error_messages      自定义错误信息(字典类型),从而定制想要显示的错误信息;
                        字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
                        如:{'null': "不能为空.", 'invalid': '格式错误'}
 
    validators          自定义错误验证(列表类型),从而定制想要的验证规则
                        from django.core.validators import RegexValidator
                        from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
                        MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
                        如:
                            test = models.CharField(
                                max_length=32,
                                error_messages={
                                    'c1': '优先错信息1',
                                    'c2': '优先错信息2',
                                    'c3': '优先错信息3',
                                },
                                validators=[
                                    RegexValidator(regex='root_\d+', message='错误了', code='c1'),
                                    RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'),
                                    EmailValidator(message='又错误了', code='c3'), ]
                            )
 
字段参数

ORM查询操做

13种操做方法 

import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "diccc.settings")
    import django
    django.setup()
    from app01 import models


aa
= models.Biao.objects.all() # all()获取到全部的数据 # get 获取一个知足条件的对象,获取不到或者获取多个就报错(通常不用) bb = models.Biao.objects.get(age=25) # filter 获取知足条件的全部对象,获取不到返回空,不会报错 cc = models.Biao.objects.filter(name='wk') #first()获取多个对象的第一个 dd = models.Biao.objects.filter(name='wk').first() #last() 获取多个对象的最后一个 ee = models.Biao.objects.filter(name='wk').last() #exclude 获取不知足条件的全部对象,获取不到返回空,不会报错 ff = models.Biao.objects.exclude(name='wk') #values() 获取数据的key以及对应的值,获取的是字典形式的QuerySet gg = models.Biao.objects.all().values() #以键值的形式获取全部数据 hh = models.Biao.objects.all().values('name','age') #也能够获取指定键的 #values_list() 以元组的形式获取数据,根据位置取值 ii = models.Biao.objects.all().values_list() #order_by 排序 可指定字段,也可指定多个字段,多字段先排第一个,加"-"降序 jj = models.Biao.objects.order_by('-nid','age') #reverse()反向排序, 对一个有序列表才有效 kk = models.Biao.objects.order_by('age').reverse() #distinct() 去重 ll = models.Biao.objects.values('name').distinct() # count() 计数,记录有多少数据 返回一个整数 mm = models.Biao.objects.count() # exists()判断数据是否存在 不在返回False 存在True nn = models.Biao.objects.first(name="wk").exists()

单表的双下划线方法

    # __gt大于  __lt小于
    cc = models.Biao.objects.filter(nid__gt=1)
    # __gte大于等于    __lte小于等于
    dd = models.Biao.objects.filter(nid__gte=1)
    #__in 取等于1和3的
    ee = models.Biao.objects.filter(nid__in=[1,3])
    #__range 取1到3的
    ff = models.Biao.objects.filter(nid__range=[1, 3])
    #__contains  name字段的值含有字母w的
    gg = models.Biao.objects.filter(name__contains='w')
    # __icontains  name字段的值含有字母w的 忽略大小写
    jj = models.Biao.objects.filter(name__icontains='w')
#相似的还有:startswith,istartswith 以什么什么开头 加i不区分大小写 # , endswith, iendswith 以什么什么结尾
# date字段还能够: models.Class.objects.filter(first_day__year=2017)
#__isnull查找为空的 =False查出不为空的 (空字符串和null不同,空字符串查不出,null才查的出)
jj = models.Biao.objects.filter(name__isnull=True)

 外键的操做

建立两个关联表 一对多

class chubanshe(models.Model):
    meme = models.CharField(max_length=32)  

class Book(models.Model):
    title = models.CharField(max_length=32)
    chubanshe = models.ForeignKey('chubanshe',on_delete=models.CASCADE)   #关联chubanshe表 ,而且级联删除

方法

import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "diccc.settings")
    import django
    django.setup()
    from app01 import models

#正向查 多对一 (book对chubanshe)
    book_obj = models.Book.objects.get(pk=1) #取出book表里主键为1的对象
    print(book_obj.chubanshe)  #点book表里对应的外键的key 获得对应外键chubansh表里的关联的对象
#反向查 一对多 (chubanshe对book 如下方法2选一 )
    #models.py里 不写 related_name的时候
    chubanshe = models.chubanshe.objects.get(pk=2)  #取出版社表里的对象
    print(chubanshe.book_set)    #关系对象管理(根据chubanshe表反向关联Book表 book即Book表的小写)
    print(chubanshe.book_set.all())
    #models.py里写related_name的时候(在 models.py里的 外键表里related_name = books)
    chubanshe = models.chubanshe.objects.get(pk=2)  # 取出版社表里的对象
    print(chubanshe.books)  # 关系对象管理(根据chubanshe表反向关联Book表 book即Book表的小写)
    print(chubanshe.books.all())
    #基于字段的查询 根据book表查 chubanshe表的内容 指定book内容的方法(related_name__键=值)
     #若是不指定related_name就小写类名book__title  这种跨表__字段的方法效率更高
    
    ret = models.chubanshe.objects.filter(books__title="呵呵哒")
    rett = models.Book.objects.filter(chubanshe__meme='沙河出版社')
    print(ret)

多对多

class Book(models.Model):
    title = models.CharField(max_length=32)   #related_name 反向查询时的名字
    chubanshe = models.ForeignKey('chubanshe',related_name='books',on_delete=models.CASCADE)
    def __str__(self):
        return "%s"  % self.title

class zuozhe(models.Model):
    name = models.CharField(max_length=32) 
# ManyToManyField 多对多 zuozhe表关联Book表 这样建立在zuozhe表里不会出现books字段而是在库里多一个zuozhe_books的表
books = models.ManyToManyField('Book')

提交数据库会出现两个表

 

 

 

多表关联操做

import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "diccc.settings")
    import django
    django.setup()
    from app01 import models

#多对多
    zuozhe_obj = models.zuozhe.objects.get(pk=1)
    print(zuozhe_obj)
    print(zuozhe_obj.name)
    #拿到主键为1的做者写的全部书籍   books为related_name起的别名
    print(zuozhe_obj.books.all())
    book_obj = models.Book.objects.get(pk=3)
    #拿到 主键为3的书的全部做者  用小写类名_set的方法(zuozhe_set)
    print(book_obj.zuozhe_set.all())
    #create 建立新数据并生成对应关系
    #经过作做者对象建立关联的书籍对象 会在多对多的关联表生成关系数据
    zuozhe_obj.books.create(title='鬼吹灯',chubanshe_id=1)
    #经过书籍对象建立关联的做者对象
    book_obj.zuozhe_set.create(name='吴老狗')
    #add添加 书与做者的关系   外键的管理对象只能用对象 不饿能用ID
    zuozhe_obj.books.add(2,5) #给 zuozhe_obj这个对象(主键为1的做者)添加主键为2和5的两本书
    book_obj.zuozhe_set.add(2,4,6) #给book_obj这个对象(主键为1的书)添加主键为2,4,6的三个做者
    zuozhe_obj.books.add(*models.Book.objects.filter(id__in=[1,2,3])) #*为打散
    #remove 删除对应关系 和add同样
    zuozhe_obj.books.remove(2, 5)
    book_obj.zuozhe_set.remove(2, 4, 6)
    #clear 清空
    zuozhe_obj.books.clear()
    book_obj.zuozhe_set.clear()
    #set 从新设置做者对应的书籍为1,2,3,4 set以后该做者对应的书只有1,2,3,4以前有的其余的会删除,以前没的会添加
    zuozhe_obj.books.set([1,2,3,4])

 聚合和分组

聚合

aggregate()QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。

键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。

用到的内置函数:

from django.db.models import Avg, Sum, Max, Min, Count

 定义表

class Book(models.Model):
    title = models.CharField(max_length=32)   #related_name 反向查询时的名字
    chubanshe = models.ForeignKey('chubanshe',related_name='books',on_delete=models.CASCADE)
    jiage = models.DecimalField(max_digits=5,decimal_places=2)   #价格 位数最大5位 999.99
    def __str__(self):
        return "%s"  % self.title

聚合函数的使用

#聚合 引用对应的方法
    from django.db.models import Max,Min,Sum,Avg,Count
    #aggregate 聚合函数 Max拿价格最高的 Min最低的 Avg平均价格 Sum总和 Count个数
    ret = models.Book.objects.aggregate(Max('jiage'),Min('jiage'),Avg('jiage'),Sum('jiage'),Count('jiage'))
    #给前端关键字传参   聚合后是个字典
    rett = models.Book.objects.aggregate(Max=Max('jiage'), Min=Min('jiage'), Avg=Avg('jiage'))
    print(ret)

分组

import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "diccc.settings")
    import django
    django.setup()
    from app01 import models
    from django.db.models import Max, Min, Sum, Avg, Count
#分组
    #看每一个出版社的书籍的平均价
    #第一种方法   按出版社分组              外接书籍的平均价格  以字典的形式显示
    ret = models.chubanshe.objects.annotate(Avg('books__jiage')).values()
    #第二种方法
    #     查询Book表         之外接出版社的名字划分字典     给书的价格分组
    ret = models.Book.objects.values('chubanshe__meme').annotate(Avg('jiage'))
    print(ret)

 F查询和Q查询

F查询 用来对俩个字段做比较

在上面全部的例子中,咱们构造的过滤器都只是将字段值与某个常量作比较。若是咱们要对两个字段的值作比较,那该怎么作呢?

Django 提供 F() 来作这样的比较。F() 的实例能够在查询中引用字段,来比较同一个 model 实例中两个不一样字段的值。

class Book(models.Model):
    title = models.CharField(max_length=32)   #related_name 反向查询时的名字
    chubanshe = models.ForeignKey('chubanshe',on_delete=models.CASCADE)
    jiage = models.DecimalField(max_digits=5,decimal_places=2)  #最大5位 999.99
    shouchu = models.IntegerField()       #增长书的售出字段
    kucun = models.IntegerField()         #增长书的库存字段
    def __str__(self):
        return "%s"  % self.title
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "diccc.settings")
    import django
    django.setup()
    from app01 import models
    from django.db.models import F, Q  #引用F 和Q
    #查询库存大于售出的书
    ret = models.Book.objects.filter(kucun__gt=F('shouchu')).values()
    print(ret)
    #Django支持 F() 对象之间以及F() 对象和常数之间的加减乘除和取模的操做。
    #更新出售的书是库存书的2倍
    models.Book.objects.update(kucun=F('shouchu') * 2)  #库存=售出*2

Q查询

filter() 等方法中的关键字参数查询都是一块儿进行“AND” 的。 若是你须要执行或查询须要用到Q

import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "diccc.settings")
    import django
    django.setup()
    from app01 import models
    from django.db.models import F, Q  #引用F 和Q
#查询主键大于5 或者主键小于3的 (
|是或 &是与 ~是非) ret = models.Book.objects.filter(Q(pk__gt=5)|Q(pk__lt=3)) print(ret)

事务

import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "diccc.settings")
    import django
    django.setup()
    from app01 import models
    from django.db import transaction   #事务
    try:
        with transaction.atomic():  #一次性这行,若是中间有报错则回滚.前边执行成功的所有做废
            #一系列操做
            models.chubanshe.objects.create(meme='唉呀妈呀')  #给出版社表增长内容
            models.zuozhe.objects.create(name='卧底玛雅')   #给做者表增长内容
    except Exception as e:
        print(e)

Django ORM执行原生SQL

# extra
# 在QuerySet的基础上继续执行子语句
# extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)

# select和select_params是一组,where和params是一组,tables用来设置from哪一个表
# Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
# Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
# Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
# Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

举个例子:
models.UserInfo.objects.extra(
                    select={'newid':'select count(1) from app01_usertype where id>%s'},
                    select_params=[1,],
                    where = ['age>%s'],
                    params=[18,],
                    order_by=['-age'],
                    tables=['app01_usertype']
                )
                """
                select 
                    app01_userinfo.id,
                    (select count(1) from app01_usertype where id>1) as newid
                from app01_userinfo,app01_usertype
                where 
                    app01_userinfo.age > 18
                order by 
                    app01_userinfo.age desc
                """


# 执行原生SQL
# 更高灵活度的方式执行原生SQL语句
# from django.db import connection, connections
# cursor = connection.cursor()  # cursor = connections['default'].cursor()
# cursor.execute("""SELECT * from auth_user where id = %s""", [1])
# row = cursor.fetchone()

Django终端打印SQL语句

在Django项目的settings.py文件中,在最后复制粘贴以下代码:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

即为你的Django项目配置上一个名为django.db.backends的logger实例便可查看翻译后的SQL语句

 cookie

https://www.cnblogs.com/maple-shaw/articles/9502602.html

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

一个简单的cookie认证登陆页

from django.conf.urls import url, include

from app01 import views
urlpatterns = [
    url(r'^login/$', views.login, name='login'),   #登陆页
    url(r'^zhanshi/$', views.zhanshi, name='zhanshi'),   #展现页
]

zhanshi.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>OJBK</h1>
</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    {% csrf_token %}
    <p>
       用户名: <input type="text" name="yh">
    </p>
    <p>
        密码: <input type="password" name="pas">
    </p>
    <button>登陆</button>
    </form>
</body>
</html>

后台逻辑

from django.shortcuts import render,reverse,redirect

# Create your views here.
#一个简单的登陆展现页
def login_required(func):         #cookie 验证装饰器
    def inner(request,*args,**kwargs):
        is_login = request.COOKIES.get('is_login')  # 展现页获取cookie
        if is_login != '1':       # 若是cookie的值不是咱们给的值则反回一个让其从新登陆
            return redirect(reverse('login'))
        ret = func(request,*args,**kwargs)
        return ret
    return inner

def login(request):
    if request.method == 'POST':
        username = request.POST.get('yh')
        passwd = request.POST.get('pas')
        if username == 'wk' and passwd == '123456':
            ret = redirect(reverse('zhanshi'))
            ret.set_cookie('is_login', '1')   #给返回的页面加一个cookie
            return ret
    return render(request,'login.html')
@login_required def zhanshi(request):
return render(request,'zhanshi.html') #若是cookie认证成功 怎返回展现页

实现 未登录时,进入登陆页,登录后直接跳转到以前的页面

from django.shortcuts import render,reverse,redirect

# Create your views here.
#实现 未登录时,进入登陆页,登录后直接跳转到以前的页面
def login_required(func):     #cookie 验证装饰器
    def inner(request,*args,**kwargs):
        is_login = request.COOKIES.get('is_login')  # 展现页获取cookie
        if is_login != '1':  # 若是cookie的值不是咱们给的值则反回一个让其从新登陆
            url = request.path_info #获取到 进入登陆页前的页面地址
            return redirect('login/?next=%s' % url) #将获取到的url地址传递给登陆页
        ret = func(request,*args,**kwargs)
        return ret
    return inner

def login(request):
    if request.method == 'POST':
        username = request.POST.get('yh')
        passwd = request.POST.get('pas')
        if username == 'wk' and passwd == '123456':
            url = request.GET.get('next')  #获取进入到登陆页时装饰器传进来的url地址
            if url :        #若是url不为空
                ret = redirect(url)   #则重定向到获取到的url地址
            else:          #若是为空
                ret = redirect(reverse('zhanshi'))  #则重定向到展现页
            ret.set_cookie('is_login', '1')  # 给返回的页面加一个cookie
            return ret
    return render(request,'login.html')
@login_required
def zhanshi(request):
    return render(request,'zhanshi.html')  #若是cookie认证成功 怎返回展现页

 加密加盐的cookie (加密和没加密同样因此没啥需求不用)

ret.set_cookie('is_login', '1')  # 普通的cookie
ret.set_signed_cookie('is_login', '1',salt='yan')  #加盐的cookie
is_login = request.COOKIES.get('is_login')  #获取普通cookie
is_login = request.get_signed_cookie('is_login',salt='yan',default='')  #获取加密的cookie,default若是获取不到值则报错因此给他一个空

删除cookie(注销功能)

逻辑:
def logout(request):
    ret = redirect('/login/')  #注销后返回登陆页面
    ret.delete_cookie('is_login')  #而且根据键删除对应的cookle return ret

路由
    url(r'^logout/$', views.logout, name='logout'),

前端
   <a href="{% url 'logout' %}">注销</a>

设置Cookie

参数:

  • 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传输             True是http传输
  • httponly=False 只能http协议传输,没法被JavaScript获取(不是绝对,底层抓包能够获取到也能够被覆盖 

session

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

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

咱们能够给每一个客户端的Cookie分配一个惟一的id,这样用户在访问时,经过Cookie,服务器就知道来的人是“谁”。而后咱们再根据不一样的Cookie的id,在服务器上保存一段时间的私密资料,如“帐号密码”等等。

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

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

session的使用

设置与获取

request.session['is_login'] = 1
is_login = request.session.get('is_login')

更多方法

# 获取、设置、删除Session中数据
request.session['k1']
request.session.get('k1',None)
request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在则不设置 若是不存在则设置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表里的session_key字段(session_data的键)

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

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

# 删除当前会话的全部Session数据
request.session.delete()   #用它能够作注销 他只删数据库的session 不删浏览器以前保存的
  
# 删除当前的会话数据并删除会话的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失效策略。

 在后台的使用

from django.shortcuts import render,reverse,redirect

# Create your views here.
#实现 未登录时,进入登陆页,登录后直接跳转到以前的页面
def login_required(func):     #session 验证装饰器
    def inner(request,*args,**kwargs):
        #is_login = request.COOKIES.get('is_login')  #获取普通cookie
        is_login = request.session.get('is_login')   #session的获取 验证是否已登录
        if is_login != 1:  # 若是session的值不是咱们给的值则反回一个让其从新登陆
            url = request.path_info #获取到 进入登陆页前的页面地址
            print(url)
            return redirect('/login/?next=%s' % url) #将获取到的url地址传递给登陆页
        ret = func(request,*args,**kwargs)
        return ret
    return inner

def login(request):
    if request.method == 'POST':
        username = request.POST.get('yh')
        passwd = request.POST.get('pas')
        if username == 'wk' and passwd == '123456':
            url = request.GET.get('next')  #获取进入到登陆页时装饰器传进来的url地址
            if url :        #若是url不为空
                ret = redirect(url)   #则重定向到获取到的url地址
            else:          #若是为空
                ret = redirect(reverse('zhanshi'))  #则重定向到展现页
            #ret.set_cookie('is_login', '1',max_age=5,path='/zhanshi/')
            request.session['is_login'] = 1      #session的使用
            request.session.set_expiry(0)     #关闭浏览器则session超时
            return ret
    return render(request,'login.html')
@login_required
def zhanshi(request):
    return render(request,'zhanshi.html')  #若是session认证成功 怎返回展现页
@login_required
def xixi(request):
    return render(request,'xixi.html')
def logout(request):             #注销
    request.session.flush()
    return redirect('/login/')

Django中的Session配置

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

from django.conf import global_settings     #打开里面是django的全部配置 session的也在
1. 数据库Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db'     # 引擎(默认)

2. 缓存Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎 可用redis和memacach  通常都存在缓存里
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,默认修改以后才保存(默认)

Django中Session相关设置

中间件

什么是中间件?

官方的说法:中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。每一个中间件组件都负责作一些特定的功能。

可是因为其影响的是全局,因此须要谨慎使用,使用不当会影响性能。

说的直白一点中间件是帮助咱们在视图函数执行以前和执行以后均可以作一些额外的操做,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在处理请求的特定的时间去执行这些方法。

咱们一直都在使用中间件,只是没有注意到而已,打开Django项目的Settings.py文件,看到下图的MIDDLEWARE配置项。

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

MIDDLEWARE配置项是一个列表,列表中是一个个字符串,这些字符串实际上是一个个类,也就是一个个中间件。

咱们以前已经接触过一个csrf相关的中间件了?咱们一开始让你们把他注释掉,再提交post请求的时候,就不会被forbidden了,后来学会使用csrf_token以后就再也不注释这个中间件了。

自定义中间件

中间件能够定义五个方法,分别是:(主要的是process_request和process_response)

  • process_request(self,request)
  • process_view(self, request, view_func, view_args, view_kwargs)
  • process_template_response(self,request,response)    #须要后台有TemplateResponse() 才触发
  • process_exception(self, request, exception)         #须要后台有错误才触发
  • process_response(self, request, response)      

以上方法的返回值能够是None或一个HttpResponse对象,若是是None,则继续按照django定义的规则向后继续执行,若是是HttpResponse对象,则直接将该对象返回给用户。

 

1.首先在项目目录下建立存放中间件文件的目录 

中间件文件

2.在项目里的settings.py里注册中间件

 

process_request(self,request)  方法 

    参数: request  请求的对象和视图中是同一个

    执行时间 :在视图以前(就是以前的后台,views.py)

    执行顺序:按照注册的顺序执行

    return返回值:

              None:  走正常流程

    HttpResponse ,不执行下边的中间件,不走视图,若是此类中有process_response方法,则走process_response方法中的return,若是有process_view则执行

             当前函数中的process_view,再执行当前函数中的process_response 其余不执行

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render,reverse,redirect,HttpResponse
class MD1(MiddlewareMixin):
     def process_request(self,request):
         print('MD1 process_request' )
         return HttpResponse("MD1 process_request") 

 

process_response(self, request, response)  方法

     参数:  request  请求的对象 和视图中的同样    response响应对象,就是后台执行完后return 的返回值

     执行时间: 在视图函数以后

     执行顺序: 按注册的顺序   倒序执行

     返回值:    必 须 有 返回值,不然报错 返回response

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render,reverse,redirect,HttpResponse
class MD1(MiddlewareMixin):
     def process_request(self,request):
         print('MD1 process_request')
         # 执行完视图函数后才执行process_response(self,request,response)  必须有return   response就是视图执行完后return的结果
     def process_response(self,request,response):
         print('MD1 process_response')
         return response

class MD2(MiddlewareMixin):
    def process_request(self,request):
         print('MD2 process_request')
    def process_response(self, request, response):
        print('MD2 process_response')
        return response

注册顺序是MD1在前MD2在后   process_response的执行顺序是倒序先执行MD2再执行MD1 

此时的执行流程

 

process_view(self,request,view_func,view_args,view_kwargs) 方法

     参数:  request  请求的对象 和视图中的同样     view_func view_args view_kwargs分别是   urls.py里的

 # view_func = views.index    view_args = url位置参数(\d+)   view_kwargs =  关键字参数(?P<num>[0-9]+)
 url(r'^index/(\d+)/(?P<num>[0-9]+)', views.index, name='index'),  

     执行时间: 在process_request以后 ,在视图函数以前执行

     执行顺序: 按注册的顺序执行

  return返回值:

              None:  走正常流程

    HttpResponse :  下一个中间件的process_view和视图不执行,直接执行最后一个中间件的process_response 在往上执行其余的process_response

process_exception(self,request,exception):

  参数:  request  请求的对象 和视图中的同样  exception 错误对象

     执行时间: 有异常才执行

     执行顺序: ,在视图出现异常后 按注册的顺序  倒序执行,可以处理掉异常后, 再执行process_response

     返回值:    

    None:  交给下一个中间件的process_exception方法处理异常

    HttpResponse : 下一个中间件的process_exception方法就不执行了,直接走最后一个中间件的process_response

使用方法 

     def process_exception(self, request, exception):   #后台报错则触发
         return HttpResponse('2322')

process_template_response(self,request,response)

  参数:  request  请求的对象 和视图中的同样   response响应对象,就是后台执行完后return 的返回值

     执行时间:  视图函数返回一个template_response对象

     执行顺序: ,按注册表倒序执行

     返回值:    

    HttpResponse :  返回response

使用方法:

视图:执行index 返回    return TemplateResponse()  而且里边带有一个字典

from django.template.response import TemplateResponse

def index(request):
    return TemplateResponse(request,'xixi.html',{'ret':'呵呵哒'})

中间件:

   def process_template_response(self,request,response):
         print('MD1 process_template_response ')
         response.context_data = {'ret':'卧槽'}   #用context_data把ret的值替换掉 并返回  response
         return response                

前端:展现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>嘻嘻</h1>
<h1> {{ ret }}</h1>
</body>
</html>

 

五种方法合集

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render,reverse,redirect,HttpResponse
class MD1(MiddlewareMixin):
     def process_request(self,request):
         print('MD1 process_request')
     def process_response(self,request,response):
         print('MD1 process_response')
         return response
     def process_view(self,request,view_func,view_args,view_kwargs):
         print('MD1 process_view')
         #return HttpResponse('MD1 process_view')
     def process_exception(self, request, exception):
         return HttpResponse('2322')
     def process_template_response(self,request,response):
         print('MD1 process_template_response ')
         response.context_data = {'ret':'卧槽'}
         return response




class MD2(MiddlewareMixin):
    def process_request(self,request):
         print('MD2 process_request')

    def process_response(self, request, response):
        print('MD2 process_response')
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print('MD1 process_view')
        # return HttpResponse('MD2 process_view')

    def process_exception(self, request, exception):
        print('MD1 process_exception')
        print(exception)
        return HttpResponse('2322')

    def process_template_response(self, request, response):
        return response
相关文章
相关标签/搜索