Q查询进阶操作 ORM查询优化 only与defer select_related与prefetch_related ORM事务 ORM常用字段类型 ORM常用字段参数

Q查询进阶操作

from django.db.models import Q
q_obj = Q()  # 1.产生q对象
q_obj.connector = 'or'  # 默认多个条件的连接是and可以修改为or
q_obj.children.append(('pk', 1))  # 2.添加查询条件
q_obj.children.append(('price__gt', 2000))  # 支持添加多个
res = models.Book.objects.filter(q_obj)  # 查询支持直接填写q对象
print(res)

如下图查询条件的左边到底是什么?
image
pk 和 'pk'是有很大区别的!
pk是一个变量,或者说是一个关键字实参。
'pk'是一个字符串。
使用Q查询可以通过输入字符串来控制如何查询。
首先Q查询,其实使用的是Q类产生的对象:
image
产生Q对象:
image
Q对象里面可以存放查询条件。

children.append

使用Q对象.children.append方法可以往Q对象里面添加多个查询条件。

查询条件添加好之后,直接将Q对象放入filter:
image

使用append添加查询条件,条件之间默认是and关系。
也可以修改成or关系:
使用q_obj.connector = 'or'
image

图书查询功能

把查询条件变成字符串,意味着可以与用户互动。
用户的输入就是字符串,获取到用户输入的字符串,就可以从数据库拿到数据。

需求:利用q查询进阶操作编写一个简易版本的图书查询功能。

ORM查询优化

惰性查询

看如下例子:
image
为什么日志不打印??
orm的查询默认都是惰性查询。什么叫惰性查询?
因为查询的结果res没有用到,所以orm干脆不执行,可以减轻一点数据库的负担。

自动分页 limit

举例:我们的表里面有几亿条数据!然后使用orm进行查询。
orm的查询自带分页处理:
image
orm底层写的sql使用了LIMIT关键字,这里就是直接限制展示的条数为21.

only与defer

	'''数据对象+含有指定字段对应的数据'''
    # res = models.Book.objects.only('title', 'price')
    # print(res)  # queryset [数据对象、数据对象]
    # for obj in res:
        # print(obj.title)  # 点击括号内填写的字段 不走SQL查询
        # print(obj.price)
        # print(obj.publish_time)  # 可以点击括号内没有的字段获取数据 但是会走SQL查询
        
    res = models.Book.objects.defer('title', 'price')
    # print(res)  # queryset [数据对象、数据对象]
    for obj in res:
        # print(obj.title)  # 点击括号内填写的字段 走SQL查询
        # print(obj.price)
        print(obj.publish_time)  # 点击括号内没有的字段获取数据 不走SQL查询

请看如下例子:
image

all方法可以拿到所有的书籍对象,数据全在对象里。通过values方法可以拿到书籍的name字段、price字段。name、price存在一个queryset结果集里,格式是列表套字典。并且这个字典是无法使用点的方式取值的。

only

image
得到的结果变成列表套数据对象(此时是书籍对象)。
此时我们就可以通过点的方式取值:
image
同时这个only产生的书籍对象,还可以点only括号内没有的字段:
image
点这个不存在的字段时,only产生的对象肯定也没有相应的数据。此时该对象就会使用sql对数据库进行查询获得数据。并且每次点这个不存在的字段时都会查一次数据库。

defer

image
defer和only相反,defer括号里没有的字段会在第一次SQL查询后封装到对象中。
如这里obj.publish_time是一个括号中没出现的字段,这种字段的数据会封装到BOOK对象中。反而是出现在括号内的字段,需要额外的sql来进行数据的查询。

总结:对象中有的数据,就不会再执行SQL进行查询,因为这会增加数据库的负担。当对象想使用自己内部没有的数据时,就会调用SQL语句进行查询。

select_related与prefetch_related

select_related与prefetch_related里面都只能填写一对多、一对一外键字段,不支持多对多外键。

	 # res = models.Book.objects.all()
    # for obj in res:
    #     print(obj.publish.name)  # 每次查询都需要走SQL
    # res = models.Book.objects.select_related('authors')  # 先连表后查询封装
    # res1 = models.Author.objects.select_related('author_detail')  # 括号内不支持多对多字段 其他两个都可以
    # print(res1)
    # for obj in res:
    #     print(obj.publish.name)  # 不再走SQL查询

    res = models.Book.objects.prefetch_related('publish')  # 子查询
    for obj in res:
        print(obj.publish.name)

示例:
image
数据对象点出版社的字段name 出版社的名字肯定不会封装在book对象 所以就需要临时写SQL去数据库查询
但是这样会写很多SQL,增加数据库负担。

不想走SQL,意味着要在第一句SQL里全部查询到。
这时候就要使用select_related:
image
底层是帮你把书籍表和出版社表进行连表操作。
这样就后续调用就不需要走sql了:因为数据都封装到对象了。
image

image
prefetch_related的底层是子查询。
先查一张表再查另外一张表,最后把数据封装到对象。

ORM事务操作

事务复习

"""
1.事务的四大特性(ACID)
	原子性、一致性、隔离性、持久性
2.相关SQL关键字
	start transaction;
	rollback;
	commit;
	savepoint;
3.相关重要概念
	脏读、幻读、不可重复读、MVCC多版本控制...
"""

django orm提供了至少三种开启事务的方式

方式1: 全局有效 配置文件数据库相关添加键值对

       "ATOMIC_REQUESTS": True每次请求所涉及到的orm操作同属于一个事务

image
默认每一次网络请求,涉及到的所有orm操作同属于一个事务。如果哪个orm报错了,事务会自动回滚。
比如:
image
对于这次请求,只要程序执行到了return,事务就算已经执行成功了。也就是说return后面报错了,数据库也还是会新增数据。
如果不配置django事务,默认情况下是没有事务的,代码走到哪里就是哪。可能出现前几个orm成功,后几个orm失败的情况。

方式2: 局部有效 装饰器

       from django.db import transaction
       @transaction.atomic
       def index():pass	

这个是局部开启:
用装饰器装饰视图函数,这个视图函数内部就属于一个事务。只要函数内报错,事务就会回滚。数据库会恢复到进行事务之前的状态。
image
极限情况:(return后面必须返回httprespon对象)
image

方式3: 局部有效 with上下文管理

       from django.db import transaction
    	def reg():
    		with transaction.atomic():
             pass

with的子代码内是一个事务:(这种方式用于更小的局部,只能在某个代码块)
image
示例:
image

ORM常用字段类型

AutoField
	primary_key=True
CharField
	max_length
IntegerField
BigIntergerField
DecimalField
	max_digits decimal_places
DateField
	auto_now auto_now_add
DateTimeField
	auto_now auto_now_add

BooleanField

传布尔值自动存0或1

TextField

存储大段文本
有个数限制的使用CharField(用户名)。
个数不固定的使用TextField(小说)。

EmailField

存储邮箱格式数据

FileField

传文件对象 自动保存到提前配置好的路径下并存储该路径信息
主库保存的是文件路径!其他数据库里保存具体的文件数据。

自定义字段

比如django orm中没有专门的char字段。Charfield实际是数据库中的varchar。
orm给我们提供的字段并不是没有使用到char字段的:
'UUIDField':'char(32)'
'IPAddressField':'char(15)'
但是你用这些字段去完成别的事,不符合变量名的见名知义。

所以,我们可以通过自定义字段类型的方式,定义出char字段:

class MyCharField(models.Field):
        def __init__(self, max_length, *args, **kwargs):
            self.max_length = max_length
            super().__init__(max_length=max_length, *args, **kwargs)

        def db_type(self, connection):
            return 'char(%s)' % self.max_length

如下图,orm这些字段都继承于父类Field:
image
重写字段类:
image
使用自定义字段:
image

更多orm字段

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):
        - 小整数 -32768 ~ 32767

    PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正小整数 0 ~ 32767
    IntegerField(Field)
        - 整数列(有符号的) -2147483648 ~ 2147483647

    PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正整数 0 ~ 2147483647

    BigIntegerField(IntegerField):
        - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807

    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)
        - 二进制类型

ORM字段与MySQL字段对应关系


    'AutoField': 'integer AUTO_INCREMENT',
    'BigAutoField': 'bigint AUTO_INCREMENT',
    'BinaryField': 'longblob',
    'BooleanField': 'bool',
    'CharField': 'varchar(%(max_length)s)',
    'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
    'DateField': 'date',
    'DateTimeField': 'datetime',
    'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
    'DurationField': 'bigint',
    'FileField': 'varchar(%(max_length)s)',
    'FilePathField': 'varchar(%(max_length)s)',
    'FloatField': 'double precision',
    'IntegerField': 'integer',
    'BigIntegerField': 'bigint',
    'IPAddressField': 'char(15)',
    'GenericIPAddressField': 'char(39)',
    'NullBooleanField': 'bool',
    'OneToOneField': 'integer',
    'PositiveIntegerField': 'integer UNSIGNED',
    'PositiveSmallIntegerField': 'smallint UNSIGNED',
    'SlugField': 'varchar(%(max_length)s)',
    'SmallIntegerField': 'smallint',
    'TextField': 'longtext',
    'TimeField': 'time',
    'UUIDField': 'char(32)',

ORM常用字段参数

primary_key 	主键
verbose_name	注释
max_length		字段长度
max_digits     小数总共多少位
decimal_places	小数点后面的位数
auto_now		每次操作数据自动更新事件
auto_now_add	首次创建自动更新事件后续不自动更新
null			允许字段为空
default			字段默认值
unique			唯一值
db_index		给字段添加索引
choices			当某个字段的可能性能够被列举完全的情况下使用
	性别、学历、工作状态、...
	class User(models.Model):
        name = models.CharField(max_length=32)
        info = MyCharField(max_length=64)
        # 提前列举好对应关系
        gender_choice = (
            (1, '男性'),
            (2, '女性'),
            (3, '其他'),
        )
        gender = models.IntegerField(choices=gender_choice,null=True)
    user_obj = User.objects.filter(pk=1).first()
    user_obj.gender 
    user_obj.get_gender_display()
    
to				关联表
to_field		关联字段(不写默认关联数据主键)

on_delete

on_delete		当删除关联表中的数据时,当前表与其关联的行的行为。
    1、models.CASCADE
        级联操作,当主表中被连接的一条数据删除时,从表中所有与之关联的数据同时被删除
    2、models.SET_NULL
        当主表中的一行数据删除时,从表中所有与之关联的数据的相关字段设置为null,此时注意定义外键时,这个字段必须可以允许为空
    3、models.PROTECT
        当主表中的一行数据删除时,由于从表中相关字段是受保护的外键,所以都不允许删除
    4、models.SET_DEFAULT
        当主表中的一行数据删除时,从表中所有相关的数据的关联字段设置为默认值,此时注意定义外键时,这个外键字段应该有一个默认值
    5、models.SET()
        当主表中的一条数据删除时,从表中所有的关联数据字段设置为SET()中设置的值,与models.SET_DEFAULT相似,只不过此时从表中的相关字段不需要设置default参数
    6、models.DO_NOTHING
        什么都不做,一切都看数据库级别的约束,注数据库级别的默认约束为RESTRICT,这个约束与django中的models.PROTECT相似

choices参数(重点)

当某个字段的可能性能够被列举完全的情况下使用
比如:性别 学历 工作状态
先在类中写好对应关系:
image
然后gender这个字段就可以用整数类型存:
image
默认值的设置:
image

display

可以通过对应关系获取整数对应的字符串:
image
如这里pk=1的user_obj里gender字段为1,数字一对应的是男性,就可以通过display获取。

如果没有对应关系:
image
display方法就会返回数据库中存储的值
示例1:
image
使用这种方式,可以节省数据库空间,用更小的字段存储数据。
示例2:工作经验
image
示例3:生源由来
image

总结:相当于在对象中存储了更多的数据。

posted @ 2022-12-19 18:55  passion2021  阅读(201)  评论(0编辑  收藏  举报