• 模型层之多表操作


    1.创建模型

    class Publish(models.Model):
        nid=models.AutoField(primary_key=True)
        name=models.CharField(max_length=32)
        addr=models.CharField(max_length=64)
        email=models.EmailField()
        
    class Author(models.Model):
        nid=models.AutoField(primary_key=True)
        name=models.CharField(max_length=32)
        age=models.IntegerField()
        # 一对一的关系,就要用onetoone,关联字段放在那都可以,如果用ForeignKey要指定unique=True
        # authordatil=models.ForeignKey(to='AuthorDetail',to_field='nid',unique=True)
        authordatil=models.OneToOneField(to='AuthorDetail',to_field='nid' )
        def __str__(self):
            return self.name
        
    class AuthorDetail(models.Model):
        nid=models.AutoField(primary_key=True)
        phone=models.CharField(max_length=32)
        email=models.EmailField()
    
    class Book(models.Model):
        nid=models.AutoField(primary_key=True)
        name=models.CharField (max_length= 32,null=True )
        price=models.DecimalField(max_digits=5,decimal_places=2)
        pub_date=models.DateField()
        # 一旦确立的一对多的关系,关联字段一定要放在多的表
        publish=models.ForeignKey(to=Publish,to_field='nid')
        # 一旦确立多对多的关系,管理字段放在那都可以
        authors=models.ManyToManyField(to='Author')
    #     django 它会创建第三张表,表里有三个字段  id   book_id   author_id  并且会做外键关联
        comments=models.IntegerField (null=True )
        reading=models.IntegerField (null=True )
        # 新增字段时设置默认值default ='xx'或者 null=True 
        def __str__(self):
            return self .name
    

    2. 添加记录

    2.1 一对一(Author与AuthorDetail)

    方式一:先创建被关联表 ,类的字段等于对象
    authordatil=AuthorDetail .objects .create(phone= '554434',email= '123433@qq.com')
    print(authordatil)  #AuthorDetail object
    author=Author .objects .create(name='egon',age=33,authordatil=authordatil)
    #类Author下的字段authordatil等于没有外键的表authordatil的对象
    print(author)  #Author object
    
    方式二:指定关联表字段authordatil_id直接赋值
    author=Author.objects .create(name='qqc',age=44,authordatil_id= 1)
    print(author)
    

    2.2 一对多(Publish与Book)

     (1)先添加被关联的表publish,关联表book的外键等于publish对象
    publish=Publish.objects .create(name='南京出版社',addr='南京西路',email= 'df54g6@qq.com')
    print(publish )
    book=Book.objects.create(name='西域记',price=33.1,pub_date= '2016-5-3',publish=publish )
    
    (2)关联表Book指定外键id 添加
    book=Book .objects .create(name='平凡人生',price=54,pub_date='2011-4-22',publish_id= 1)
    print(book)
    

    2.3 多对多(Book与Author)

    注:
    book=Book.objects.create(name='python',price=53,pub_date='2009-5-23',publish_id= 2)
    # print(book.name) #红楼梦
    # print(book.authors.all(),type(book.authors ))   #book.authors是manager对象
    #结果: < QuerySet[] > <class 'django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager'>
    
    (1)通过关联关系add(),括号内传对应表的作者ID,可传多个ID用逗号分开,
    也可以*(作者对象,作者对象)
    res=book.authors.add(1)
    # res=book.authors .add(*(1,2))
    
    (2)add() 括号传作者对象(可以是多个作者对象)
     book=Book.objects .create(name='java',price=43,pub_date= '2019-4-6',publish_id= 1)
    author=Author .objects .filter(pk=2).first()
    # print(author.name)  # qqc
    book.authors .add(author)
    -----------------------------------------
    # 注:author=Author .objects .filter(pk=2)结尾不加first() 结果为<QuerySet [<Author: Author object>]>,author.name报错;
    #加first(),结果为Author object ,author.name可以查到对应的值
    

    3. 解绑操作

    3.1 remove()

    注:括号既可以传对象,又可以传author_id,既可以传多个,又可以传一个
    (1)传author_id 值
     book=Book.objects .filter(pk=6) .first()  #假如表中书的ID6,与作者ID1关联
    ret=book.authors.remove(1)  #括号中传作者id 解除第三张表中book_id=6和author_id=1的绑定关系
    print(ret) # 返回值None
    #传多个ID值解绑
    # book=Book.objects.filter(pk=4).first()
    # ret=book.authors .remove(1,2)
    
    (2)传对象
    book=Book .objects.filter(pk=3).first()
    author=Author .objects .filter(pk=2) .first()
    ret=book.authors.remove(author)
    print(ret)
    

    3.2 clear 一次性全部解除绑定关系

    book=Book.objects .filter(pk=2).first()
    book.authors.clear() #解除与book_id=2 相关联的绑定关系
    

    3.3 set()

    注:set先清空再添加  传参数,参数必须是可迭代对象,可以传ID也可以传对象
    (1)传被关联表ID
    # 将book_id=6相关联的记录清空,再添加个author_id=1与book_id=6相关联
    book=Book.objects .filter(pk=6) .first()
    book.authors.set([1])
    
    (2)传对象
    book=Book.objects .filter(pk=7).first()
    author=Author.objects .filter(pk=1) .first()
    book.authors.set([author])
    
    ------------------------------------
     book=Book.objects .filter(pk=3) .first()
    ret=book.authors.all() # author对象 拿到与book__id=3相关联的author对象
    print(ret,type(ret))
    # <QuerySet[< Author: Authorobject >,< Author: Authorobject >]> <class 'django.db.models.query.QuerySet'>
    

    4. 基于对象的多表查询(子查询)

    4.1 一对一

    (1) 正向查,按字段
    例:查询egon号码 将自己的外键ID作为筛选条件得到AuthorDetail对象
     egon=Author.objects .filter(name='egon') .first()
    # print(egon)   #Author object
    authordatil = AuthorDetail.objects.filter(pk=egon.authordatil_id)
    # print(authordatil)  #<QuerySet [<AuthorDetail: AuthorDetail object>]>
    # print(egon.authordatil.phone)  # 得到具体的号码 554434
    
    # 注:authordatil后加first(),再点相关字段,也可得到相对应的值
    # print(egon.authordatil,type(egon.authordatil)) #AuthorDetail object <class 'app01.models.Autho
    
    (2)反向查,表名小写
    例:查询号码是554255 的作者 
    authordatil = AuthorDetail.objects.filter(phone='554255').first()
    print(authordatil.author)
    

    4.2 一对多

    Book表(关联外键)                     Publish表
    
    正向查询 book-->publish      book表去查publish  按字段查
    反向查询 publish-->book      publish表去查book表  按表名小写_set
    -----------------------------------------
    (1)红楼梦是哪个出版社出版 (正向查)
    # book = Book.objects.filter(name='红楼梦').first()
    # publish= Publish .objects .filter(pk=book.publish_id).first()
    # print(book.publish.name)
    
    (2)北京出版社出版的书 (反向查)
    publish= Publish .objects .filter(name='北京出版社').first()
    # print(publish.book_set,type(publish .book_set))
    # app01.Book.None <class 'django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager'>
    print(publish.book_set.all())
    

    4.3 多对多

    (1) 正向 按字段查
    #三国这本书的作者
    book=Book .objects.filter(name='三国').first()
    print(book.authors.all())
    
    (2)qqc写的的所有的书  (反相查,表名小写_set)
    author=Author.objects.filter(name='qqc') .first()
    print(author.book_set.all())
    

    4.4 查询总结

    A表book(关联自动段)          B表 publish
            # 正向查询   A--->B    
            # 反向查询   B-->A 
    总结:一对一  正向:按字段  反向:按表名小写
          一对多  正向:按字段  反向:按表名小写_set
          多对多  正向:按字段  反向:按表名小写_set    
    

    5. 基于双下划线的多表查询(联表查询)

    5.1 一对一

     # 例:查看egon 号码
    (1)正向
    author=Author.objects .filter(name='egon').values('authordatil__phone')
    print(author)
    
    (2)反向
    ret = AuthorDetail.objects.filter(author__name='egon').values('phone')
    print(ret)
    

    5.2 一对多

    #例:北京出版社出版的所有书籍的价格,名字
    (1) 正向查
    res=Book .objects .filter(publish__name='北京出版社').values('name','price')
    print(res)
    
    (2)反向
    res=Publish .objects .filter(name='北京出版社').values('book__name','book__price')
    print(res)
    

    5.3 多对多

    #例:egon写的书
    (1)正向查
     book=Book .objects .filter(authors__name='egon').values('name')
    print(book)
    
    (2)反向查
     author=Author.objects .filter(name='egon').values('book__name')
    print(author)
    

    5.5 总结

    总结:用__告诉orm,要连接那个表
        一对一: 正向:按字段  反向:按表名小写 
        一对多:  正向:按字段  反向:按表名小写 
        多对多:  正向:按字段  反向:按表名小写
        
    例:
     #手机号以55开头的作者出版过的所有书籍名称以及出版社名称
    # res=AuthorDetail .objects .filter(phone__startswith= 55) .values('author__book__name','author__book__publish__name')
    # res=Author .objects.filter(authordatil__phone__startswith=55).values('book__name','book__publish__name')
    # res= Book.objects.filter(authors__authordatil__phone__startswith=55).values('name','publish__name')
    # res=Publish.objects.filter(book__authors__authordatil__phone__startswith=55) .values('book__name','name')
    # print(res)
    

    6. 聚合函数

    注:aggregate(*args, **kwargs)
    aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。
    键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。
    例:
    from django.db.models import Avg,Count,Max,Min,Sum
    res=Book.objects.all().aggregate(c=Avg('price'))
    res=Book.objects .all().aggregate(m=Max('price'))
    res=Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
    print(res)
    

    7. F查询和Q查询

    7.1 F查询

    F()的实例可以在查询中引用字段,对字段进行比较
    例:
    #查阅评论数大于阅读数的书
    from django.db.models import F, Q
    res= Book .objects .filter(comments__gt=F('reading')).values('name')
    print(res)
    
    #将所有书的价格加一
    res=Book.objects .all().update(price=F('price')+1)
    print(res)
    

    7.2 Q查询

    (1)filter()等方法中的关键字参数查询都是一起进行“AND”的。通过Q可以进行(| 或  & 和)操作
    # 查找名字是三国或者价格是14的书
    res=Book.objects.all().filter(Q(name='三国')|Q(price='14'))
    (2)~ 取反  除什么之外,加括号优先级高或者按照and ,or ,not
    res =Book.objects .all().filter(~(Q(name='三国')|Q(price='14')))
    print(res)
    注:查询函数可以混合使用Q 对象和关键字参数,如果出现Q 对象,它必须位于所有关键字参数的前面
    

    8. 分组

    与sql语法对应关系:
    # values()在annotate()之前代指group by ,之后代指select,不写values,默认以基表的主键做group by,;
    filter() 在annotate()之前代指where,之后代指having;
    ---------------------------------------
    #例:统计不止一个作者的图书
    from django.db.models import Count,Min,Max,Sum
    ret=Book .objects .all().values('name').annotate(author_num=Count('authors__name')).filter(author_num__gt=1).values('name')
    print(ret)
    
    #查询各个作者出的书的总价格:
    # ret=Book.objects.all().values('authors__name').annotate(s=Sum('price')) .values('authors__name','s')
    # print(ret) #以book为基表查找,以作者名进行分组
    # ret = Author .objects .all().annotate(s=Sum('book__price')) .values('name','s')
    # print(ret) #以author为基表,默认以他的主键分组
    
    # 查询每个出版社的名称和书籍个数
    ret=Publish.objects .all().annotate(s=Count('book__name')).values('name','s')
    ret =Book .objects .all().values('publish__name').annotate(s=Count('nid')).values('publish__name','s')
    print(ret)
    -----------------------------------------
    注:annotate()为调用的QuerySet中每一个对象都生成一个独立的统计值(统计方法用聚合函数)。
    总结 :跨表分组查询本质就是将关联表join成一张表,再按单表的思路进行分组查询
    
  • 相关阅读:
    Dede CMS如何在文章中增加“附件下载”操作说明
    仿站模仿的三个网站
    PHP面相对象中的重载与重写
    面向对象思想
    最常用的正则表达式
    PHP第二阶段学习 一、php的基本语法
    PHP isset()与empty()的使用区别详解
    mysql索引总结----mysql 索引类型以及创建
    MySQL实现当前数据表的所有时间都增加或减少指定的时间间隔
    T-SQL语句以及几个数据库引擎
  • 原文地址:https://www.cnblogs.com/quqinchao/p/11220132.html
Copyright © 2020-2023  润新知