Django ORM表查询操作

Posted dominic-ji

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django ORM表查询操作相关的知识,希望对你有一定的参考价值。

单表查询之神奇的双下划线

技术分享图片
models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值
 
models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in
 
models.Tb1.objects.filter(name__contains="ven")  # 获取name字段包含"ven"的
models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
 
models.Tb1.objects.filter(id__range=[1, 3])      # id范围是1到3的,等价于SQL的bettwen and
 
类似的还有:startswith,istartswith, endswith, iendswith 

date字段还可以:
models.Class.objects.filter(first_day__year=2017)
date字段可以通过在其后加__year,__month,__day等来获取date的特点部分数据
# date
        #
        # Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
        # Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))

        # year
        #
        # Entry.objects.filter(pub_date__year=2005)
        # Entry.objects.filter(pub_date__year__gte=2005)

        # month
        #
        # Entry.objects.filter(pub_date__month=12)
        # Entry.objects.filter(pub_date__month__gte=6)

        # day
        #
        # Entry.objects.filter(pub_date__day=3)
        # Entry.objects.filter(pub_date__day__gte=3)

        # week_day
        #
        # Entry.objects.filter(pub_date__week_day=2)
        # Entry.objects.filter(pub_date__week_day__gte=2)
需要注意的是在表示一年的时间的时候,我们通常用52周来表示,因为天数是不确定的,老外就是按周来计算薪资的哦~
集锦

ForeignKey操作

正向查找(两种方式)

1.对象查找(跨表)

语法:

对象.关联字段.字段

要点:先拿到对象,再通过对象去查对应的外键字段,分两步

示例:

book_obj = models.Book.objects.first()  # 第一本书对象(第一步)
print(book_obj.publisher)  # 得到这本书关联的出版社对象
print(book_obj.publisher.name)  # 得到出版社对象的名称

2.字段查找(跨表)

语法:

关联字段__字段

要点:利用Django给我们提供的神奇的双下划线查找方式

示例:

models.Book.objects.all().values("publisher__name")
#拿到所有数据对应的出版社的名字,神奇的下划线帮我们夸表查询

反向操作(两种方式)

1.对象查找

语法:

obj.表名_set

要点:先拿到外键关联多对一,一中的某个对象,由于外键字段设置在多的一方,所以这里还是借用Django提供的双下划线来查找

示例:

publisher_obj = models.Publisher.objects.first()  # 找到第一个出版社对象
books = publisher_obj.book_set.all()  # 找到第一个出版社出版的所有书
titles = books.values_list("title")  # 找到第一个出版社出版的所有书的书名

结论:如果想通过一的那一方去查找多的一方,由于外键字段不在一这一方,所以用__set来查找即可

2.字段查找

语法:

表名__字段

要点:直接利用双下滑线完成夸表操作

titles = models.Publisher.objects.values("book__title")

ManyToManyField

class RelatedManager

"关联管理器"是在一对多或者多对多的关联上下文中使用的管理器。

它存在于下面两种情况:

  1. 外键关系的反向查询
  2. 多对多关联关系

简单来说就是在多对多表关系并且这一张多对多的关系表是有Django自动帮你建的情况下,下面的方法才可使用。

方法

create()

创建一个关联对象,并自动写入数据库,且在第三张双方的关联表中自动新建上双方对应关系。

models.Author.objects.first().book_set.create(title="偷塔秘籍")
上面这一句干了哪些事儿:
1.由作者表中的一个对象跨到书籍比表
2.新增名为偷塔秘籍的书籍并保存
3.到作者与书籍的第三张表中新增两者之间的多对多关系并保存

add()

把指定的model对象添加到第三张关联表中。

添加对象

>>> author_objs = models.Author.objects.filter(id__lt=3)
>>> models.Book.objects.first().authors.add(*author_objs)

添加id

>>> models.Book.objects.first().authors.add(*[1, 2])

set()

更新某个对象在第三张表中的关联对象。不同于上面的add是添加,set相当于重置

>>> book_obj = models.Book.objects.first()
>>> book_obj.authors.set([2, 3])

remove()

从关联对象集中移除执行的model对象(移除对象在第三张表中与某个关联对象的关系)

>>> book_obj = models.Book.objects.first()
>>> book_obj.authors.remove(3)

clear()

从关联对象集中移除一切对象。(移除所有与对象相关的关系信息)

>>> book_obj = models.Book.objects.first()
>>> book_obj.authors.clear()

注意:

对于ForeignKey对象,clear()和remove()方法仅在null=True时存在。

举个例子:

ForeignKey字段没设置null=True时,

class Book(models.Model):
    title = models.CharField(max_length=32)
    publisher = models.ForeignKey(to=Publisher)

没有clear()和remove()方法:

>>> models.Publisher.objects.first().book_set.clear()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AttributeError: RelatedManager object has no attribute clear

当ForeignKey字段设置null=True时,

class Book(models.Model):
    name = models.CharField(max_length=32)
    publisher = models.ForeignKey(to=Class, null=True)

此时就有clear()和remove()方法:

>>> models.Publisher.objects.first().book_set.clear()

再次强调:

  1. 对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。

聚合查询和分组查询

聚合(利用聚合函数)

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

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

用到的内置函数:

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

示例:

>>> from django.db.models import Avg, Sum, Max, Min, Count
>>> models.Book.objects.all().aggregate(Avg("price"))
{price__avg: 13.233333}

如果你想要为聚合值指定一个名称,可以向聚合子句提供它。

>>> models.Book.objects.aggregate(average_price=Avg(price))
{average_price: 13.233333}

如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:

>>> models.Book.objects.all().aggregate(Avg("price"), Max("price"), Min("price"))
{price__avg: 13.233333, price__max: Decimal(19.90), price__min: Decimal(9.90)}

分组

我们在这里先复习一下SQL语句的分组。

假设现在有一张公司职员表:

技术分享图片

 我们使用原生SQL语句,按照部分分组求平均工资:

select dept,AVG(salary) from employee group by dept;

ORM查询:

from django.db.models import Avg
Employee.objects.values("dept").annotate(avg=Avg("salary").values(dept, "avg")
这里需要注意的是annotate分组依据就是他前面的值,
如果前面没有特点的字段,则默认按照ID分组,
这里有dept字段,所以按照dept字段分组

连表查询的分组:

技术分享图片

SQL查询:

select dept.name,AVG(salary) from employee inner join dept on (employee.dept_id=dept.id) group by dept_id;

ORM查询:

from django.db.models import Avg
models.Dept.objects.annotate(avg=Avg("employee__salary")).values("name", "avg")

更多示例:

示例1:统计每一本书的作者个数

>>> book_list = models.Book.objects.all().annotate(author_num=Count("author"))
>>> for obj in book_list:
...     print(obj.author_num)
...
1

示例2:统计出每个出版社买的最便宜的书的价格

>>> publisher_list = models.Publisher.objects.annotate(min_price=Min("book__price"))
>>> for obj in publisher_list:
...     print(obj.min_price)
...     
9.90
19.90

方法二:

>>> models.Book.objects.values("publisher__name").annotate(min_price=Min("price"))
<QuerySet [{publisher__name: 沙河出版社, min_price: Decimal(9.90)}, {publisher__name: 人民出版社, min_price: Decimal(19.90)}]>

示例3:统计不止一个作者的图书

>>> models.Book.objects.annotate(author_num=Count("author")).filter(author_num__gt=1)
<QuerySet [<Book: 番茄物语>]>

示例4:根据一本图书作者数量的多少对查询集 QuerySet进行排序

>>> models.Book.objects.annotate(author_num=Count("author")).order_by("author_num")
<QuerySet [<Book: 香蕉物语>, <Book: 橘子物语>, <Book: 番茄物语>]>

示例5:查询各个作者出的书的总价格

技术分享图片
>>> models.Author.objects.annotate(sum_price=Sum("book__price")).values("name", "sum_price")
<QuerySet [{name: 小精灵, sum_price: Decimal(9.90)}, {name: 小仙女, sum_price: Decimal(29.80)}, {name: 小魔女, sum_price: Decimal(9.90)}]>
View Code

 总结

value里面的参数对应的是sql语句中的select要查找显示的字段,

filter里面的参数相当于where或者having里面的筛选条件

annotate本身表示group by的作用,前面找寻分组依据,内部放置显示可能用到的聚合运算式,后面跟filter来增加限制条件,最后的value来表示分组后想要查找的字段值

技术分享图片

 

以上是关于Django ORM表查询操作的主要内容,如果未能解决你的问题,请参考以下文章

Django-ORM-多表操作

八Django的orm之多表操作

Django ORM 操作 必知必会13条 单表查询

django-模型层(ORM语法)

django-模型层(ORM语法)

Django ORM表查询操作