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 @ 2023-05-06 15:21  致丶幻  阅读(28)  评论(0编辑  收藏  举报