第四章 python Web django学习入门之 ORM Manager、QuerySet、Model 用法及介绍

系列文章目录

第一章 python Web django学习入门之创建人生中第一个django项目

第二章 python Web django学习入门之settings 配置详解,路由详解,响应数据 !

第三章 python Web django学习入门之 模板渲染,静态文件,模型数据库,admin控制台



在这里插入图片描述

前言

一直很忙也没太多时间去做总结,以及知识的分享,今天正好想起来我还有系列文章没做更新,就带大家一起来讲解学习一下Python Web Django 的 ORM的相关知识,本着学习记录的心态, 文章仅代表个人见解,有不同看法的同学留言我们相互学习。


一、Django ORM 是什么?

  • 之前有说到ORM用作于与实际存储库的对象关系映射。那么我们来认识一下Django ORM用到三个类:Manager、QuerySet、Model。

1.1 Manager 介绍

Manager管理器是定义表级方法(表级方法就是影响一条或多条记录的方法)的,我们可以以models.Manager为父类,定义自己的manager,增加表级方法。例如有一个定义的Books表, 那么 Books.objects.all()中的 objects是一个特殊的属性,可以通过它来进行数据库查询,它就是模型的一个Manager且每一表表都会自有一个Manager。

1.2 Manager 的使用

就例如我们在使用过程中为Book书籍的分类去做数量的统计该如何去实现呢?

代码(示例):

from django.db import models


# 自定义模型管理器类统计当前分类中书籍的数量
class BooksModelManager(models.Manager):
    # 自定义模型管理器中的方法
    def category_count(self, field):
    	# 返回一个统计结果, self 代表继承表本身也就是Book, filter()为自带方法
        return self.filter(category_id=field).count()


class Books(models.Model):
	CATE_ID = (
		(1, "文学书籍"),
		(2, "漫画书籍")
	)
	title = models.CharField(max_length=50, verbose_name='书籍名称')
    category_id = models.SmallIntegerField(choices=CATE_ID, verbose_name='书籍分类(枚举)')
    ...省略其他字段信息
    # 将BooksModelManager()赋值给模型的objects属性,它将替代模型的默认objects,并在拥有原因方法的同时也新增我们自定义的方法
    objects = BooksModelManager()
    class Meta:
        db_table = 'tb_books'
    def __str__(self):
        return self.title

那么自定义统计与Django原始的统计分别是怎么使用的呢?

# 自定义manager方法的使用
 Books.objects.category_count(1)
 
# django原始查询方法
Books.objects.filter(category_id=1).count()

2.1 QuerySet 介绍

QuerySet:Manager类的一些方法会返回QuerySet实例,QuerySet是一个可遍历结构,包含一个或多个元素,每个元素都是一个Model 实例,它里面的方法也是表级方法,前面说了,Django给我们提供了增加表级方法的途径,那就是自定义manager类,而不是自定义QuerySet类,一般的我们没有自定义QuerySet类的必要。

3.1 Model 介绍

django.db.models模块中的Model类,我们定义表的model时,就是继承它,它的功能很强大,通过自定义model的instance可以获取外键实体等,它的方法都是记录级方法(都是实例方法,貌似没有类方法),不要在里面定义类方法,比如计算记录的总数,查看所有记录,这些应该放在自定义的manager类中。

每个Model都有一个默认的manager实例(前面也说了),名为objects,QuerySet有两种来源:通过manager的方法得到、通过QuerySet的方法得到。
mananger的方法和QuerySet的方法大部分同名,同意思,如filter(), update()等,但也有些不同,如manager有create()get_or_create(),而QuerySet有delete()等,看源码就可以很容易的清楚Manager类与Queryset类的关系,Manager类的绝大部分方法是基于Queryset的。
一个QuerySet包含一个或多个model instance。
QuerySet类似于Python中的list,list的一些方法QuerySet也有,比如切片,遍历等。

3.2 介绍Model QuerySet 的相关用法

3.2.1. 查看 Django queryset 执行的 SQL

print(str(Books.objects.all().query))
# 结果:
SELECT "tb_books"."id", "tb_books"."title", "tb_books"."category_id" FROM "tb_books"
简化版: SELECT "id", "title", "category_id" FROM "tb_books"

所以,当不知道Django做了什么时,你可以把执行的 SQL 打出来看看,也可以借助 django-debug-toolbar 等工具在页面上看到访问当前页面执行了哪些SQL,耗时等。

3.2.2. values_list() 获取元组形式结果

books = Books.objects.values_list("title", "category_id")
print(books)
# 结果:
# 得到的是一个querSet 可直接list强转类型
<QuerySet [('火影忍者', 2), ('狂人日记', 1)]>
# 如果只需要 1 个字段,可以指定 flat=True
books = Books.objects.values_list("title", flat=True)
print(books)
# 结果:
<QuerySet ['火影忍者', '狂人日记']>

3.2.3. values() 获取字典形式的结果

books = Books.objects.values("title", "category_id")
print(books)
# 结果:
# 得到的是一个querySet 可直接list强转类型
<QuerySet [{
    
    'title': '火影忍者', 'category_id': 2}, {
    
    'title': '狂人日记', 'category_id': 1}]>

注意:

  1. values_list 和 values 返回的并不是真正的 列表 或 字典,也是 queryset,他们也是 lazy evaluation 的(惰性评估,通俗地说,就是用的时候才真正的去数据库查)

  2. 如果查询后没有使用,在数据库更新后再使用,你发现得到在是新内容!!!如果想要旧内容保持着,数据库更新后不要变,可以 list 一下

  3. 如果只是遍历这些结果,没有必要 list 它们转成列表(浪费内存,数据量大的时候要更谨慎!!!)

3.2.4. extra() 实现 别名,条件,排序等 主要实现别名用法

比如 Books表中有 name, 我们想执行以下面的sql语句:
SELECT name AS author_name FROM tb_books;在django中怎么实现
这样的语句,就可以用 select 来实现,如下:

books = Books.objects.all().extra(select={
    
    'author_name': 'name'})
print(books[0].name)
print(books[0].author_name)
# 两者结果一致
我们用query打印以下sql的执行
books = Books.objects.all().extra(select={
    
    'author_name': 'name'}).query.__str__()
print(books)
# 得到的结果
简化版: SELECT (name) AS "author_name", "id", "title", "name", "category_id" FROM "tb_books"

我们发现查询的时候弄了两次 (name) AS “author_name” 和 “name”, 如果我们只想其中一个能用怎么排除掉另一个字段呢? 往下看。

3.2.4. defer() 字段排除

拿上一条举列排除掉name

books = Books.objects.all().extra(select={
    
    'author_name': 'name'}).defer('name').query.__str__()
# 查看sql执行(从结果看name是不是被排除掉了)
SELECT (name) AS "author_name", "id", "title", "category_id" FROM "tb_books"

3.2.5. filter() 与 exclude() 排除过滤查询

# 查询分类id为1的记录, books, 为可迭代对象遍历可取出每一条记录信息
books = Books.objects.filter(category_id=1)
# 排除掉查询分类id为1的记录
books = Books.objects.exclude(category_id=1)

filter( )中存在两个特殊的对象, Q 与 F 传送门: Q与F的简单应用

3.2.6. first() 与 last() 对多条查询记录取第一条与最后一条

# 以上一个例子为例取第一条(得到的就是个对象了, 可以直接获取属性值)
books.first()
# 取最后一条也一样
books.last()

3.2.7. aggregate() 与 annotate() 特殊查询方法用于聚合查询

  • aggregate(): aggregate()⽅法作⽤是对⼀组值(⽐如queryset的某个字段)进⾏统计计
    算,并以字典(Dict)格式返回统计计算结果。
    django的aggregate⽅法⽀持的聚合操作有AVG / COUNT / MAX / MIN /SUM 等。

使用常用的统计函数来讲解相关 aggregate() 用法

from django.db.models import Count, Sum, Avg
# 例如我要对books的 销量: sales_value字段求和, 得出总销售量的值
books = Books.objects.aggregate(Sum("sales_value"))
print(books)
# 结果为字典可直接通过key取值:
{
    
    "sales_value__sum": 20001}
# 再试试使用平均值的方法求得每本书籍平均销量
books = Books.objects.aggregate(Avg("sales_value"))
print(books)
# 结果为字典可直接通过key取值:
{
    
    "sales_value__avg": 6278}
"""
由此简单的印证得出结论: aggregate() 结合聚合函数统计出的结果都会默认在统计字段名后加上`__sum`, `__avg`等. 当然我们也可以给它起一个新的名称去接收它
"""
books = Books.objects.aggregate(sales_avg=Avg("sales_value"))
print(books)
# 结果:
{
    
    "sales_avg": 6278}
  • annotate() : 如果你想要对数据集先进⾏分组然后再进⾏某些聚合操作或排序时,需要使⽤annotate⽅法来实现。
    与aggregate⽅法不同的是,annotate⽅法返回结果的不在是含有统计结
    果的⼀个字典,⽽是包含有新增统计字段的查询集(queryset)。

再来看看annotate()方法的使用与不同

from django.db.models import Count, Sum, Avg
# 例如我要对books的 销量: sales_value字段求和, 得出总销售量的值
books = Books.objects.annotate(Sum("sales_value"))
print(books)
# 结果不再是字典而是一个全新的querySet并且会带上我们新统计出的字段:
<QuerySet [('sales_value__sum', 20001), ('title', '狂人日记'), ('category_id', 1)]>
当然它要想换一个名称也和上面一样就可以了

3.2.8. order_by() 与 distinct() 关键字段排序 与 去重

  • order_by(): 返回一个新的QuerySet实例,其顺序已更改。
# 例如我们以销量字段进行正序排序
books = Books.objects.order_by("sales_value")
# 也可进行多字段排序
books = Books.objects.order_by("sales_value", "id")
# 也可以进行倒序排列在前面加上 `-`横杆
books = Books.objects.order_by("-sales_value")
  • distinct(): 返回一个新的QuerySet实例,它将只选择不同的结果。
# 例如我们要统计出销量为200的书籍作者(并要求作者唯一)
books = Books.objects.filter(sales_value=200).values_list("author").distinct()

注意点 官方文档提示要达到去重的效果需要结合order_by()使用, 不然实际数据没有达到去重的效果


总结

以上就是今天要讲的内容,本文仅仅简单介绍了Django web的使用,而django ORM 提供了大量能使我们快速便捷地处理数据的函数和方法。乘风破浪会有时, 学习的路上不能停下来.

猜你喜欢

转载自blog.csdn.net/Return_Li/article/details/124799527