Django-orm:单表查询、基于对象和双下划线的多表操作、集合查询、分组查询、F查询和Q查询

############################################## 单表操作 ##############################################
简单查:
    models.User.objects.filter(id=3)               # queryset对象,相当于一个列表,放了一个或多个User对象
    models.User.objects.filter(id=3).first()    # User对象,相当于只去了queryset对象中的第一个

增:
    第一种:# 先创建数据,再保存
        user = models.User(username='alex',password='a654321')
        user.save()
    第二种:# 直接保存或先创建再保存
        1.方式一:
        models.User.objects.create(username='alex',password='a654321')
        2.方式二:
        dic={"username":"alex","password":'a654321'}
        models.Book.object.create(**dic)

删:
    方式一:# 删除所查出queryset对象中所有对象
        models.User.objects.filter(id=3).delete()
    方式二:# 删除所查出的user对象
        user = models.User.objects.filter(id=3).first()
        user.delete()

改:
    方式一:# 将修改所查到queryset对象中所有的对象
        models.User.objects.filter(id=3).update(username='egon',password='aa123')
    方式二:# 只修改所查到的user对象,一般在收集日志的时候用此方法,因为有save()动作
        user = models.User.objects.filter(id=3).first()
        user.username='egon'
        user.password='aa123'
        user.save()
        
查:
    查询相关API:
        <1>filter(**kwargs):      它包含了与所给筛选条件相匹配的对象
        <2>all():                 查询所有结果
        <3>get(**kwargs):         返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
        #---------下面的方法都是对查询的结果再进行处理:比如 objects.filter().values()--------
        '''
            **kwargs:代表以键值(字段=值)的方法传参
            *field:代表只传键(字段),不传值
        '''
        <4>values(*field):        返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列
        <5>exclude(**kwargs):     它包含了与所给筛选条件不匹配的对象  # 可直接用,也可用在filter之后
        <6>order_by(*field):      对查询结果排序              # 只能用在queryset之后
        <7>reverse():             对查询结果反向排序          # 必须用在order_by之后
        <8>distinct():            从返回结果中剔除重复纪录  # 一般用在values之后,对某一字段进行去重
        <9>values_list(*field):   它与values()非常相似,它返回的是一个元组序列  # 将values()字典中的值拿了出来,组成元组
        <10>count():              返回数据库中匹配查询(QuerySet)的对象数量。  # 只能用在queryset对象之后
        <11>first():              返回第一条记录
        <12>last():               返回最后一条记录
        <13>exists():             如果QuerySet包含数据,就返回True,否则返回False。  # 只能用在queryset之后
    
    基于双下划线__的模糊查询:
        models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值
        models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
        models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in
        models.Tb1.objects.filter(id__range=[1, 2])      # 范围bettwen and
        models.Tb1.objects.filter(name__contains="ven")  # name中包含ven的,对大小写敏感
        models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
        ....startswith(以什么开头)
        ....istartswith(返回Bool值)
        ....endswith(以什么结尾)
        ....iendswith(返回Bool值)
        

        
# ****************小Tip***************
    1.user = models.User.objects.filter(id=3)
      print(user.query)  # 打印其原生SQL
    2.pk即id,pk指主键,一般id也作为主键
    3.models中,在创建一对一和一对多关系时,需要添加约束:"on_delete=models.CASCADE",意为在删除被关联字段时,必须删除其关联的所有字段,称为“级联删除”
    4.正向查询:关联字段在当前表中,从当前表向外查
      反向查询:关联字段不在当前表中,从当前表向外查
# ************************************


############################################## 多表操作 ##############################################
多表操作相对于单表来说只是多了约束条件,只要在操作时考虑到相关联的表就行了

例:'''
    author表:name
    detail表:age,sex,addr
    book表:name,pub_date
    publisher表:name,addr
    
    author和detail之间存在一对一关系,其关联字段'det_auth'写在detail中
    book和publisher之间存在一对多关系,其关联字段'book_pub'写在book中
    book和author之间存在多对多关系,其关联字段'book_auth'写在book中
    '''
    
增:
    # ********顺序为先创建被关联方,再创建关联方********
    一对一(OneToOne):
        models.Author.objects.create(name='alex')
        models.Detail.objects.create(age=38,sex='male',det_auth_id=1)  # 其中det_auth_id为关联字段,必须得写,_id是django自己加上去的(也可换成pk)
        或
        author = models.Author.objects.create(name='alex')
        models.Detail.objects.create(age=38,sex='male',det_auth=author)  # 传对象方式
    一对多(ForeignKey):#与一对一相似
    多对多(ManyToMany):
        book = models.Book.objects.filter(id=3).first()
        book.book_auth.add(1,2)  # 在给id为3的书添加了id为1和2的两个作者
        或
        book = models.Book.objects.filter(id=3).first()
        author1 = models.Author.objects.get(id=1)
        author2 = models.Author.objects.get(id=2)
        book.book_auth.add(author1,author2)  # 传对象方式

删:
    一对一and一对多:
        # ********删除被关联方字段,其所有的关联方字段都会被删除********
        models.Author.objests.filter(id=3).delete()
        models.Publisher.objests.filter(id=3).delete()
    多对多:
        book = models.Book.objects.filter(id=3)
        方式一:
            book.book_auth.clear()  # 将book下关联的所有都删掉
        方式二:
            book.book_auth.remove(1,2)  # 传id方式
            或
            author1 = models.Author.objects.get(id=1)
            author2 = models.Author.objects.get(id=2)
            book.book_auth.remove(author1,author2)  # 传对象方式
    
改:
    一对一and一对多:
        models.Detail.objects.update(det_auth_id=3)
        或 
        author = models.Author.objects.get(id=3)
        models.Detail.objects.update(det_auth=author)  # 传对象方式
    多对多:
        book = models.Book.objects.filter(id=3)
        方式一:
            全部clear,再add
        方式二:
            book.book_auth.set([3,])  # 传id方式
            或 
            author1 = models.Author.objects.get(id=1)
            author2 = models.Author.objects.get(id=2)
            book.book_auth.set(author1,author2)  # 传对象方式
    
查:
    # ******************基于对象的跨表查询(子查询)*********************
    '''
        总结:
            已知谁以谁为基表进行查询
            一对一:正向查询按关联字段,反向查询按表名小写
            一对多:正向查询按关联字段,反向查询按表名小写_set.all()(因为可能查到多条数据)
            多对多:正向查询按关联字段.all(),反向查询按表名小写_set.all()(因为可能查到多条数据)
    '''
    一对一:
        例1:查询地址为北京的人的名字
            detial = models.Detail.objects.filter(addr='北京').first()
            print(detail.det_auth.name)  # 正向查
        例2:查询alex这个人的地址
            author = models.Author.objects.filter(name='alex').first()
            print(author.detail.addr)  # 反向查
    一对多:
        例1:查询红楼梦这本书的出版社地址
            book = models.Book.objects.filter(name='红楼梦').first()
            print(book.book_pub.addr)  # 正向查
        例2:查询南京出版社出版的所有书的名字
            publisher = models.Publisher.objects.filter(name='南京出版社').first()
            books = publisher.book_set.all()  # 反向查,查到的是个queryset对象(可迭代)
            for book in books:
                print(book.name)
    多对多:
        例1:查询红楼梦这本书的所有作者
            book = models.Book.objects.filter(name='红楼梦').first()
            authors = book.book_auth.all()  # 正向查,查到的是个queryset对象(可迭代)
            for author in authors:
                print(author.name)
        例2:查询作者为alex写的所有书
            author = models.Author.objects.filter(name='alex').first()
            books = author.book_set.all()  # 反向查,查到的是个queryset对象(可迭代)
            for book in books:
                print(book.name)
        
    # ******************基于双下划线__的跨表查询(子查询)*********************
    '''
        总结:
            1.基表可以是已知方,也可以是未知方,但必须相关
            2.基本上已经不区分一对一、一对多、多对多
        3.本质就是用了sql中的join连表查
            3.相比于基于对象查询:没有_set.all(),也不用关联字段.all()
                优势:1.写起来比较方便,需要记得东西少
             2.跨多表查询起来很方便
                劣势:查询速度较慢,查询出来的是值,而不是对象,所以不能用.的方式进行连表查
    '''
    一对一:
        例1:查询alex这个人的地址
            -以Author表作为基表:
                ret = models.Author.objects.filter(name='alex').values('name','detail__addr')  # 反向查
                print(ret)
            -以Detail表作为基表:
                ret = models.Detail.objects.filter(det_auth__name='alex').values('det_auth__name','addr')  # 正向查
                print(ret)
        例2:查询地址为武汉的人的名字
            -以Author表作为基表:
                ret = models.Author.objects.filter(detail__addr='武汉').values('name','detail__addr')  # 反向查
                print(ret)
            -以Detail表作为基表:
                ret = models.Detail.objects.filter(addr='武汉').values('det_auth__name','addr')  # 正向查
                print(ret)
    一对多:
        例1:查红楼梦这本书的出版社地址
            -以Book作为基表:
                ret = models.Book.objects.filter(name='红楼梦').values('name','book_pub__addr')  # 正向查
                print(ret)
            -以Publisher作为基表:
                rets = models.Publisher.objects.filter(book__name='红楼梦').values('book__name','addr')  # 反向查
                print(ret)
        例2:查询南京出版社出版的所有书的名字
            -以Book作为基表:
                rets = models.Book.objects.filter(book_pub__name='南京出版社').values('name')  # 正向查
                for ret in rets:
                    print(ret)
            -以Publisher作为基表:
                rets = models.Publisher.objects.filter(name='南京出版社').values('book__name')  # 反向查 
                for ret in rets:
                    print(ret)
    多对多:
        例1:查询红楼梦这本书的所有作者
            -以Book作为基表:
                rets = models.Book.objects.filter(name='红楼梦').values('name','book_auth__name')  # 正向查
                for ret in rets:
                    print(ret)
            -以Author作为基表:
                rets = models.Author.objects.filter(book__name='红楼梦').values('book__name','name')  # 反向查
                for ret in rets:
                    print(ret)
        例2:查询作者为alex写的所有书
            -以Book作为基表:
                rets = models.Book.objects.filter(book_auth__name='alex').values('name')  # 正向查
                for ret in rets:
                    print(ret)
            -以Author作为基表:
                rets = models.Author.objects.filter(name='alex').values('book__name')  # 反向查
                for ret in rets:
                    print(ret)
    终极练习(连跨多表查询):
        例:查询人民出版社出版过的所有书籍的名字及作者的姓名
            -以出版社作为基表
                rets = models.Publisher.objects.filter(name='人民出版社').values('book__name','book__book_auth__name')
            -以书作为基表
                rets = models.Book.objects.filter(book_pub='人民出版社').values('name','book_auth__name')
                print(rets)  # 打印出来是一个queryset对象中包含多个字典

################################## 聚合查询 #######################################
aggregate() # 返回结果是一个字典,键为django自动生成"字段名_函数名",值为所查询并计算的结果

from django.db.models import Count,Avg,Max,Min,Sum
ret = models.Book.objects.aggregate(Avg('price'))
ret = models.Book.objects.aggregate(Max('price'))
ret = models.Book.objects.aggregate(Min('price'),Count('price'),Sum('price'))
print(ret)

>>:{'price__min':'25.5','price__count':'35.8','price__sum':'40.3'}

################################## 分组查询 #######################################
annotate() # 返回的值是queryset对象,在annotate中算出的结果可赋给一个新值,可以作为后续查询的条件,也可作为结果
'''
  values在前,表示group by,在后,表示取值 
  filter在前,表示过滤(where),在后,表示having(对分组之后的结果再进行过滤)
'''

from django.db.models import Count,Avg,Max,Min,Sum

例1:统计每一本书的作者个数
  book_list = models.Book.objects.all().annotate(author_num=Count(authors)).value('name','author_num')
  print(book_list)
例2:统计每一个出版社的最便宜的书
  publishList=Publish.objects.annotate(MinPrice=Min("book__price"))
  for publish_obj in publishList:
    print(publish_obj.name,publish_obj.MinPrice)
例3:统计不止一个作者的图书:(作者数量大于1)
  ret=models.Book.objects.annotate(author_num=Count("authors")).filter(author_num__gt=1).values('name','author_num')
例4:根据一本图书作者数量的多少对查询集 QuerySet进行排序
  ret = Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')
例5:查询各个作者出的书的总价格
  ret = models.Author.objects.annotate(sum_price=Sum("book__price")).values("name", "sum_price")
例6:查询每个出版社的名称和书籍个数
  ret = models.Publish.objects.all().annotate(c=Count('book__name')).values('name','c')

#################################F 查询和Q查询 #####################################
from django.db.models import F,Q

F:取出表中字段对应的值
例1:查询评论数大于阅读数的书
  ret = models.Book.objects.filter(commit_num__gt=F('read_num'))

例2:把所有书的评论数+1
  ret = models.Book.objects.all().update(commit_num=F('commit_num')+1)

Q:查询,构造出与"&"、或"|"、非"~"的关系
  与:
  例:查询出书名为红楼梦并且价格为20的书
    ret = models.Book.objects.all().filter(Q(name='红楼梦')&Q(price=20))
  或:
  例:查询出书名为红楼梦或价格为20的书
    ret = models.Book.objects.all().filter(Q(name='红楼梦')|Q(price=20))
  非:
  例:查询出书名为不为红楼梦的书
    ret = models.Book.objects.all().filter(~Q(name='红楼梦'))
原文地址:https://www.cnblogs.com/gyk1030/p/10804037.html