django模型层(orm相关知识点)


一、模型层之前期准备

模型层的了解

模型(Model)负责业务是对象和数据库的关系映射(ORM),即对象关系映射。

ORM是“对象-关系-映射”的简称,主要任务是:

  1. 建立模型类和表之间的对应关系,允许我们通过面向对象的方式来操作数据库。
  2. 将对象、列表的操作,转换为sql语句。
  3. 根据设计的模型类生成数据库中的表格。
  4. 将sql查询到的结果转换为对象、列表

image

而我们的models.py主要负责程序中用于处理数据逻辑的部分(如数据的存取)。它包含你所储存数据的必要字段和行为。通常,每个模型对应数据库中唯一的一张表。

模型

我们知道了模型层的作用,你有没有想过模型是一个什么东西呢?下面带领大家一起来学习。

什么是模型?

  1. 模型是一个Python类,它是由django.db.models.Model派生出的子类。
  2. 一个模型类代表数据库的一张数据表。
  3. 模型类中的每一个属性都代表数据库中的一个字段。
  4. 模型是数据交互的接口,是表示和操作数据库的方法和方式。

image

模型层的前置知识点

为什么要将Django自带的sqlite3数据库替换成MySQL?

Django自带的sqlite3数据库对时间字段不敏感,有时候会展示错乱,所以我们习惯切换成常见的数据库比如MySQL。

ps:django的ORM并不会自动帮我们自动创建库,所以需要提前准备好。

如何单独测试Django的某个功能层

这里我们说的功能层,指代的就是代表某一层的py文件,通常来说别的功能层的py文件并不能单独测试,彼此都是有关联的。这里我们主要指代的还是模型层的my文件models.py。

默认情况下Django并不允许单独测试某个py文件

创建测试环境的方式一:

pycharm提供的python console

创建测试环境的方式二:

自己搭建(可以使用应用中自带的test.py文件或者自己创建一个新的py文件)

在打开对应的py文件后,先拷贝manage.py前四行

image

接着我们再添加两行代码

import django
django.setup()

image

django的orm底层还是SQL语句,我们是可以查看的。

临时查看的方法

如果我们手上是一个QuerySet对象 那么可以直接点query查看SQL语句

image

Django终端打印SQL语句

如果想查看所有orm底层的SQL语句也可以在配置文件添加日志记录

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

二、ORM常用关键字

1.create()

创建数据并直接获取当前创建的数据对象

    res = models.User.objects.create(name='阿兵', age=28)
    res = models.User.objects.create(name='oscar', age=18)
    res = models.User.objects.create(name='jerry', age=38)
    res = models.User.objects.create(name='jack', age=88)
    print(res)

2.filter()

根据条件筛选数据,结果是QuerySet [数据对象1,数据对象2]

    res = models.User.objects.filter()
    res = models.User.objects.filter(name='jason')
    res = models.User.objects.filter(name='jason', age=19)  # 括号内支持多个条件但是默认是and关系

3.first()、last()

QuerySet支持索引取值但是只支持正数 并且orm不建议你使用索引

    res = models.User.objects.filter()[1]
    res = models.User.objects.filter(pk=100)[0]  # 数据不存在索引取值会报错
    res = models.User.objects.filter(pk=100).first()  # 数据不存在不会报错而是返回None
    res = models.User.objects.filter().last()  # 数据不存在不会报错而是返回None

4.update()

更新数据(批量更新)

    models.User.objects.filter().update()     批量更新
    models.User.objects.filter(id=1).update() 单个更新

5.delete()

删除数据(批量删除)

    models.User.objects.filter().delete()      批量删除
    models.User.objects.filter(id=1).delete()  单个删除

6.all()

查询所有数据,结果是QuerySet [数据对象1,数据对象2]

res = models.User.objects.all()

7.values()

根据指定字段获取数据,结果是QuerySet [{},{},{},{}]

    res = models.User.objects.all().values('name')
    res = models.User.objects.filter().values()
    res = models.User.objects.values()

8.values_list()

根据指定字段获取数据,结果是QuerySet [(),(),(),()]

res = models.User.objects.all().values_list('name','age')

9.distinct()

去重 数据一定要一模一样才可以 如果有主键肯定不行

res = models.User.objects.values('name','age').distinct()

10.order_by()

根据指定条件排序 默认是升序 字段前面加负号就是降序

res = models.User.objects.all().order_by('age')
print(res)

11.get()

根据条件筛选数据并直接获取到数据对象 一旦条件不存在会直接报错 不建议使用

    res = models.User.objects.get(pk=1)
    print(res)
    res = models.User.objects.get(pk=100, name='jason')
    print(res)

12.exclude()

取反操作

    res = models.User.objects.exclude(pk=1)
    print(res)

13.reverse()

颠倒顺序(被操作的对象必须是已经排过序的才可以)

    res = models.User.objects.all()
    res = models.User.objects.all().order_by('age')
    res1 = models.User.objects.all().order_by('age').reverse()
    print(res, res1)

14.count()

统计结果集中数据的个数

    res = models.User.objects.all().count()
    print(res)

15.exists()

判断结果集中是否含有数据 如果有则返回True 没有则返回False

    res = models.User.objects.all().exists()
    print(res)
    res1 = models.User.objects.filter(pk=100).exists()
    print(res1)

三、ORM执行SQL语句

django中的ORM提供的操作功能有限,在模型提供的查询API不能满足实际工作需要时,可以在ORM中直接执行原生sql语句。

Django 提供两种方法使用原生SQL进行查询:

一种是使用raw()方法,进行原生SQL查询并返回模型实例;

另一种是完全避开模型层,直接执行自定义的SQL语句。

方式1:

	 models.User.objects.raw('select * from app01_user;')

image

方式2:

    from django.db import connection
    cursor = connection.cursor()
    cursor.execute('select name from app01_user;')
    print(cursor.fetchall())

image

四、神奇的双下划线查询

我们在使用ORM操作对数据进行操作的时候,结果通常都是queryset对象,只要结果还是queryset对象就可以无限制的点queryset对象能使用的各种方法。例:

queryset.filter().values().filter().values_list().filter()...

我们在使用ORM操作数据库的时候,会发现一些符号、一些查找条件并不能跟sql语句中一样直接用,比如大于号小于号等符号,如果我们想要在ORM中使用这些条件,需要使用双下滑线查询方法。

常见的双下划线查询方法

方法 描述
__gt 大于
__lt 小于
__gte 大于等于
__lte 小于等于
__in
__range 取指定范围内对应数据,并且首尾都要
__contains 模糊查询,查询出指定字符数据,区分大小写
__icontains 忽略大小写
__startswith 查询以指定字符开头数据
__endswith 查询以指定字符结尾数据

代码实操:

    # 查询年龄大于18的用户数据
    # res = models.User.objects.filter(age__gt=18)
    # print(res)
    # 查询年龄小于38的用户数据
    # res = models.User.objects.filter(age__lt=38)
    # print(res)
    # 大于等于  小于等于
    # res = models.User.objects.filter(age__gte=18)
    # res = models.User.objects.filter(age__lte=38)
    # 查询年龄是18或者28或者38的数据
    # res = models.User.objects.filter(age__in=(18, 28, 38))
    # print(res)
    # 查询年龄在18到38范围之内的数据
    # res = models.User.objects.filter(age__range=(18, 38))
    # print(res)
    # 查询名字中含有字母j的数据
    # res = models.User.objects.filter(name__contains='j')  # 区分大小写
    # print(res)
    # res = models.User.objects.filter(name__icontains='j')  # 不区分大小写
    # print(res)
    # 查询注册年份是2022的数据
    # res = models.User.objects.filter(register_time__year=2022)
    # print(res)
    '''针对django框架的时区问题 是需要配置文件中修改的 后续bbs讲解'''

五、ORM外键字段的创建

复习MySQL外键关系

一对多
	外键字段建在多的一方
多对多
	外键字段统一建在第三张关系表
一对一
	建在任何一方都可以 但是建议建在查询频率较高的表中

ps:关系的判断可以采用换位思考原则 熟练的之后可以瞬间判断

外键字段的创建

1.创建基础表(书籍表、出版社表、作者表、作者详情)

代码在下方

2.确定外键关系

一对多 ORM与MySQL一致 外键字段建在多的一方

多对多 ORM比MySQL有更多变化

	1.外键字段可以直接建在某张表中(查询频率较高的)

		内部会自动帮你创建第三张关系表

	2.自己创建第三张关系表并创建外键字段

		详情后续讲解

一对一 ORM与MySQL一致 外键字段建在查询较高的一方

3.ORM创建

针对一对多和一对一的外键字段,同步到表中之后会自动加_id的后缀。

即ForeignKey 和 OneToOneField,会自动给字段加_id后缀

    	publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)
	 	author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
    针对多对多 不会在表中有展示 而是创建第三张表
    	authors = models.ManyToManyField(to='Author')   

4.完整的创建代码

from django.db import models

# Create your models here.


class Book(models.Model):
    """图书表"""
    title = models.CharField(max_length=32, verbose_name='书名')
    price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='价格')
    # 八位整数,两位小数
    publish_time = models.DateField(auto_now_add=True, verbose_name='出版日期')
    # 这里要区别auto_now和auto_now_add的区别,auto_now在创建时会将创建时间当成数据记录,在修改的时候会用修改时间进行数据记录
    #而auto_now_add只会记录创建的时间信息,修改的时候不会进行记录

    # 创建书籍与出版社的一对多外键字段
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    '''django1.x版本外键字段默认都是级联更新删除 django2.x及以上需要自己申明  on_delete=models.XXX '''
    # 创建书籍与作者的多对多外键字段
    authors = models.ManyToManyField(to='Author')
    '''多对多字段为虚拟字段,用于告诉ORM自动创建第三张表,数据库迁移(也就是创建了对应的表)之后,并没有这个字段,但是会有一张新的表'''

    # def __str__(self):
    #     return f'书籍对象:{self.title}'



class Publish(models.Model):
    """出版社表"""
    name = models.CharField(max_length=32, verbose_name='名称')
    address = models.CharField(max_length=64, verbose_name='地址')

    # def __str__(self):
    #     return f'出版社对象:{self.name}'



class Author(models.Model):
    """作者表"""
    name = models.CharField(max_length=32, verbose_name='姓名')
    age = models.IntegerField(verbose_name='年龄')
    # 创建作者与作者详情的一对一外键字段
    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
    # on_delete就是设置外键的级联更新,这里的to的值是创建外键连接的表的名称,如果不写称字符串就会出现查找顺序的问题

    # def __str__(self):
    #     return f'作者对象:{self.name}'



class AuthorDetail(models.Model):
    """作者详情表"""
    phone = models.BigIntegerField(verbose_name='手机号')
    address = models.CharField(max_length=64, verbose_name='家庭住址')

    # def __str__(self):
    #     return f'详情对象:{self.address}'

ps:模型层写好还要进行数据库迁移

六、外键字段相关操作

数据准备

book表中的数据是用下方的代码创建的,多对多外键创建的表不用手动创建数据,其他的数据需要我们手动创建。因为创建book中的数据的时候会用到别的表中的字段值。

image

image

image

image

    # 针对一对多 插入数据可以直接填写表中的实际字段
    models.Book.objects.create(title='三国演义', price=888.88, publish_id=1)
    models.Book.objects.create(title='人性的弱点', price=777.55, publish_id=1)
    # 针对一对多 插入数据也可以填写表中的类中字段名
    publish_obj = models.Publish.objects.filter(pk=1).first()
    models.Book.objects.create(title='水浒传', price=555.66, publish=publish_obj)
    '''一对一与一对多一致,既可以传数字也可以传对象'''
    
    
    # 针对多对多关系绑定
    # 添加记录
    book_obj = models.Book.objects.filter(pk=1).first()
    book_obj.authors.add(1)  # 在第三张关系表中给当前书籍绑定作者

    book_obj = models.Book.objects.filter(pk=1).first()
    book_obj.authors.add(2, 3)  # 书和作者的外键是authors

    book_obj = models.Book.objects.filter(pk=3).first()
    author_obj1 = models.Author.objects.filter(pk=1).first()
    author_obj2 = models.Author.objects.filter(pk=2).first()
    book_obj.authors.add(author_obj1)

    book_obj = models.Book.objects.filter(pk=4).first()
    author_obj1 = models.Author.objects.filter(pk=1).first()
    author_obj2 = models.Author.objects.filter(pk=2).first()
    book_obj.authors.add(author_obj1, author_obj2)

    # 修改记录
    # 这里是用值修改
    book_obj = models.Book.objects.filter(pk=1).first()
    book_obj.authors.set((1, 3))  # 修改关系

    book_obj = models.Book.objects.filter(pk=1).first()
    book_obj.authors.set([2, ])  # 修改关系

    # 这里是用获取到的对象进行修改
    book_obj = models.Book.objects.filter(pk=1).first()
    author_obj1 = models.Author.objects.filter(pk=1).first()
    author_obj2 = models.Author.objects.filter(pk=2).first()
    book_obj.authors.set((author_obj1,))
    book_obj.authors.set((author_obj1, author_obj2))

    # 删除记录,也是分成根据值对象进行删除
    book_obj = models.Book.objects.filter(pk=1).first()
    book_obj.authors.remove(2)
    book_obj.authors.remove(1, 3)
    book_obj.authors.remove(author_obj1,)
    book_obj.authors.remove(author_obj1,author_obj2)
    book_obj.authors.clear()

参数使用讲解

add()

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

参数可以是多个位置参数(数字或对象)

remove()

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

参数可以是多个位置参数(数字或对象)

set()

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

可迭代对象(参数要放到元组或列表中,元组或列表中存放的参数可以是数据值或对象)

clear()

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

七、ORM跨表查询

复习MySQL跨表查询的思路

子查询

	分步操作:将一条SQL语句用括号括起来当做另外一条SQL

语句的条件

连表操作

	先整合多张表之后基于单表查询即可

		inner join		内连接

		left join		   左连接

		right join		 右连接

		union			  全连接

正反向查询的概念(重要)

  • 正向查询

      由外键字段所在的表数据查询关联的表数据是正向
      
      或者说外键在自己手上则是正向查询
    
  • 反向查询

      没有外键字段的表数据查询关联的表数据是反向
      
      或者说外键在别人手上则是反向查询
    

ps:正反向的核心就看外键字段在不在当前数据所在的表中

ORM跨表查询的口诀(重要)

正向查询按外键字段

反向查询按表名小写

八、基于对象的跨表查询

    '''基于对象的跨表查询'''
    # 1.查询主键为1的书籍对应的出版社名称
    # 先根据条件获取数据对象
    book_obj = models.Book.objects.filter(pk=1).first()
    # 再判断正反向的概念  由书查出版社 外键字段在书所在的表中 所以是正向查询
    print(book_obj.publish.name)

    # 2.查询主键为4的书籍对应的作者姓名
    # 先根据条件获取数据对象
    book_obj = models.Book.objects.filter(pk=4).first()
    # 再判断正反向的概念  由书查作者 外键字段在书所在的表中 所以是正向查询
    print(book_obj.authors)  # app01.Author.None
    print(book_obj.authors.all())
    print(book_obj.authors.all().values('name'))
    
    # 3.查询jason的电话号码
    author_obj = models.Author.objects.filter(name='jason').first()
    print(author_obj.author_detail.phone)

    # 4.查询北方出版社出版过的书籍
    publish_obj = models.Publish.objects.filter(name='北方出版社').first()
    print(publish_obj.book_set)  # app01.Book.None
    print(publish_obj.book_set.all())

    # 5.查询jason写过的书籍
    author_obj = models.Author.objects.filter(name='jason').first()
    print(author_obj.book_set)  # app01.Book.None
    print(author_obj.book_set.all())

    # 6.查询电话号码是110的作者姓名
    author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
    print(author_detail_obj.author)
    print(author_detail_obj.author.name)

ps:在进行反向查询的时候,因为会用掉小写的表名,这时候需要在表名后面跟下_set才能获取到信息,这时候获取到的内容如下:

app01.Book.None

获取到内容后点all方法就能看到内部的数据值

九、基于双下划线的跨表查询

    '''基于双下划线的跨表查询'''
    # 1.查询主键为1的书籍对应的出版社名称
    res = models.Book.objects.filter(pk=1).values('publish__name','title')
    print(res)

    # 2.查询主键为4的书籍对应的作者姓名
    res = models.Book.objects.filter(pk=4).values('title', 'authors__name')
    print(res)

    # 3.查询jason的电话号码
    res = models.Author.objects.filter(name='jason').values('author_detail__phone')
    print(res)

    # 4.查询北方出版社出版过的书籍名称和价格
    res = models.Publish.objects.filter(name='北方出版社').values('book__title','book__price','name')
    print(res)

    # 5.查询jason写过的书籍名称
    res = models.Author.objects.filter(name='jason').values('book__title', 'name')
    print(res)

    # 6.查询电话号码是110的作者姓名
    res = models.AuthorDetail.objects.filter(phone=110).values('phone', 'author__name')
    print(res)

十、进阶操作

    '''基于双下划线的跨表查询'''
    # 1.查询主键为1的书籍对应的出版社名称
    res = models.Book.objects.filter(pk=1).values('publish__name','title')
    print(res)

    # 2.查询主键为4的书籍对应的作者姓名
    res = models.Book.objects.filter(pk=4).values('title', 'authors__name')
    print(res)

    # 3.查询jason的电话号码
    res = models.Author.objects.filter(name='jason').values('author_detail__phone')
    print(res)

    # 4.查询北方出版社出版过的书籍名称和价格
    res = models.Publish.objects.filter(name='北方出版社').values('book__title','book__price','name')
    print(res)

    # 5.查询jason写过的书籍名称
    res = models.Author.objects.filter(name='jason').values('book__title', 'name')
    print(res)

    # 6.查询电话号码是110的作者姓名
    res = models.AuthorDetail.objects.filter(phone=110).values('phone', 'author__name')
    print(res)

    '''进阶操作'''
    # 1.查询主键为1的书籍对应的出版社名称
    res = models.Publish.objects.filter(book__pk=1).values('name')
    print(res)

    # 2.查询主键为4的书籍对应的作者姓名
    res = models.Author.objects.filter(book__pk=4).values('name','book__title')
    print(res)

    # 3.查询jason的电话号码
    res = models.AuthorDetail.objects.filter(author__name='jason').values('phone')
    print(res)

    # 4.查询北方出版社出版过的书籍名称和价格
    res = models.Book.objects.filter(publish__name='北方出版社').values('title','price')
    print(res)

    # 5.查询jason写过的书籍名称
    res = models.Book.objects.filter(authors__name='jason').values('title')
    print(res)

    # 6.查询电话号码是110的作者姓名
    res = models.Author.objects.filter(author_detail__phone=110).values('name')
    print(res)

    '''补充'''
    # 查询主键为4的书籍对应的作者的电话号码
    res = models.Book.objects.filter(pk=4).values('authors__author_detail__phone')
    print(res)
    res = models.AuthorDetail.objects.filter(author__book__pk=4).values('phone')
    print(res)
    res = models.Author.objects.filter(book__pk=4).values('author_detail__phone')
    print(res)

十一、图书管理系统讲解

1.表设计
	先考虑普通字段再考虑外键字段
	数据库迁移、测试数据录入
2.首页展示
3.书籍展示
4.书籍添加
5.书籍编辑
	后端如何获取用户想要编辑的数据、前端如何展示出待编辑的数据
6.书籍删除

十二、聚合查询

在ORM中支持单独使用聚合函数,需要使用aggregate方法。

聚合函数:Max最大、Min最小、Sum总和、Avg平均、count统计

示例1

from django.db.models import Max, Min, Sum, Count, Avg
res = models.Book.objects.aggregate(Max('price'), Count('pk'), 最小价格=Min('price'), allPrice=Sum('price'),平均价格=Avg('price'))
print(res)

ps:我们在进行聚合查询的时候,可以给聚合查询的内容命名,如果不主动命名,也会自动用下划线拼接两个字段名,但是这里的命名不建议用中文(虽然能用)。

示例2


	    from django.db.models import Max, Min, Sum, Avg, Count
	    1.查找所有书籍中价格最高的书籍
	    res = models.Book.objects.aggregate(我是世界上最贵的书=Max('price'))
	    print(res)  # 没有分组之前如果单纯的时候聚合函数 需要关键字aggregate
	
	    2.获取书籍中最贵的 最便宜的 全部需要多少钱 平均一本书多少钱 总共有几本书
	    res1 = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Avg('price'), Count('pk'))
	    print(res1)
	    
	    		'''通过 别名 = 聚合函数的方法可以取一个别名'''

image

十三、分组查询

小提示:

如果执行orm分组查询报错,并且有关键字sql_mode,需要去mysql配置文件中修改严格模式的配置信息。(Mac不会有)

strict mode
	移除sql_mode中的only_full_group_by

image

    # 分组查询
    from django.db.models import Max, Min, Sum, Count, Avg
    # 统计每一本书的作者个数
    res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
    print(res)
    # 统计出每个出版社卖的最便宜的书的价格
    res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
    print(res)
    # 统计不止一个作者的图书
    # 1.先统计每本书的作者个数
    res = models.Book.objects.annotate(author_num=Count('authors__pk'))
    print(res)
    # 2.筛选出作者个数大于1的数据
    res = models.Book.objects.annotate(author_num=Count('authors__pk')).filter(author_num__gt=1).values('title',
                                                                                                        'author_num')
    print(res)
    # 查询每个作者出的书的总价格
    """
    models.表名.objects.annotate()                            按照表分组
    models.表名.objects.values('字段名').annotate()            按照values括号内指定的字段分组
    """
    res = models.Author.objects.annotate(总价=Sum('book__price'),count_book=Count('book__pk')).values('name','总价','count_book')
    print(res)

    res = models.Book.objects.values('publish_id').annotate(count_pk=Count('pk')).values('publish_id', 'count_pk')
    print(res)

十四、ORM中如何给表再次添加新的字段

当我们在对数据库使用ORM进行操作的时候,如果出现需要新增字段的情况,在模型层中修改了代码后,进行数据库迁移操作前,需要对新增的字段值进行设置,否则不让进行数据库迁移。

我们可以把字段值设置成一个固定的值,也可以设置字段值可以为空。

代码

		首先我们先给我们的书籍表格再添加两条数据(库存以及已销售)
		
	    storage_num = models.IntegerField(verbose_name='库存数')
		sale_num = models.IntegerField(verbose_name='已销售')
		但是出现报错了  
		we can't do that (the database needs something to populate existing rows)
		
		现在这张表已经有数据了 再添加两个新的字段 没有数据值 它的数据值呢 ?
		两种方式 一个默认值 一个退出不执行这个命令再去修改(所以我们得给他加上数据)
		
		storage_num = models.IntegerField(verbose_name='库存数', null=True)
		sale_num = models.IntegerField(verbose_name='已销售', default=1000)

如果不对新的字段值进行设置会出现一下提示:

image

十五、F与Q查询

F查询

    # 1.查询库存数大于卖出数的书籍
    '''当查询条件不是明确的 也需要从数据库中获取 就需要使用F查询'''
    from django.db.models import F
    res = models.Book.objects.filter(kucun__gt=F('maichu'))
    print(res)
    # 2.将所有书的价格涨800
    models.Book.objects.update(price=F('price') + 800)
    # 3.将所有书的名称后面追加爆款
    from django.db.models.functions import Concat
    from django.db.models import Value
    models.Book.objects.update(title=Concat(F('title'), Value('新款')))

在用ORM进行查询的时候,我们会发现当查询条件不明确的时候不能执行语句,因此需要用到F查询。

image

Q查询

    # 查询主键是1或者价格大于2000的书籍
    res = models.Book.objects.filter(pk=1, price__gt=2000)  # 逗号默认是and关系
    from django.db.models import Q
    res = models.Book.objects.filter(Q(pk=1), Q(price__gt=2000))  # 逗号是and
    res = models.Book.objects.filter(Q(pk=1) | Q(price__gt=2000))  # |是or
    res = models.Book.objects.filter(~Q(pk=1) | Q(price__gt=2000))  # ~是not
    print(res.query)

image

十六、Q查询进阶操作

这里主要就是让查询数据的时候,可以使用input获取的信息,进行用户交互。

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)

十七、ORM查询优化

  • 1.ORM的查询默认都是惰性查询(当我们不执行打印操作的时候,ORM语句不会执行,想要看到这个现象需要打开日志功能,即在配置文件中进行配置)
  • 2.ORM的查询自带分页处理(可以通过日志展示的代码查看,日志返回的sql代码后端会有一个limit)
  • 3.only与defer

only

前置说明

这里需要做一些具体的说明,方便大家理解only和defer。

当我们在Django中执行ORM操作进行数据库查询的时候,其实内部的代码把所有的数据库中的记录,都封装到了ORM操作的对象中去了,因此我们可以通过点的方式或是索引等方式查询到对应的数据。

但是当遇到查询的时候需要查询不在条件中的记录时,就需要执行sql语句进行查询了。

比如我们在查询的时候,需要的结果在外键对应的表中,这时候去外键对应的表中查询数据,就需要执行sql语句进行查询,并且查询一条记录需要执行一次sql语句

而我们的only的作用是把写在括号内的参数中的字段的值封装到对象中,让后续查找的时候 不需要执行sql语句进行查询,加快执行速度。或是起到一个减少代码封装的数据量,加快运行的作用。

而defer则是和only相反,写在括号内的字段值不会被封装到对象中,别的字段反而会被封装到对象中。

only
'''数据对象+含有指定字段对应的数据'''
    # 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查询

defer
    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查询
  • 4.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

image

image

image

十八、ORM事务操作

"""
1.事务的四大特性(ACID)
	原子性、一致性、隔离性、持久性
2.相关SQL关键字
	start transaction;
	rollback;
	commit;
	savepoint;
3.相关重要概念
	脏读、幻读、不可重复读、MVCC多版本控制...
"""
django orm提供了至少三种开启事务的方式
	方式1:配置文件的数据库相关配置中添加键值对	 
    全局有效
       "ATOMIC_REQUESTS": True
        ps:每次请求所涉及到的orm操作同属于一个事务
	方式2:装饰器							
    局部有效,这个视图函数内的一些orm操作属于一个事务
       from django.db import transaction
       @transaction.atomic
       def index():pass	
 	方式3:with上下文管理					
    局部有效,写在with下方的orm操作属于一个事务
       from django.db import transaction
    	def reg():
    		with transaction.atomic():
             pass

ps:这里的三种方法有个小区别,前面两种方式执行事务,遇到返回值类型不对,orm操作雀食是可以正常执行的,但是with上下文管理的方式操作事务的话,则不行,操作会回退。

十九、ORM常用字段类型

名称 含义
AutoField() Int自增列 必须填入参数 primary_key=True 当model中如果没有自增列 则自动会创建一个列名为id的列
CharField() 字符类型 必须提供max_length参数 max_length表示字符长度
IntegerField() 一个整数类型 范围在 -2147483648 to 2147483647 (一般不用它来存手机号(位数也不够) 直接用字符串存)
BigIntegerField() 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
DateField() 日期字段 日期格式 YYYY-MM-DD 相当于Python中的datetime.date()实例
DateTimeField() 日期时间字段 格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] 相当于Python中的datetime.datetime()实例
DecimalField() 10进制小数 参数 max_digits 小数总长度 decimal_places,小数位长度
EmailField() 字符串类型 Django Admin以及ModelForm中提供验证机制
BooleanField() 布尔值类型 传布尔值存数字0或1
TextField() 文本类型 存储大段文本
FileField() 字符串 路径保存在数据库 文件上传到指定目录
参数 upload_to = " " 上传文件的保存路径
storage = None 存储组件 默认django.core.files.storage.FileSystemStorage
ForeignKey() 外键类型在ORM中用来表示外键关联关系 一般把ForeignKey字段设置在 '一对多’中’多’的一方 ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系
OneToOneField() 一对一字段 通常一对一字段用来扩展已有字段 通俗的说就是一个人的所有信息不是放在一张表里面的,简单的信息一张表,隐私的信息另一张表,之间通过一对一外键关联
ManyToManyField() 简单来说就是在多对多表关系并且这一张多对多的关系表是有Django自动帮你建的情况下 下面的方法才可使用create add set remove clear

ORM还支持用户自定义字段类型,比方说ORM中没有设置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

这里是调用我们定义的字段类型
    class User(models.Model):
        name = models.CharField(max_length=32)
        info = MyCharField(max_length=64)

image

二十、ORM常用字段参数

名称 含义
primary_key 主键
verbose_name 注释
max_length 字段长度
max_digits 小数总共多少位
decimal_places 小数点后面的位数
auto_now 每次操作数据自动更新事件
auto_now_add 首次创建自动更新事件后续不自动更新
null 允许字段为空
default 字段默认值
unique 唯一值
db_index 给字段添加索引
choices 当某个字段的可能性能够被列举完全的情况下使用。如:性别、学历、工作状态、...
to 关联表
to_field 关联字段(不写默认关联数据主键)
on_delete 当删除关联表中的数据时,当前表与其关联的行的行为。

需要特别拿出来讲的是关联表时用的字段参数:

on_delete

它的值决定了关联表之前的关联操作行为

这里是代码使用展示

def func():
    return 10

class MyModel(models.Model):
    user = models.ForeignKey(
        to="User",
        to_field="id",
        on_delete=models.SET(func)
    )

不同的值对应的功能

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

当字段数据的可能性是可以完全列举出来的时候 应该考虑使用该参数

		class UserInfo(models.Model):
	    username = models.CharField(max_length=32)
	    gender_choice = (
	        (1, '男性'),
	        (2, '女性'),
	        (3, 'other'),
	    )
	    gender = models.IntegerField(choices=gender_choice)

	    user_obj = models.UserInfo.objects.filter(pk=1).first()
	    print(user_obj.gender)  # 获取的是真实数据
	    print(user_obj.get_gender_display())
	    user_obj1 = models.UserInfo.objects.filter(pk=2).first()
	    user_obj2 = models.UserInfo.objects.filter(pk=3).first()
	    user_obj3 = models.UserInfo.objects.filter(pk=4).first()
	    print(user_obj1.get_gender_display())
	    print(user_obj2.get_gender_display())
	    print(user_obj3.get_gender_display())   # 如果没有则按照真实数据返回

image

二十一、多对多三种创建方式

1.全自动创建
	class Book(models.Model):
        title = models.CharField(max_length=32)
        authors = models.ManyToManyField(to='Author')
	class Author(models.Model):
        name = models.CharField(max_length=32)
	优势:自动创建第三张表 并且提供了add、remove、set、clear四种操作
	劣势:第三张表无法创建更多的字段 扩展性较差
 
2.纯手动创建
	 class Book(models.Model):
        title = models.CharField(max_length=32)
    class Author(models.Model):
        name = models.CharField(max_length=32)
    class Book2Author(models.Model):
        book = models.ForeignKey(to='Book')
        author = models.ForeignKey(to='Author')
        others = models.CharField(max_length=32)
        join_time = models.DateField(auto_now_add=True)
 	 优势:第三张表完全由自己创建 扩展性强
	 劣势:编写繁琐 并且不再支持add、remove、set、clear以及正反向概念
        
3.半自动创建
	 class Book(models.Model):
        title = models.CharField(max_length=32)
        authors = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('book','author'))
    class Author(models.Model):
        name = models.CharField(max_length=32)
    class Book2Author(models.Model):
        book = models.ForeignKey(to='Book', on_delete=models.CASCADE)
        author = models.ForeignKey(to='Author', on_delete=models.CASCADE)
        others = models.CharField(max_length=32)
        join_time = models.DateField(auto_now_add=True)
	 优势:第三张表完全由自己创建 扩展性强 正反向概念依然清晰可用
    劣势:编写繁琐不再支持add、remove、set、clear

二十二、批量操作数据

当我们给数据库插入很多的数据时,如果我们使用orm操作一条条用for循环插入数据,效率很低。

这里就需要介绍两个批量操作数据的方法:

models.Books01.objects.bulk_create

models.Books01.objects.bulk_update

这里的create是批量创建数据,update是批量更新数据。

在进行批量操作数据之前,下方代码使用的方式是把数据封装到对象中,然后把对象添加到一个列表中,最后执行批量操作的时候把这个列表当作参数放到小括号内即可。

def ab_bk_func(request):
    # 1.往books表中插入10万条数据
    # for i in range(1, 100000):
    #     models.Books.objects.create(title='第%s本书' % i)
    
    """直接循环插入 10s 500条左右"""
    book_obj_list = []  # 可以用列表生成式[... for i in ... if ...]     生成器表达式(... for i in ... if ...)
    for i in range(1, 100000):
        book_obj = models.Books01(title='第%s本书' % i)  # 单纯的用类名加括号产生对象
        book_obj_list.append(book_obj)
    # 批量插入数据
    models.Books01.objects.bulk_create(book_obj_list)
    """使用orm提供的批量插入操作 5s 10万条左右"""
    # 2.查询出所有的表中并展示到前端页面
    book_queryset = models.Books01.objects.all()
    return render(request, 'BkPage.html', locals())


posted @   致丶幻  阅读(47)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
  1. 1 So Far Away (Acoustic) Adam Christopher
  2. 2 雪 Distance Capper&罗言RollFlash
  3. 3 CollapsingWorld
  4. 4 Call You Tonight Johnta Austin
Call You Tonight - Johnta Austin
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

Mmmm...

Mmmmm...

Mmmmmm....

The stars must be alighted tonight

I believe this has to have a meaning

Lightning had to strike tonight

Cause the two of us are finally meeting

In this place at this time

And I feel safe when I look in your eyes

I feel like I know you from another life

And it makes me wish I wasn‘t so pressed by time

I can‘t catch my breathe

Cause you take it away

The best writer in town

Could not find words to say

How there are so many things

I wanna get to know

I wish that I could stay

But I gotta go

So I‘mma call you tonight

I will baby

Just as soon as I get time alone

I‘mma call you tonight

I will baby

Just as soon as I get home

That‘s not the way it goes in life

You get busy when you just don‘t wanna

There‘s never enough time day or night

You have to make it so baby I‘m gonna

Make a way to connect

Cause your face is one I can‘t forget

I feel like I know you from another life

And it makes me wish I wasn‘t so pressed for time

I can‘t catch my breathe

I can‘t catch my breathe

Cause you take it away

The best writer in town

Could not find words to say

How there are so many things

I wanna get to know

I wish that I could stay

But I gotta go

So I‘mma call you tonight

I will baby

Just as soon as I get time alone

I‘mma call you tonight

I will baby

Just as soon as I get home

Destiny

I believe in it

Meant to be

Don‘t you see it‘s possible that this kind of magic

Anything can happen

And if you wanna know

Then you‘ll stay by the phone

Ohhhhhh

I can‘t catch my breathe

Cause you take it away

The best writer in town

Could not find words to say

How there are so many things

I wanna get to know

I wish that I could stay

But I gotta go

So I‘mma call you tonight

I will baby

Just as soon as I get time alone

I‘mma call you tonight

I will baby

Just as soon as I get home

I‘mma call you tonight

I will baby

Just as soon as I get home

点击右上角即可分享
微信分享提示