ORM进阶操作

聚合查询

在ORM中也有相应的方法与MySQL中的聚合函数作用一致。

image

一共有五个聚合函数:

方法 作用
Max(字段) 返回字段中数据最大值
Min(字段) 返回字段中数据最小值
Sum(字段) 返回字段中数据总和
Count(字段) 返回字段中数据个数
Avg(字段) 返回字段中数据平均值

在使用以上五个聚合函数是需要先导入才能使用:

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

使用:统计Student表中score字段各项数据

res = models.Student.objects.aggregate(
    Max('score'),
    Min('score'),
    Sum('score'),
    Avg('score'),
    Count('score')
)

分组查询

ORM分组查询使用方法annotate()。

现有如下表:

  • 作者(编号,姓名,性别)
  • 书本(编号,书名,价格,作者编号)

以字段为单位分组

要求:统计作者中各个性别的人数。

res = models.Author.objects.values('sex').annotate(sex_count=Count('pk')).values('sex','sex_count')
print(res)

以表为单位做分组

要求:统计每个作者出的书本数量。

res = models.Author.objects.annotate(book_count=Count('book__pk')).values('name','book_count')
print(res)

上述代码表示对作者表进行分组,并对书本编号进行统计,annotate()中的book_count是自己起的别名。

要求:统计每个作者出的最便宜的价格。

res = models.Author.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
print(res)

F与Q查询

F方法

如果我们想要让两个字段进行比较筛选,或者是让字段数据在原来的基础上继续修改,这时就需要用到F()方法了。

导入:

from django.db.models import F

查询学生中数学成绩(math)高于英语(english)的学生:

res = models.Student.objects.filter(math__gt=F('english'))

把所有书本的价格提升10元:

models.Book.objects.update(price=F('price')+10)

把所有书本的后面加上"(爆款)"(字符串拼接需要额外两个方法):

from django.db.models import Value
from django.db.models.functions import Concat
models.Book.objects.update(name=Concat(F('name'), Value('(爆款)')))

Q方法

filter()方法可以填写多个条件,每个条件逗号隔开,条件之间默认是and关系,无法直接修改。

如果想要让多个条件之间有更多的关系(比如or、not),需要用到Q方法。

使用Q方法后,,(逗号)隔开代表and,|(管道符)隔开代表or,~(波浪号)隔开代表not。

导入:

from django.db.models import Q

查询书本价格(price)大于50或者数量(quantity)大于50的书本

res = models.Book.objects.filter(
    Q(price__gt=50) | Q(quantity__gt=50)
)

查询书本名称(name)不包含'J'的书本:

res = models.Book.objects.filter(
    ~Q(name__contains='J')
)

进阶

我们都知道,filter()内部不能使用字符串传参,比如filter('price'=50)这种,也就是说我们无法让用户自己输入字段名和字段值筛选数据,但是使用Q方法就可以做到。

q_obj = Q()  # 获取Q对象
field = input('请输入字段名:')
value = input('请输入字段值:')
q_obj.children.append((field,value))  # 添加条件
res = models.Book.objects.filter(q_obj)  # 筛选
print(res)

还可以设置条件之间的关系:

q_obj = Q()
q_obj.connector = 'or'  # 用于表示条件之间的关系,默认为and
q_obj.children.append(('price__gt',50))
q_obj.children.append(('quantity__gt',50))
res = models.Book.objects.filter(q_obj)
print(res.query)

ORM查询优化

1.ORM查询默认都是惰性查询(能不消耗数据库资源就不消耗)

编写ORM语句并不会直接使用SQL语句,只有后续的代码用到了才会执行。

image

2.ORM查询默认自带分页功能(尽量减轻单次查询数据的压力)

每次执行SQL都会限制查询数据数量。
image

除了values()可以查询指定字段外,还有only()、defer()、select_related()、prefetch_related()。

only()与defer()

only()

only()返回对象结果集,获取only()中指定的字段不会再次执行SQL语句,但是如果获取没有指定的字段,会去执行SQL语句获取结果。

res = models.Book.objects.only('name','price')
for i in res:
    print(i.quantity)  # 获取没有指定的字段

image

defer()

defer()与only()刚好相反,获取defer()中指定的字段会再次执行SQL语句获取结果,但是如果获取没有指定的字段,不会去执行SQL语句。

res = models.Book.objects.defer('name','price')
for i in res:
    print(i.name)  # 获取指定的字段

image

select_related()

select_related()括号内只能传一对一和一对多字段,不能传多对多字段或者其他字段。

相当于连表操作,使用join一次性连接之后的大表中所有的数据全部封装到数据对象中,后续使用对象跨表查询都不会执行SQL语句。

res = models.Book.objects.select_related('author')
for i in res:
    print(i.name)
    print(i.author.name)

image

prefetch_related()

prefetch_related()括号内只能传一对一、一对多、多对多字段,不能传其他字段。

相当于子查询,会分开操作查询语句,将多次查询之后的结果封装到数据对象中,后续使用对象跨表查询都不会执行SQL语句。

res = models.Book.objects.prefetch_related('author')
for i in res:
    print(i.name)
    print(i.author.name)

image

ORM常见字段

常见字段 描述
AutoField() 自动增长int类型
CharField(max_length=) varchar类型,必须提供max_length参数指定长度
IntergerField() int类型
DecimalField(max_digits=, decimal_places=) decimal类型,总位数max_digits,小数位数decimal_places
DateField() date类型,提供auto_now和auto_now_add参数
DateTimeField() datetime类型,提供auto_now和auto_now_add参数
BigIntergerField() bigint类型
BooleanField() 布尔值,存0和1
TextField() 存储大段文本
FileField() 传文件时会自动保存到指定位置并只存文件路径
EmailField() varchar(254)
ForeignKey(to=) 一对多外键关系,to指定有关系的表
OneToOneField(to=) 一对一外键关系,to指定有关系的表
ManyToManyField(to=) 多对多外键关系,to指定有关系的表

自定义字段类型

自定义一个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

字段重要参数

重要参数 描述
primary_key 是否为主键
null 是否可以为空
unique 是否唯一
db_index 是否设置索引
default 设置默认值
max_length 设置长度
verbose_name 给字段起别名
choices 给定一些选择,有对应关系就拿,没有还是本身
日期字段参数
auto_now 是否每次更新数据时都要更新该字段
auto_now_add 是否添加数据时将当前时间添加到该字段
Decimal字段参数
max_digits 设置Decimal字段总位数
decimal_places 设置Decimal字段小数位数
外键字段
to 设置要关联的表
to_fileld 设置要关联的表的字段
on_delete 设置当删除关联表中的数据时,当前表与其关联的行的行为
db_constraint 是否在数据库中创建外键约束,默认为True

choices参数

设置

class User(models.Model):
    gender_choice = (
        (1, '男性'),
        (2, '女性')
    )
    gender = models.IntegerField(choices=gender_choice)

取值的时候使用对象.get_xxx_display()来获取choices后面具体的值,当choices中没有该数据对应的时候获取的仍是数字自己

User表

id gender
1 1
2 4
user_obj = models.Userinfo.objects.get(pk=1)
print(user_obj.get_gender_display())  # 男

user_obj = models.Userinfo.objects.get(pk=2)
print(user_obj.get_gender_display())   # 4

on_delete参数

on_delete有如下选择:

on_delete值 描述
models.CASCADE 删除关联数据,与之关联也删除
models.DO_NOTHING 删除关联数据,引发错误IntegrityError
models.PROTECT 删除关联数据,引发错误ProtectedError
models.SET_NULL 删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
models.SET_DEFAULT 删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
models.SET 删除关联数据,与之关联的值设置为指定值或是可执行对象的返回值

事务操作

使用:

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

with上下文管理开始执行代表事务开始,中间报错自动回滚,结束with语句自动提交事务。

ORM执行原生SQL

方式一:

from django.db import connection, connections
cursor = connection.cursor()  
cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
cursor.fetchone()

方式二:

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

多对多三种创建方式

全自动

orm自动创建第三张表,但是无法扩展第三张表的字段。

class Student(models.Model):
	courses = models.ManyToManyField(to='Course')

全手动

优势在于第三张表完全自定义扩展性高,劣势在于无法使用外键方法和正反向查询。

class Student(models.Model):
    title = models.CharField(max_length=32)
class Course(models.Model):
    name = models.CharField(max_length=32)
class Student2Course(models.Model):
    student = models.ForeignKey(to='Student')
    course = models.ForeignKey(to='Course')

半自动

正反向查询还可以使用,并且第三张表可以扩展,唯一的缺陷是不能用add\set\remove\clear四个方法。

class Student(models.Model):
    name = models.CharField(max_length=32)
    courses = models.ManyToManyField(
        to='Course',
        through='Student2Course',  # 指定表
        through_fields=('student', 'course')  # 指定字段,注意顺序
    )
class Course(models.Model):
    name = models.CharField(max_length=32)
class Student2Course(models.Model):
    student = models.ForeignKey(to='Student')
    course = models.ForeignKey(to='Course')

多对多建在任意一方都可以,如果建在Course表,字段顺序互换即可。

posted @ 2022-05-18 19:04  Yume_Minami  阅读(98)  评论(0编辑  收藏  举报