• Django(多表查询操作)


    首先了解一下 mysql中的表之间的关系,一对一,一对多,多对一,多对多。

    一对多关系、多对一关系、一对一关系

    1. 至少都有一侧是单个实体,所以记录之间的联系通过外键实现,让外键指向这个实体。
    2. 实现这种关系时,要在“多”这一侧加入一个外键,指向“一”这一侧联接的记录。

    多对多关系

    1. 解决方法是添加第三个表,这个表称为关联表。
    2. 多对多关系可以分解成原表和关联表之间的两个一对多关系

    多对多关系例子

    查询多对多关系要分成两步。

    1. 若想知道某位学生选择了哪些课程,要先从学生和注册之间的一对多关系开始, 获取这位学生在 registrations 表中的所有记录。
    2. 然后再按照多到一的方向遍历课程和注册之间的一对多关系, 找到这位学生在 registrations 表中各记录所对应的课程。
    3. 同样,若想找到选择了某门课程的所有学生,你要先从课程表中开始,获取其在 registrations 表中的记录,再获取这些记录联接的学生。

    自引用关系也是多对多的一种特殊情况

    如果关系中的两侧都在同一个表中, 这种关系称为自引用关系。在关注中, 关系的左侧是用户实体,可以称为“关注者”;关系的右侧也是用户实体,但这些是“被关注者”。

    创建一个多表之间关系的例子

    实例:我们来假定下面这些概念,字段和关系
    作者模型:一个作者有姓名和年龄。
    作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
    出版商模型:出版商有名称,所在城市以及email。
    书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
    模型建立如下:

    from django.db import models
    
    
    # Create your models here.
    
    class Author(models.Model):
        nid = models.AutoField(primary_key=True)
        name = models.CharField(max_length=32)
        age = models.IntegerField()
    
        # 与AuthorDetail建立一对一的关系
        authorDetail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE)
    
    
    class AuthorDetail(models.Model):
        nid = models.AutoField(primary_key=True)
        birthday = models.DateField()
        telephone = models.BigIntegerField()
        addr = models.CharField(max_length=64)
    
    
    class Publish(models.Model):
        nid = models.AutoField(primary_key=True)
        name = models.CharField(max_length=32)
        city = models.CharField(max_length=32)
        email = models.EmailField()
    
    
    class Book(models.Model):
        nid = models.AutoField(primary_key=True)
        title = models.CharField(max_length=32)
        publishDate = models.DateField()
        price = models.DecimalField(max_digits=5, decimal_places=2)
    
        # 与Publish建立一对多的关系,外键字段建立在多的一方
        publish = models.ForeignKey(to="Publish", to_field="nid", on_delete=models.CASCADE)
        # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
        authors = models.ManyToManyField(to='Author', )

    添加记录

    #方式一:如果是这样直接指定publish_id字段去添加值,前提是你的主表里面必须有数据
    # 主表:没有被关联的(因为book表是要依赖于publish这个表的)也就是publish表
    # 子表:关联的表
    book_obj = Book.objects.create(title='小猪猪', publishDate='2012-12-12', price=200, publish_id=1)
    #方式二 建议
    pub_obj = Publish.objects.filter(name='西瓜出版社')[0])
    Book.objects.create(title='神雕侠侣',publishDate='2013-12-12',price=188,publish=pub_obj)
    #第二种方法可以避免出版社不存在的问题

    多对多添加记录:

      书和作者是多对多的关系:一个书可以有多个作者,一个作者可以出版多本书

      步骤:先找到书对象

         再找到需要的作者对象

         给书对象绑定作者对象(用add方法),也就是绑定多对多的关系.

    #方式一
    #
    查找作者id xiaojiu =Author.objects.filter(name='xiaojiu').first() zhang = Author.objects.filter(name='zhang').first() #给书籍绑定作者 book_obj.authors.add(zhang,xiaojiu)

    #方式二

      pub_obj = Publish.objects.filter(name='苹果出版社').first()
      book_obj = Book.objects.create(title='苹果书',publishDate='2012-2-2',price=188,publish=pub_obj)
      authers = Author.objects.all()

      绑定多对多的关系
      book_obj.authors.add(*authers)

      

    解除绑定:remove

     # 将某个特定的对象从被关联对象集合中去除。    ======   book_obj.authors.remove(*[])

        book_obj = Book.objects.filter(title='小猪猪5').last() #找到对象
        authers = Author.objects.filter(nid__lt=3)#找到符合作者的对象
        book_obj.authors.remove(*authers) #因为是多个所以要加*

    清除绑定:clear”  #清空被关联对象集合。

      book_obj = Book.objects.filter(title='小猪猪4')#找到对象
        for book_obj_item in book_obj: #循环赋值
            book_obj_item.authors.clear()#清空关联对象

    总结:remove和clear的区别
      remove:得吧你要清除的数据筛选出来,然后移除
      clear:不用查,直接就把数据都清空了。
      各有应用场景

     

    一对一查询

    author和authordetile是一对一的关系

    正向查询(按字段author)

    反向查询(按表名authordeital):因为是一对一的关系了,就不用_set了。

        #一对一查询
        #正向查询: 手机号为110的作者姓名
        deital_obj = AuthorDetail.objects.filter(telephone=110).first()
        print(deital_obj.author.name)
    
        #反向查询:查询作者的手机号
        xiaojiu_obj = Author.objects.filter(name='xiaojiu').first()
        print(dir(xiaojiu_obj)) #如果找不到方法,用dir 看看这个对象都有什么方法
        print(xiaojiu_obj.authorDetail.telephone)

    一对多查询

    正向查询(按字段:publish):

    反向查询(按表名:book_set):

        #正向查询:查询 小红帽这本书的出版社地址
        book_obj = Book.objects.filter(title='小红帽')[0] #拿到书对象
        print('=======',book_obj.publish)
        print(book_obj.publish.city)
    
        #反向查询:查询苹果出版社都初版过哪本书和价格
        pub_obj = Publish.objects.filter(name='苹果出版社')[0]
        book_dic = pub_obj.book_set.all().values('price','title')[0]
        print(book_dic)
        print(book_dic['price'])
        #查询出版过的所有书籍
        book_list = pub_obj.book_set.all()
        for book_obj in book_list:
            print(book_obj.title,book_obj.price)

    庄杰大佬解答了我的疑惑, 在这里谢谢大佬,小弟甘拜下风

    多对多查询

    正向查询(按字段authorlist)

    反向查询(按表名book_set)

        #多对多查询
        #正向查询:查询小红帽这本书的所有作者的姓名和年龄
        book_obj = Book.objects.filter(title='小猪猪3')[0]
        print(book_obj.authors.all().values('name','age'))
    
        #反向查询 : 查询作者是xiaojiu的 出了那几本书
        #方法一: 使用book_set.all
    
        xiaojiu_obj = Author.objects.filter(name='zhang').first()
        alls= (xiaojiu_obj.book_set.all())
        for i in alls:
            print(i.title)
        #方法二:使用values
        author_list = xiaojiu_obj.book_set.all().values('title')
        for i in author_list:
            print(i.get('title'))

     你可以通过在 ForeignKey() 和ManyToManyField的定义中设置 related_name 的值来覆写 FOO_set 的名称。例如,如果 Article model 中做一下更改: publish = ForeignKey(Blog, related_name='bookList'),那么接下来就会如我们看到这般

    # 查询 人民出版社出版过的所有书籍
     
       publish=Publish.objects.get(name="人民出版社")
     
       book_list=publish.bookList.all()  # 与人民出版社关联的所有书籍对象集合

    基于双下划线的查询

    Django 还提供了一种直观而高效的方式在查询(lookups)中表示关联关系,它能自动确认 SQL JOIN 联系。要做跨关系查询,就使用两个下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的 model 为止。(相当于用sql语句用join连接的方式,可以在settings里面设置,可查看sql语句)

    一对多查询

    #练习1、苹果出版社出版过的所有的书的价格和名字
       
        #基于双下划线的查询方式
        #第一种查法
        ret = Publish.objects.filter(name='苹果出版社').values('book__title','book__price')
        # #先找到苹果出版社这个对象,之后再通过vlaues取值,book表下的什么字段中间用__链接,固定写法
        print(ret)
        #第二种查法
        #对比第一种方法,这种方法就是先join publish表,之后再进行取值。
        ret2 = Book.objects.filter(publish__name='苹果出版社').values('title','price')
        print(ret2)
      #练习2、查询手机号以11开头的作者出版过的所有书的名称以及出版社的名称
        #方式1
        author_obj = AuthorDetail.objects.filter(telephone__startswith='11').first()#找到以11开头的电话号,获取AuthorDetail 主键对象
    
        print(author_obj.author.book_set.all().values('title','publish__name'))#获取该条件所对应的作者,获取所有书籍对象信息,通过values取值。
        #方式2
    
        ret =Book.objects.filter(authors__authorDetail__telephone__startswith='11').values('title','publish__name')
        print(ret)

    聚合查询与分组查询(很重要!!!)

    聚合查询:aggregate(*args, **kwargs),只对一个组进行聚合

    #查询 所有书籍价格的平均值
        print(Book.objects.all().aggregate(Avg('price')))

    aggregate()QuerySet 的一个终止子句(也就是返回的不再是一个QuerySet集合的时候),意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。

        print(Book.objects.all().aggregate(avgprice =Avg('price')))

    如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:

    print(Book.objects.all().aggregate(Avg('price'),Max("price"),Min("price")))
    
    #输出
    {'price__avg': 186.222222, 'price__max': Decimal('200.00'), 'price__min': Decimal('100.00')}

    分组查询 :

    annotate():为QuerySet中每一个对象都生成一个独立的汇总值。

     是对分组完之后的结果进行的聚合

        #统计每一本书的作者数量
        #方式一
        print(Book.objects.all().annotate(authorNum = Count('authors__name')).values('authorNum'))
        #方式二
        book_list = Book.objects.all().annotate(authorNum = Count('authors__name'))
        for book_obj in book_list:
            print(book_obj.title,book_obj.authorNum)

    #统计每一个出版社最便宜的书

      #方式一
        print(Book.objects.values('publish__name').annotate(MinPrice=Min('price')))
    
        #方式二
        print(Publish.objects.all().annotate(minprice=Min('book__price')).values('name','minprice'))
    
        #方式三
        publishlist = Publish.objects.annotate(minprice =Min('book__price'))
        for pub_obj  in publishlist:
            print(pub_obj.name,pub_obj.minprice)

    #打印以py开头的书籍对应的作者个数

       print(Book.objects.filter(title__startswith='py').annotate(au_count = Count('authors__name')).values('au_count'))
    #根据一本书的作者数量多少对查询集QuuerySet进行排序
        print(Book.objects.all().annotate(Num=Count('authors__name')).order_by('Num').values('title'))
      #获取book所有对象,创建分组Num 统计作者的数量,通过order_by进行排序, values取值

       #查询各个作者出的书的总价格

    #方式一
    print(Author.objects.all().annotate(pricesum = Sum('book__price')).values('name','pricesum'))
    #方式二
    print(Book.objects.values('authors__name').annotate(pricesum = Sum('price')).values('authors__name','pricesum'))

    F与Q查询

    F查询:

    在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?

    Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。

    #查询书籍价格小于100元并且作者年龄=2017的书

    print(Book.objects.filter(Q(price__lt='100')&Q(authors__authorDetail__birthday__year=2017)).values('title'))

    #修改也可以使用F函数,比如说 书籍ID为4的书籍价格上涨100元

     print(Book.objects.filter(nid=4).update(price=F('price')+100))

    #Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。

    #查询书记价格小于作者年龄*2的书籍

        print(Book.objects.filter(price__lt=F('authors__age')*2).values('title'))

    Q查询:

    filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR 语句),你可以使用对象

    # 查询书籍价格小于100元并且作者年份等于2017的书

    print(Book.objects.filter(Q(price__lt='100')&Q(authors__authorDetail__birthday__year=2017)).values('title'))

    #查询评论数大于100或者阅读数小于200的书

    print(models.Book.objects.filter(Q(commentNum__gt=100)|Q(readNum__lt=200)))
    Q 对象可以使用& 和| 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。

    #查询年份等于2017年或者价格大于200的书

     print(models.Book.objects.filter(Q(publishDdata__year=2017)|Q(price__gt=200)))

    #查询年份不是2017年或者价格大于200的书

    print(models.Book.objects.filter(~Q(publishDdata__year=2017)&Q(price__gt=200)))

    与:&Q

    或:|Q

    非:~Q

    注意:

      bookList=models.Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),
                                      title__icontains="python"
                                     )

    查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。例如:

  • 相关阅读:
    微信输入文字和更多切换时不改变下面layout大小
    bitmap1 = bitmap2导致bitmap1不能使用一致报回收错误解决
    Mac下命令行打开Sublime
    android.content.res.Resources$NotFoundException:String resource ID #0x86
    SEGGER RTT STOP/SLEEP 模式下使用
    STM32中用 stop 模式 配合低功耗模式下的自动唤醒(AWU) 能否实现FreeRTOS tickless 模式
    NRF52832 能烧写代码 但是不运行 ,是因为没有烧写协议栈
    NRF52832 Logger module 设置
    jlink RTT 打印 BUG , FreeRTOS 在开启 tickless 模式下 无法使用的问题
    gattAttribute_t 含义 中文解释
  • 原文地址:https://www.cnblogs.com/mjiu/p/9888000.html
Copyright © 2020-2023  润新知