6.ORM模型层-多表查询
ORM模型层-多表查询
1.创建表
在应用的models.py文件中声明类
from django.db import models
# Create your models here.
'''
Author作者 -- 一对一的关系 -- AuthorDetail作者详细信息
models.OneToOneField("AuthorDetail",)
book表 -- 多对一的关系 -- 出版社表Publish
models.ForeignKey("Publish")
book表 -- 多对多的关系 -- Author作者表
models.ManyToManyField(to='Author',)
第三张表: book_to_author 表
'''
# 作者表
class Author(models.Model):
# nid = models.AutoField(primary_key=True) # 修改主键字段名称
# id 默认
name=models.CharField(max_length=32)
ad = models.OneToOneField(to="AuthorDetail",to_field="id",on_delete=models.CASCADE) # CASCADE 级联有很多
# foreign key reference AuthorDetail(id) unique 级联删除on delete cascade 级联更新on update cascade
# ad = models.OneToOneField("AuthorDetail",) # 等同于上面注释的这个
# 作者详细信息表
class AuthorDetail(models.Model):
birthday=models.DateField()
# telephone=models.BigIntegerField()
telephone=models.CharField(max_length=32)
addr=models.CharField(max_length=64)
# 出版社表
class Publish(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
# 书籍表
class Book(models.Model):
title = models.CharField(max_length=32)
publishDate = models.DateField()
# DecimalField -- Decimal(12,4)类型 -- 完全精度
price = models.DecimalField(max_digits=5,decimal_places=2) # 999.99
# publishs = models.ForeignKey(to="Publish",to_field="id",on_delete=models.CASCADE)
publishs = models.ForeignKey("Publish") # 同上
# authors不会生成book表的字段,而是生成book表和作者表的第三张关系记录表,通过这个属性可以操作第三张表
authors = models.ManyToManyField(to='Author',)
# 第三张关系记录表 ,如果第三张表只有book和author两个字段,则不需要建立第三张表,否则需要建立第三张表添加字段
# class book_to_author(models.Model):
# book = models.ForeignKey('Book')
# author = models.ForeignKey('Author')
# xx = models.CharField(max_length=32)
表的元信息补充
# 出版社表
class Publish(models.Model):
name = models.CharField(max_length=32, verbose_name='出版社名称', db_column='namexx') #namexx是生成的表字段名称,verbose_name给字段进行说明
city = models.CharField(max_length=32)
class Meta:
db_table = 'pub' # 生成的数据库表名,自定义表名
index_together = ['name','city'] #联合索引
unique_together = ['name','city'] #联合唯一索引
# 指定默认按什么字段排序。
ordering = ['name', ] # 查询的数据就有个默认排序规则了
2.创建mysql连接
1 创建库
mysql> create database orm02 charset=utf8mb4;
2 配置连接
settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'orm02',
'HOST': '127.0.0.1',
'PORT': 3306,
'USER': 'root',
'PASSWORD': '666',
}
}
3 项目主目录下的__init__.py
import pymysql
pymysql.install_as_MySQLdb()
4 数据库同步指令
python manage.py makemigrations
python manage.py migrate
3.表数据的增删改查
在应用的views.py视图中声明函数,urls.py路由指向视图函数
1.增加
一对一添加数据
from app01 import models
import datetime
def query(request):
# 增加
# 一对一
#添加一个名称为王照的作者
# author_detail_obj = models.AuthorDetail.objects.get(id=1) #作者详细表模型类对象
models.Author.objects.create(
name='王照',
# ad=author_detail_obj #方法1:关系名称 = 模型类对象
ad_id= 1 #方法2:关系属性名称_id = 关系属性id值添加,不用先找模型类对象了
)
return HttpResponse('ok')
一对多添加数据
#一对多
publish_obj = models.Publish.objects.get(id=2) #获取出版社模型类对象
models.Book.objects.create(
title='沈阳',
publishDate='2020-12-12',
price=12,
publishs=publish_obj #方法1:模型类对象
# publishs_id=2 #方法2:关系属性id值添加
)
多对多添加数据: add()方法
# 多对多
# 其实就是添加多对多关系记录表的关系记录
# 添加沈阳这本书 是 王照和玉波写
author_obj1 = models.Author.objects.get(name='王照')
author_obj2 = models.Author.objects.get(name='玉波')
book_obj = models.Book.objects.get(title='沈阳')
#方法1:通过模型类对象添加
book_obj.authors.add(author_obj1,author_obj2) #添加这本书的两个作者
#方法2:通过关系属性id值添加
book_obj.authors.add(2,3) #只要知道两个作者的id就可以了
# book_obj.authors.add(*[2,3]) #同上
2.修改
一对一和一对多的操作和单表一样
方式1:update根据Queryset类型数据进行更新,可能批量更新
models.Book.objects.filter(title='沈阳').update(
title = '沈阳11',
# publishs = #出版社模型类对象
publishs_id = 1
)
方式2:根据模型类对象进行更新,必须执行save()才能保存更新
book_obj = models.Book.objects.get(title='赘婿')
book_obj.title = '赘婿11'
book_obj.publishs_id = 2
book_obj.save()
多对多: set()方法
# 多对多 :set()方法 修改这本书的两个作者
obj = models.Book.objects.get(title='沈阳11')
obj.authors.set(['3','4']) #更新 -两步:1 删除之前的关系记录 2 添加新记录
3.删除
一对一和一对多的操作和单表一样
delete() 方法:
#Queryset类型的删除,可能是多条数据
models.Book.objects.filter(title='沈阳11').delete()
#模型类对象删除,只能是一条数据
models.Book.objects.get(title='沈阳11').delete()
多对多
方法1: 通过Queryset获取模型类对象
obj = models.Book.objects.filter(title='斗破苍穹').first()
obj.authors.remove(2,3) #删除这本书中的两个作者
obj.authors.clear() #清除这本书对应第三张表的所有记录
方法2: 直接后期模型类对象
obj = models.Book.objects.get(title='斗破苍穹')
obj.authors.remove(2,3) #删除这本书中的两个作者
obj.authors.clear() #清除这本书对应第三张表的所有记录
4.查询
1.基于对象的跨表查询(子查询)
# 正向:关系属性写在A,那么通过A表数据去查询B表数据时,就是正向查询
# 反向:反之就是反向查询
# 一对一
#正向查询 : 靠关联属性
#查询一下 王照作者的家庭住址
author_obj = models.Author.objects.get(name='王照')
# author_obj.ad #直接找到了对应的关系记录对象
print(author_obj.ad.addr) #直接找到地址--北京
#反向查询 : 靠模型类名小写
#查询手机号为120的作者名称
author_detail_obj = models.AuthorDetail.objects.get(telephone='120')
# author_detail_obj.author #拿到对应的关系记录对象
print(author_detail_obj.author.name) #玉波
# 一对多
#正向查询 : 靠关联属性
#查询赘婿这本书是哪个出版社出版的
book_obj = models.Book.objects.get(title='赘婿')
print(book_obj.publishs.name) #py33期出版社
#反向查询 : 靠模型类名小写_set
#查询py33期出版社出版了那些书
publish_obj = models.Publish.objects.get(name='py33期出版社')
# publish_obj.book_set # objects控制器 得到可能是多条记录
print(publish_obj.book_set.all()) # 取出反向查询的所有结果
print(publish_obj.book_set.all().values('title')) # 取出反向查询的书籍名称
# print(publish_obj.book_set.filter()) # 还可以过滤
# 多对多
#正向查询 : 靠关联属性
#查斗破苍穹有哪几个作者写的
book_obj = models.Book.objects.get(title='斗破苍穹')
# book_obj.authors # objects控制器 得到可能是多条记录
print(book_obj.authors.all())
#反向查询 :靠模型类名小写_set
#查询王照写了那些书
author_obj = models.Author.objects.get(name='王照')
# author_obj.book_set #objects控制器 得到可能是多条记录
print(author_obj.book_set.all())
2.基于双下划线的跨表查询
一对一,一对多,多对多操作一样
一对一: 查询一下 王照作者的家庭住址
# 正向连表 : 靠关联属性,先找作者再连表找字段
ret = models.Author.objects.filter(name='王照').values('ad__addr')
# print(ret) #<QuerySet [{'ad__addr': '北京'}]>
# 反向连表 : 模型类名小写,先连表再找字段
ret = models.AuthorDetail.objects.filter(author__name='照').values('addr',)
# print(ret) #<QuerySet [{'addr': '北京'}]>
一对多: 查询白洁这本书是哪个出版社出版的
# 正向连表
models.Book.objects.filter(title='白洁').values('publishs__name')
# 反向连表
models.Publish.objects.filter(book__title='白洁').values('name')
多对多: 查询一斗破苍穹梅这本书是哪几个作者写的
# 正向连表
models.Book.objects.filter(title='斗破苍穹').values('authors__name')
# 反向连表
models.Author.objects.filter(book__title='斗破苍穹').values('name')
连续跨表练习: 查询py33期出版社出版过的所有书籍的名字以及作者的姓名
正向查询:
res = models.Publish.objects.filter(name='py33期出版社').values('book__title','book__authors__name')
print(res)
反向查询:
res = models.Book.objects.filter(publishs__name='py33期出版社').values('title','authors__name')
print(res)
查看原生sql语句
方式1 : 在settings.py 文件中加入下面代码
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
方式2:
res =models.Author.objects.filter(name='王照').values('ad__addr')
print(res)
from django.db import connection
print(connection.queries)
3.聚合查询,分组查询,F查询,Q查询
引入
from django.db.models import Avg, Max, Min, Sum, F,Q
聚合查询: aggregate--聚合的
#聚合查询:
from django.db.models import Avg, Max, Min, Sum, F,Q
# 查询一下书籍的平均价格
ret = models.Book.objects.aggregate(a=Avg('price')) #a是起别名
print(ret) #{'a': 18.5}结果是字典,所以不能在后面继续使用queryset其他方法了
分组查询 : annotate
# 查看一下每个出版社出版书的平均价格
# 方式1
ret = models.Book.objects.values('publishs_id').annotate(a=Avg('price'))
#以values字段进行分组,annotate分组后统计的数据
# 方式2
ret = models.Publish.objects.annotate(a=Avg('book__price')).values('name','a')
print(ret)
# annotate前面不指定values字段,默认以id主键进行分组
# 出版社模型类对象:包含出版社表的所有字段数据和a这个分组统计结果
# sql语句group by:select app01_publish.name,avg(app01_book.price) as a from app01_publish inner join app01_book on app01_publish.id = app01_book.publishs_id group by app01_publish.id;
#<QuerySet [{'name': 'py33期出版社', 'a': 28.5}, {'name': '红浪漫出版社', 'a': None}]> None是出版社没出过书
F查询 : 对同一张表中字段操作
# F查询
# 1.用于对同一张表中多个字段进行操作时使用
# 查询一下点赞数大于评论数的书籍
models.Book.objects.filter(dianzan__gt=F('comment')).values('title')
# 2.对同一张表某个字段做统一操作时也能有效
# 书籍价格上调10元
models.Book.objects.all().update(
price=F('price') + 10, # 支持四则运算
)
Q查询 : 组合查询条件 &--and并且, |--or或者, ~ --取反 , 支持Q嵌套
# 引入Q查询
from django.db.models import Q
# & -- and
# | -- or
# ~ -- not 取反
# 1.查询一下点赞数小于20 并且 价格大于20的书籍
ret = models.Book.objects.filter(dianzan__lt=20,price__gt=20)#默认是and查询
ret = models.Book.objects.filter(Q(dianzan__lt=20)&Q(price__gt=20))
# 2.查询一下点赞数大于等于20 或者 价格大于20的书籍
ret = models.Book.objects.filter(Q(dianzan__gte=20) | Q(price__gt=20))
# 3.查询一下点赞数小于20 或者 价格大于20的书籍, 并且出版日期为2021年的
ret = models.Book.objects.filter(Q(dianzan__lt=20) | Q(price__gt=20), publishDate__year='2021') #Q(dianzan__lt=20) | Q(price__gt=20)是一个条件,和逗号后面的是and的关系,如果有的条件没有用Q包裹,那么这个条件要放到被Q包裹的条件后面
ret = models.Book.objects.filter(Q(Q(dianzan__lt=20) | Q(price__gt=20))&Q(publishDate__year='2021')) # 支持 Q 嵌套
print(ret)
执行原生sql语句
方式1 :raw只能写操作本表的原生sql
# 查询一下所有书籍
ret = models.Book.objects.raw('select * from app01_book')
# <RawQuerySet: select * from app01_book>
# print(ret)
for i in ret:
print(i.title,i.price)
方式2 :从django提供的接口中获取数据库连接,然后像使用pymysql模块一样操作数据库
from django.db import connection
# connection -- pymysql连接--conn
cursor = connection.cursor() #创建游标
cursor.execute('select * from app01_book;') #execute里可以写任意sql语句
print(cursor.fetchall()) #取出所有
print(cursor.fetchone()) #取出一条
#((3, 斗破苍穹梅', datetime.date(2021, 2, 4), Decimal('21.00'), 2, 11, 12), (4, '白洁', datetime.date(2021, 2, 4), Decimal('32.00'), 1, 11, 23)))
5.python脚本调动django环境
在根目录下随意创建.py文件(项目外部建立脚本),用来测试项目
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_orm02.settings") #manage.py中的拿过来加载Django环境
import django
django.setup() #把Django运行起来--然后可以运行项目里面的指令
from app01 import models
ret = models.Book.objects.all()
print(ret)