开发工程师必备————【Day29】ORM多表查询

本文深入探讨了Django ORM如何与数据库进行交互,包括模型定义、数据迁移、测试环境搭建、外键操作、数据增删改查、多表查询等。讲解了各种查询关键字如filter、all、first、last、get、values、values_list等,并展示了如何进行子查询和连表操作。此外,还介绍了日期处理、正反向查询、跨表查询的本质,以及外键和多对多关系的建立与操作。

今日内容详细

  • 表查询数据准备及测试环境搭建
  • 表查询关键字
  • 外键创建
  • 外键数据增删改查
  • 多表查询(子查询、连表操作)

表查询数据准备及测试环境搭建

1.django自带一个sqlite3小型数据库
说明:该数据库功能非常有限,并且针对日期类型的数据兼容性很差。

2. django切换MySQL数据

  • django1.X版本
导入模块pymysql:

import pymysql
	pymysql.install_as_MySQLdb()
  • django2.X 3.X 4.X
下载mysqlclient模块:

pip install mysqlclient

3.定义模型类

class User(models.Model):
   uid = models.AutoField(primary_key=True,verbose_name='编号')
   name = models.CharField(max_length=32,verbose_name='姓名')
   age = models.IntegerField(verbose_name='年龄')
   join_time = models.DateField(auto_now_add=True)

   """
   auto_now:每次操作数据并保存都会自动更新当前时间;
   auto_now_add:只在创建数据的那一刻自动获取当前时间,之后如果不认为更改则不变。
   """

4.执行数据库迁移命令(模型类>>>表)
makemigrations
migrate

5.模型层测试环境准备

  • 方式1:在任意空的py文件中准备环境(拷贝manage.py文件内一部分代码)
import os
def main():
   os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day59.settings')
   # 必须要等到上述环境准备好才可以导下面django和models模块!!!
   import django
   django.setup()

   from app01 import models
   print(models.User.objects.filter())

if __name__ == '__main__':
   main()
  • 方式2:pycharm提供测试环境
    python console命令行测试环境
    请添加图片描述

ORM常见查询关键字

1/扩展知识点

  • 当需要查询数据主键字段值的时候,可以使用pk忽略掉数据字段真正的名字;
  • 在模型类中可以定义一个__str__方法,便于后续数据对象被打印展示的是查看方便;
  • Queryset中如果是列表套对象那么直接for循环和索引取值但是索引不支持负数;
  • 虽然queryset支持索引但是当queryset没有数据的时候索引会报错 推荐使用first。

2.常见查询关键字
(1)批量操作数据。

  • create()
    创建数据方式一,返回值就是当前创建的数据对象;
import os
def main():
   os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day59.settings')
   import django
   django.setup()
   from app01 import models
   res = models.User.objects.create(name='kevin',age=28)
   print(res.name)    # kevin
   print(res.age)     # 28
   print(res.join_time)    # 2022-09-13
   print(res.pk)          # 3

if __name__ == '__main__':
   main()

创建对象方式二:还可以利用类实例化对象然后调用save方法创建

   user_obj = models.User(name='oscar',age=28)
   user_obj.save()
  • update
    更新数据
models.User.objects.filter(pk=5).update(name='jasonnb')
  • delete()
    删除数据
models.User.objects.filter(pk=2).delete()

(2)13种必会操作。

  • filter()
    筛选数据,返回值是一个QuerySet(可以看成是列表套数据对象)
    (1)括号内不写查询条件 默认就是查询所有
res = models.User.objects.filter()
   print(res)   # <QuerySet [<User: User object (1)>, <User: User object (2)>, <User: User object (3)>, <User: User object (4)>, <User: User object (5)>]>

(2)括号内可以填写条件 并且支持多个 逗号隔开 默认是and关系

   res = models.User.objects.filter(name='oscar',age=28)
   print(res)      # <QuerySet [<User: 对象:oscar>]>
  • all()
    查询所有数据 返回值是一个QuerySet(可以看成是列表套数据对象),即可以for循环取值以及索引取值。
   res = models.User.objects.all()  
   print(res)    # <QuerySet [<User: 对象:jason>, <User: 对象:jason>, <User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]>    
  • first()
    获取Queryset中第一个数据对象 如果为空则返回None
   res = models.User.objects.all()
   print(res)    # <QuerySet [<User: 对象:jason>, <User: 对象:jason>, <User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]>
   print(res.first())   # 对象:jason
  • last()
    获取Queryset中最后一个数据对象 如果为空则返回None
   res = models.User.objects.last()
   print(res) # 对象:oscar
  • get()
    直接根据条件查询具体的数据对象 但是条件不存在直接报错 不推荐使用
   res = models.User.objects.get(pk=3)
   print(res)    # 对象:kevin
  • values()
    指定查询字段 结果是Queryset(可以看成是列表套字典数据)
res = models.User.objects.values('name','age')
   print(res)    # <QuerySet [{'name': 'jason', 'age': 18}, {'name': 'jason', 'age': 18}, {'name': 'kevin', 'age': 28}, {'name': 'kevin', 'age': 28}, {'name': 'oscar', 'age': 28}]>
  • values_list()
    指定查询字段 结果是Queryset(可以看成是列表套元组数据)
res = models.User.objects.values_list('name','join_time')
   print(res)   # <QuerySet [('jason', datetime.date(2022, 9, 13)), ('jason', datetime.date(2022, 9, 13)), ('kevin', datetime.date(2022, 9, 13)), ('kevin', datetime.date(2022, 9, 13)), ('oscar', datetime.date(2022, 9, 13))]>
  • order_by()
    指定字段排序 默认是升序 在字段前加负号则为降序 并且支持多个字段排序
res = models.User.objects.order_by('age')  # 升序
   print(res)   # <QuerySet [<User: 对象:jason>, <User: 对象:jason>, <User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]>
   res = models.User.objects.order_by('-age')   # 降序
   print(res)   # <QuerySet [<User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>, <User: 对象:jason>, <User: 对象:jason>]>
  • count()
    统计orm查询之后结果集中的数据格式
   res = models.User.objects.all().count()
   print(res)   # 5
  • distinct()
    针对重复的数据集进行去重 一定要注意数据对象中的主键(带主键后不能去重)
res = models.User.objects.values('name','age').distinct()
   print(res)   # <QuerySet [{'name': 'jason', 'age': 18}, {'name': 'kevin', 'age': 28}, {'name': 'oscar', 'age': 28}]>
  • exclude()
    针对括号内的条件取反进行数据查询 QuerySet(可以看成是列表套数据对象)
res = models.User.objects.exclude(pk = 1)
   print(res)  # <QuerySet [<User: 对象:jason>, <User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]>
  • reverse()
    针对已经排了序的结果集做颠倒
res = models.User.objects.all().order_by('age')
   print(res.reverse())  # <QuerySet [<User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>, <User: 对象:jason>, <User: 对象:jason>]>
  • exists()
    判断查询结果集是否有数据 返回布尔值 但是几乎不用因为所有数据自带布尔值
   res = models.User.objects.filter(pk=100).exists()
   print(res)    # False
  • raw()

执行SQL语句;

res = models.User.objects.raw('select * from app01_user')
   print(res)   # 原生sql语句:<RawQuerySet: select * from app01_user>

还可以借助于模块。

 from django.db import connection  
   cursor = connection.cursor()  
   cursor.execute("insert into hello_author(name) VALUES ('郭敬明')") 
   cursor.execute("update hello_author set name='韩寒' WHERE name='郭敬明'")  
   cursor.execute("delete from hello_author where name='韩寒'")  
   cursor.execute("select * from hello_author")  
   cursor.fetchone()  
   cursor.fetchall()

神奇的双下划线的查询

1.比较运算符

  • 字段__gt(大于)
res = models.User.objects.filter(age__gt=20)
   print(res)   # <QuerySet [<User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]>   
  • 字段__lt(小于)
   res = models.User.objects.filter(age__lt=20)
   print(res)   # <QuerySet [<User: 对象:jason>, <User: 对象:jason>]>
  • 字段__gte(大于等于)
   res = models.User.objects.filter(age__gte=18)
   print(res)   # <QuerySet [<User: 对象:jason>, <User: 对象:jason>, <User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]>
  • 字段__lte (小于等于)
   res = models.User.objects.filter(age__lte=18)
   print(res)    # <QuerySet [<User: 对象:jason>, <User: 对象:jason>]>

2.成员运算符

  • 字段__in
   res =models.User.objects.filter(name__in=['jason','kevin'])
   print(res)   # <QuerySet [<User: 对象:jason>, <User: 对象:jason>, <User: 对象:kevin>, <User: 对象:kevin>]>

3.范围查询(数字)
字段__range

   res = models.User.objects.filter(age__range=(20,30))
   print(res)   # <QuerySet [<User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]>

4.模糊查询

  • 字段__contains(不忽略大小写)
   res = models.User.objects.filter(name__icontains='j')
   print(res)  # <QuerySet [<User: 对象:jason>, <User: 对象:jason>]>
  • 字段__icontains(忽略大小写)
res = models.User.objects.filter(name__contains='j')
   print(res)  # <QuerySet [<User: 对象:jason>, <User: 对象:jason>]>

5.日期处理

  • 字段__year
   res = models.User.objects.filter(join_time__year=2022)
   print(res)  # <QuerySet [<User: 对象:jason>, <User: 对象:jason>, <User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]>
  • 字段__month
res = models.User.objects.filter(join_time__month=9)
   print(res) # <QuerySet [<User: 对象:jason>, <User: 对象:jason>, <User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]>
  • 字段__day
res = models.User.objects.filter(join_time__day=13)
   print(res) # <QuerySet [<User: 对象:jason>, <User: 对象:jason>, <User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]>

查看ORM底层SQL语句

方式1(局限性比较强):
如果是Queryset对象,那么可以直接点query查看SQL语句。

举个栗子:
res = models.User.objects.filter(join_time__month=9)
   print(res.query)  # SELECT `app01_user`.`uid`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`join_time` FROM `app01_user` WHERE EXTRACT(MONTH FROM `app01_user`.`join_time`) = 9

方式2(通用性强):
配置文件配置,打印所有的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.外键字段关系之一对多

models.ForeignKey()

ORM中外键字段建在多的一方 ;
会自动添加_id后缀。

2.外键字段关系之多对多

models.ManyToManyField()

ORM中有三种创建多对多字段的方式:

  • 方式1:直接在查询频率较高的表中填写字段即可 自动创建第三张关系表
  • 方式2:自己创建第三张关系表
  • 方式3:自己创建第三张关系表 但是还是要orm多对多字段做关联

3.外键字段关系之一对一

models.OneToOneField()

ORM中外键字段建在查询频率较高的表中 ;
会自动添加_id后缀。

4.特别补充:

  • django1.X 针对 models.ForeignKey() models.OneToOneField()不需要on_delete参数的;
  • django2.X 3.X 则需要添加on_delete参数

5.举个栗子之图书管理系统外键字段之间的关系

class Book(models.Model):
   title = models.CharField(max_length=32)
   price = models.DecimalField(max_digits=8, decimal_places=2)  # 总共八位,小数点后占两位
   publish_time = models.DateTimeField(auto_now=True)

   publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)
   authors = models.ManyToManyField(to='Author')

class Publish(models.Model):
   name = models.CharField(max_length=32)
   email = models.EmailField()


class Author(models.Model):
   name = models.CharField(max_length=32)
   age = models.IntegerField()
   author_detail = >models.OneToOneField(to='AuthorDetail',on_delete=models.CASCADE)

class AuthorDetail(models.Model):
   phone =models.BigIntegerField()
   addr = models.CharField(max_length=255)

外键字段数据操作

1.针对models.ForeignKey(to=‘Publish’, on_delete=models.CASCADE)外键添加方式。

方式1直接给实际字段添加关联数据值-------publish_id = 1

models.Book.objects.create(title='python从入门到放弃',price='29800.88',publish_id=1)
方式2间接使用外键虚拟字段添加数据对象 --------publish=publish_obj

publish_obj = models.Publish.objects.filter(pk=2).first()
   models.Book.objects.create(title='django从入门到入土',price=19888.80,publish=publish_obj)

2.针对models.OneToOneField(to=‘AuthorDetail’, on_delete=models.CASCADE)外键添加方式。

方式1直接给实际字段添加关联数据值--------author_detail_id = 1
	方式2间接使用外键虚拟字段添加数据对象--------author_detail=authorDetail_obj

3.针对多对多models.ManyToManyField(to=‘Author’)添加外键方式。

add()
   	添加数据  括号内即可以填写数字值也可以填写数据对象 支持多个

remove()
   删除数据  括号内即可以填写数字值也可以填写数据对象 支持多个

set()
	修改数据  括号内必须是可迭代对象 

clear()
	清空指定数据  括号内不需要任何参数

正反向概念

1.正反向的概念核心就在于外键字段在谁手上

  • 正向查询
    通过书查询出版社 外键字段在书表中
  • 反向查询
    通过出版社查询书 外键字段不在出版社表中

ORM跨表查询口诀>>>:正向查询按外键字段 反向查询按表名小写

2.基于对象的跨表查询(子查询)
基于对象的正向跨表查询

  • 查询主键为1的书籍对应的出版社(书>>>出版社)
    (1)先根据条件查询数据对象(先查书籍对象)
book_obj = models.Book.objects.filter(pk=1).first()

(2)以对象为基准 思考正反向概念(书查出版社 外键字段在书表中 所以是正向查询)

print(book_obj.publish)
  • 查询主键为3的书籍对应的作者(书>>>作者)
    (1)先根据条件查询数据对象(先查书籍对象)
book_obj = models.Book.objects.filter(pk=3).first()

(2)以对象为基准 思考正反向概念(书查作者 外键字段在书表中 所以是正向查询)

print(book_obj.authors)  # app01.Author.None
print(book_obj.authors.all())
  • 查询jason的作者详情
    (1)先根据条件查询数据对象
author_obj = models.Author.objects.filter(name='jason').first()

(2)以对象为基准 思考正反向概念

print(author_obj.author_detail)

基于对象的反向跨表查询

  • 查询南方出版社出版的书籍
    (1)先拿出版社对象
publish_obj = models.Publish.objects.filter(name='南方出版社').first()

(2)思考正反向

# print(publish_obj.book)
# print(publish_obj.book_set)  # app01.Book.None
print(publish_obj.book_set.all())
  • 查询jason写过的书
    (1)先拿作者对象
author_obj = models.Author.objects.filter(name='jason').first()

(2)思考正反向

# print(author_obj.book)
# print(author_obj.book_set)  # app01.Book.None
print(author_obj.book_set.all())
  • 查询电话是110的作者
    (1)先拿作者详情对象
author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()

(2)思考正反向

print(author_detail_obj.author)

2.基于双下划线的跨表查询(连表操作)
基于双下划线的正向跨表查询

  • 查询主键为1的书籍对应的出版社名称及书名
res = models.Book.objects.filter(pk=1).values('publish__name','title')
print(res)
  • 查询主键为3的书籍对应的作者姓名及书名
res = models.Book.objects.filter(pk=3).values('authors__name', 'title')
print(res)
  • 查询jason的作者的电话号码和地址
res = models.Author.objects.filter(name='jason').values('author_detail__phone','author_detail__addr')
print(res)

基于双下划线的反向跨表查询

  • 查询南方出版社出版的书籍名称和价格
res = models.Publish.objects.filter(name='南方出版社').values('book__title','book__price')
# print(res)
  • 查询jason写过的书的名称和日期
res = models.Author.objects.filter(name='jason').values('book__title','book__publish_time')
# print(res)
  • 查询电话是110的作者姓名和年龄
res = models.AuthorDetail.objects.filter(phone=110).values('author__name','author__age')
print(res)

研究ORM跨表本质(需要用到三张表)

  • 查询主键为1的书籍对应的作者电话号码
res = models.Book.objects.filter(pk=1).values('authors__author_detail__phone')
   print(res)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值