欢迎来到十九分快乐的博客

生死看淡,不服就干。

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)
posted @ 2021-03-28 20:21  十九分快乐  阅读(93)  评论(0编辑  收藏  举报