day08 外键字段的增删查改


day08 外键字段的增删查改

今日内容概要

  • 外键字段的增删查改
  • 正反向查询的概念
  • 基于对象的跨表查询(子查询)
  • 基于双下划线的跨表查询(连表操作)
  • 聚合查询与分组查询
  • F查询和Q查询

前提准备

class Books(models.Model):  # 出版书
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)  # 浮点型
    publish_time = models.DateField(auto_now_add=True)
    # 建立外键
    publish = models.ForeignKey(to='Publish')  # 一对多
    author = models.ManyToManyField(to='Author')  # 多对多


class Publish(models.Model):  # 出版社
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=64)


class Author(models.Model):  # 作者
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    # 一对一
    author_detail = models.OneToOneField(to='AuthorDetail',null=True)


class AuthorDetail(models.Model):  # 作者详情
    phone = models.BigIntegerField()
    addr = models.CharField(max_length=32)

外键字段的增删查改

针对一对一、一对多字段操作

# 增的两种方式
	1、直接增加
    	models.Books.objects.create(title='圣王',price=111.11,publish_id=1)
        
	2、使用对象增加
        publish_obj = models.Publish.objects.filter(pk=2).first()   # 出版社对象
        models.Books.objects.create(title='阳神',price=222.22,publish=publish_obj)
        
# 改的两种方式
	1、直接修改
    	models.Books.objects.filter(pk=2).update(publish_id=1)
        
	2、使用对象修改
        publish_obj = models.Publish.objects.filter(pk=2).first()  # 出版社对象
        models.Books.objects.filter(pk=1).update(publish=publish_obj)  # 把书籍1改成出版社2

针对多对多字段操作

'''
多对多中向第三张表增加数据用:add
多对多中向第三张表修改数据用:set
'''
# 增(虚拟的第三张表)
	1、直接增加
        book_obj = models.Books.objects.filter(pk=2).first()  # 书籍2的和作者1关联
        book_obj.author.add(1)
        book_obj.author.add(1,2)  # 书籍也可以关联多个作者
        
	2、使用对象增加
        book_obj = models.Books.objects.filter(pk=1).first()  # 书籍1的对象
        author_obj = models.Author.objects.filter(pk=2).first()  # 作者2的对象
        book_obj.author.add(author_obj) # 书籍1关联作者2的对象,也可以放多个作者对象
        '''
        总结:add在第三张关系表中添加数据,
        括号内既可以传主键字段也可以传数据对象,并且支持传多个
        '''

# 改
	1、直接修改
	    book_obj = models.Books.objects.filter(pk=2).first()  # 书籍2的的对象
    	book_obj.author.set([2,])  # 书籍1的作者改成作者2
        # 注:set后面必须是可迭代对象,比如列表,元祖
        
	2、使用对象修改
        book_obj = models.Books.objects.filter(pk=2).first()  # 书籍2的的对象
        author_obj = models.Author.objects.filter(pk=1).first()  # 作者1的对象
        book_obj.author.set([author_obj])  # 把书籍2绑定的作者2改成作者1
        
        '''
        总结:set 在第三张关系表中修改数据
        	括号内需要传一个可迭代对象
        	可迭代对象里面的元素既可以传主键字段,也可以传数据对象,并且支持传多个	
        '''
        
# 删
	1、直接删除
        book_obj = models.Books.objects.filter(pk=2).first()  # 书籍2的的对象
        book_obj.author.remove(1)  # 移除书籍2绑定的作者1的数据
        
	2、使用对象修改
        book_obj = models.Books.objects.filter(pk=2).first()  # 书籍2的的对象
        author_obj = models.Author.objects.filter(pk=2).first() # 作者2的对象
        book_obj.author.remove(author_obj)  # 把书籍2绑定给作者2的数据移除
        '''
        总结:remove:
        	在第三张表关系中删除数据
        		括号内既可以传主键字段也可以传数据对象,并且支持传多个
        
        '''
        
	3、清空所有数据
        book_obj = models.Books.objects.filter(pk=2).first()  # 书籍2的的对象
        book_obj.author.clear()  # 清空书籍2的所有绑定关系
        '''
        总结:clear
        	在第三张表关系中清空所有的数据,并且括号内无需传值
        '''

正反向理论

'''
多对多外键、一对多外键建在books表中
一对一外键建在author表中
'''
正向查询
    书籍对象查出版社对象 外键字段在书表中   # 正向查询
    书籍对象查作者对象 外键字段在书表中     # 正向查询
    作者对象查作者详情 外键字段在作者中     # 正向查询
    
反向查询
	出版社查书籍对象 外键字段不在出版社表中  # 反向查询
    作者查书籍对象 外键字段不在作者表中      # 反向查询
    作者详情查作者 外键字段不在作者详情表中  # 反向查询 
"""
	查询数据的时候如果外键字段"在你的手上"则为正向查询
	如果外键字段不在则为反向查询
"""
*****************************************重要******************************************
# 口诀:
    正向查询按外键字段 ...  # 当关联多个时,后面加上.all()
    反向查询按表名小写 ...  # 当关联多个时,表名小写加上_set.all()

基于对象的跨表查询(子查询)正向查询

什么是子查询
	将一条SQL语句的查询结果当做另外一条SQL语句的查询条件
    
# 基于对象的跨表查询(分步查询)

    案例1:查询主键为2的书籍对应的出版社
        # 先查询书籍对象
        book_obj = models.Books.objects.filter(pk=2).first()
        # 再基于书籍对象查询出版社对象(书跟出版社的外键字段在书中 所以为正向)
        res = book_obj.publish
        print(res.name)
        
    案例2:查询主键为3的书籍对应的作者
        # 先查询书籍对象
        book_obj = models.Books.objects.filter(pk=3).first()
        # 再基于书籍对象查询作者对象(书跟作者的外键字段在书中 所以为正向)
        res = book_obj.author.all()  # 当多个值时,要加上all()
        print(res)  # , ]>
        for author_name in res:
            print(author_name.name)

    案例3:查询作者"老鹰吃小鸡"的详情数据
        author_obj = models.Author.objects.filter(name="老鹰吃小鸡").first()  # 作者详情
        res = author_obj.author_detail  # 正向查询按字段名
        print(res,res.phone) 
        
        '''
        总结:
            基于对象的正向查询,数据对象点了外键之后
            是否需要再点all,取决于关联的数据项有几个,单个无需点,多个则需要点
        '''

基于对象的跨表查询(反向查询)

案例1:查询东方出版社出版的书籍

	# 先查询东方出版社对象
    publish_obj = models.Publish.objects.filter(name='东方出版社').first()
    # 基于东方出版社对象对象查询书籍对象(东方出版社对象跟书籍的外键字段在书籍表中 所以为反向)
    res = publish_obj.books_set.all()
    print(res)  # , ]>
    
案例2:查询作者"老鹰吃小鸡"写过的书籍
    author_obj = models.Author.objects.filter(name='老鹰吃小鸡').first()
    res = author_obj.books_set.all()
    print(res)
    
案例3:查询电话是111的作者姓名
    author_detail_obj = models.AuthorDetail.objects.filter(phone=111).first()
    res = author_detail_obj.author
    print(res)
    
    '''
    总结:
    	如果查询出来的数据对象可以有多个,需要加上:表名小写_set.all()
    	如果查询出来的数据对象只有一个,表名小写
    '''

基于双下划线的跨表查询(连表操作)

# 双下划线的用法:values里面根据外键字段去到哪个表,如果要取那个字段,直接__字段名。
'''
values():指定查询字段,返回的列表套字典
values_list():指定查询字段,返回的是列表套元祖

'''
案例1:主键为2的书籍对应的出版社
	# 主键2的书籍对象,根据外键字段去到publish表中查找name字段
    res = models.Books.objects.filter(pk=2).values('publish__name')  # 正向查询
    print(res)  # 
    
案例2:查询主键为3的书籍对应的作者
    res = models.Books.objects.filter(pk=3).values('author__name')   # 正向查询
    print(res)   # 
    
案例3:查询作者"老鹰吃小鸡"的详情数据
    res = models.Author.objects.filter(name='老鹰吃小鸡').values('author_detail__phone','author_detail__addr')  # 正向查询
    print(res)
    # 
    
案例4:查询书籍pk为2的作者的电话
    res = models.Books.objects.filter(pk=2).values('author__name','author__author_detail__phone')
    print(res)
    # 
    
    '''
    总结:
    	使用双下划线查询比较方便,代码简洁
    	values():指定查询字段
    	values()里面可以一直根据外键字段点下去,然后在查找需要的字段
    '''

基于双下划线的跨表查询(反向查询)

# 双下划线的用法:values里面根据表名小写去到哪个表,如果要取那个字段,直接__字段名。

案例1:查询东方出版社出版的书籍
	# 基于东方出版社对象对象查询书籍对象(东方出版社对象跟书籍的外键字段在书籍表中 所以为反向)
    res = models.Publish.objects.filter(name='东方出版社').values('books__title')
    print(res)  # 反向查询:表名小写
    
案例2:查询作者"老鹰吃小鸡"写过的书籍
    res = models.Author.objects.filter(name='老鹰吃小鸡').values('books__title')
    print(res)
 
案例3:查询电话是111的作者姓名
    res = models.AuthorDetail.objects.filter(phone=111).values('author__name')
    print(res)

双下划线进阶操作

1、查询主键为2的书籍对应的出版社(不能点书籍)  # 所以只能点出版社
    res = models.Publish.objects.filter(books__pk=2)  # 拿到书籍2的对应出版社
    print(res)
    
2、查询主键为3的书籍对应的作者
    res = models.Author.objects.filter(books__pk=3)  # 拿到书籍的对应作者
    print(res)
    
3、查询东方出版社出版的书籍
    res = models.Books.objects.filter(publish__name='东方出版社')  # 拿到东方出版社的书籍
    print(res)
    
4、查询作者"老鹰吃小鸡"的写过的书籍
    res = models.Books.objects.filter(author__name='老鹰吃小鸡')
    print(res)
    
5、查询电话是111的作者姓名
    res = models.Author.objects.filter(author_detail__phone=111)
    print(res)  # ]>
    
'''
总结:
	代码相比其他更简便
	由没有条件的表来查询有条件的表
'''

聚合查询

集合函数:
	max min sun count avg
    
# 聚合函数在SQL语句是要配合分组使用的,在这里要想使用,需要aggregate

from django.db.models import Max,Min,Sum,Count,Avg   # 导模块

1、统计所有书籍的总价格
    res = models.Books.objects.aggregate(Sum('price'))  # 需要使用aggregate
    print(res)
    
2、统计所有书籍的总数
    res = models.Books.objects.aggregate(Count('pk'))  # 根据id来统计
    print(res)
    
3、统计价格最高的
    res = models.Books.objects.aggregate(Max('price'))  # 也可以放多个聚合
    res = models.Books.objects.aggregate(Max('price'),Min('price'))
    print(res)

分组查询

# 在哪张表就在哪里分组
'''author_num是必须要起的别名 因为后面的values中需要使用'''

1、统计每一本书的作者个数
    res = models.Books.objects.annotate
    (author_num=Count('author__pk')).values('author_num', 'title')
    
2、统计出每个出版社卖的最便宜的书的价格
	# 先根据主板社分组,之后再拿到便宜的书。
    res = models.Publish.objects.annotate(min_price=Min('books__price')).values('min_price','name')
    print(res)
    
3、统计不止一个作者的图书
	# 先根据书分组,然后在拿到作者,之后过滤图书作者大于1的
    res = models.Books.objects.annotate(author_num=Count('author__pk')).filter(author_num__gt=1)
    print(res)
    
4、查询各个作者出的书的总价格
    res = models.Author.objects.annotate(price_sum=Sum('books__price')).values('price_sum','name')
    print(res)
    # 
    
# 总结:
    1.value里面的参数对应的是sql语句中的select要查找显示的字段
    2.filter里面的参数相当于where或者having里面的筛选条件
    3.annotate本身表示group by的作用,前面找寻分组依据,内部放置显示可能用到的聚合运算式,后面跟filter来增加限制条件,最后的value来表示分组后想要查找的字段值

F与Q查询

# F的功能是获取数据库中字段原有的数据,并且F查询主要操作数字类型的数据。

1、将所有书籍的价格提升100块
	models.Books.objects.update(price=F('price')+1000)  # F把书籍中price数据都拿出来加1000
    
2、将所有书籍名称后面加上爆款后缀
    from django.db.models.functions import Concat
    from django.db.models import Value
    models.Books.objects.update(title=Concat(F('title'),Value('爆款')))
    # F查询要想操作字符串类型,需要导入上述的两个模块
    
3、查询库存数大于卖出数的书籍
    res = models.Books.objects.filter(kucun__gt=F('maichu'))
    print(res)
    
    '''
    总结:
    	F查询主要是操作数字数据的,要想操作字符需要导入两个模块
    	Concat表示进行字符串的拼接操作,参数位置决定了拼接是在头部拼接还是尾部拼接,Value里面是要新增的拼接值
    	比值的时候,可以先拿一个条件比,再使用F获取另外一个条件数据
    '''

Q查询

"""Q查询"""
    # filter括号内多个条件均为and关系
    1.查询主键为3或者卖出数为1000的数据
    res = models.Book.objects.filter(pk=3, maichu=1000)  # 逗号分隔为and关系
    from django.db.models import Q

    ###########基本用法###########
    res = models.Book.objects.filter(Q(pk=3), Q(maichu=1000))  # 逗号分隔为and关系
    res = models.Book.objects.filter(Q(pk=3) | Q(maichu=1000))  # |分隔为or关系
    res = models.Book.objects.filter(~Q(pk=3) | Q(maichu=1000))  # ~分隔为not关系
    print(res)
    ###########进阶用法###########
    filter(title='123') filter('title'='123')
    q = Q()  # 默认多个条件之间也是and关系
    q.connector = 'or'  # 也可以修改链接关系
    q.children.append(('title', '三国演义爆款'))
    q.children.append(('title__contains', '爆'))
    q.children.append(('price', 1123))
    res = models.Book.objects.filter(q)
    print(res)
    
'''
总结:
	用Q查询包起来条件,可以使用成员关系运算符
	,  为 and
	|   为 or
	~   为 not
'''

相关