zoukankan      html  css  js  c++  java
  • 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)
    
  • 相关阅读:
    HUST 1584 摆放餐桌
    HUST 1585 排队
    HUST 1583 长度单位
    树状数组 poj2352 Stars
    Visual Studio2013应用笔记---WinForm事件中的Object sender和EventArgs e参数
    倒置输入的数 Exercise07_02
    指定等级 Exercise07_01
    检测密码 Exercise06_18
    一年的天数 Exercise06_16
    数列求和 Exercise06_13
  • 原文地址:https://www.cnblogs.com/jia-shu/p/14589806.html
Copyright © 2011-2022 走看看