ORM操作

ORM概念 

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

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

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

ORM由来

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

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

按照之前的方式来进行开发就会出现程序员会在自己的业务逻辑代码中夹杂很多SQL语句用来增加、读取、修改、删除相关数据,而这些代码通常都是极其相似或者重复的。

ORM的优势

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

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

让软件开发人员专注于业务逻辑的处理,提高了开发效率。

ORM的劣势

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

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

 

 

 

 

  

 

 

 

   

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted on 2019-03-04 20:58  临渊慕鱼不如退而结网  阅读(336)  评论(0编辑  收藏  举报