django ORM外键连表查询

系列文章目录

初识Django

Django ORM表的创建和增删改查

django请求生命周期流程图和路由层

Django视图层,模版层

ORM单表关键字查询

文章目录

  • 系列文章目录
    • 外键一对多和一对一增删改查
    • 清除数据
    • 基于对象的跨表查询
    • 基于双下滑线的跨表查询
    • 双下划线进阶操作
    • 聚合查询:aggregate
    • 分组查询:annotate
    • 报错修改配置
    • 大小分组
    • F查询
    • Q查询
    • django orm开启事物操作
    • orm查询为惰性查询,要数据才会给出数据对象,不要就不给
    • orm查询优化only与defer
    • orm查询优化select_related与prefetch_related
    • orm常用字段
    • django1.X与django2.X外键字段使用的区别

外键一对多和一对一增删改查

   #增
   #直接录入相关的主键字段数据
   models.Book.objects.create(title='三国演义',price=123.32,publish_id=1)
   # 有主键就传主键,没主键就传数据对象
       publish_obj = models.Publish.objects.filter(pk=2).first()
       models.Book.objects.create(title='人性的弱点',price=55.78,publish=publish_obj



   #改
   models.Book.objects.filter(pk=2).update(publish_id=2)
       publish_obj = models.Publish.objects.filter(pk=1).first()
       models.Book.objects.filter(pk=3).update(publish=publish_obj)



外键字段多对多字段操作

   #增
    book_obj = models.Book.objects.filter(pk=2).first()
       book_obj.authors.add(1)  # 书籍对象点多对多虚拟字段就相当于在操作第三张关系表
       book_obj.authors.add(1,2)



    #增
     book_obj = models.Book.objects.filter(pk=2).first()
     author_obj = models.Author.objects.filter(pk=3).first()
     author_obj1 = models.Author.objects.filter(pk=2).first()
     book_obj.authors.add(author_obj,author_obj1)
      """
       add
           在第三张关系表中添加数据
               括号内既可以传主键字段也可以传数据对象 并且都支持传多个
       """



   #改
   book_obj = models.Book.objects.filter(pk=2).first()
   book_obj.author.set([1,])
   book_obj.authors.set([1,2])
   
   
    author_obj = models.Author.objects.filter(pk=1).first()
  book_obj.authors.set([author_obj])
   author_obj1 = models.Author.objects.filter(pk=2).first()
   book_obj.authors.set([author_obj])
  book_obj.authors.set([author_obj,author_obj1])
       
       
       """
       set
           在第三张关系表中修改数据
               括号内需要传一个可迭代对象
                   可迭代对象里面的元素既可以传主键字段也可以传数据对象 并且都支持传多个
       """





   # 删
       book_obj = models.Book.objects.filter(pk=2).first()
       book_obj.authors.remove(1)
       book_obj.authors.remove(1,2)
       author_obj = models.Author.objects.filter(pk=1).first()
       author_obj1 = models.Author.objects.filter(pk=2).first()
       book_obj.authors.remove(author_obj)
       book_obj.authors.remove(author_obj, author_obj1)
       """
       remove
           在第三张关系表中删除数据
               括号内既可以传主键字段也可以传数据对象 并且都支持传多个
       """

清除数据

   book_obj = models.Book.objects.filter(pk=2).first()
       book_obj.authors.clear()  # 清空当前书籍与作者的所有绑定关系
       """
       clear
           在第三张关系表中清空数据
               括号内无需传值
       """ 

基于对象的跨表查询

1、查询主键为的书籍

   book_obj=models.Book.objects.filter(pk=2).first()
   res = book_obj.publish
       print(res, res.pk, res.name, res.addr)

2.查询主键为3的书籍对应的作者

   book_obj = models.Book.objects.filter(pk=3).first()
   res = book_obj.authors
   print(res) 
   
   #输出结果
   app01.Author.None
   res = book_obj.authors.all()
   print(res)
   for author_obj in res:
          print(author_obj.pk)
          print(author_obj.name)
          print(author_obj.age)
   # 可以拿到所有的对象

3、查询卡耐基的详细数据

   # 先查询作者对象
   author_obj = models.Author.objects.filter(name='卡耐基').first()
   res = author_obj.author_detail
   print(res,res.phone)
   



总结:基于对象的正向查询,数据对象点了外键字段之后,是否需要在点all 取决于关联的数据有几项,单个无须点,多个则需要点

4、查询北京科技出版社出版的书籍

   # 先查询北京科技出版社对象
   publish_obj = models.Publish.objects.filter(name='北京科技出版社').first()
   # 基于北京科技出版社对象查询书籍对象(北京科技出版社对象跟书籍的外键字段在书籍表中,所以为反向)

查询zz写过的书

   # 先查询zz对象
   author_obj = models.Author.objects.filter(name='卡耐基').first()
   # 在基于对象查询书籍对象,对象跟书籍的外键字段在书籍表中 所以为反向
   res = author_obj.book_set.all()
   print(res)

查询电话是432的作者姓名

查询432详情对象
   author_obj = models.AuthorDetail.objects.filter(phone=432).first()
   res = author_detail_obj.author
   print(res)   
  • 基于对象的反向查询
    • 如果查询出来的数据对象可以有多个,那么需要表名小写_set.all(),如果查询出来的数据对象只有一个,表名小写即可

基于双下滑线的跨表查询

1、查询主键为2的数据对应的出版社

    res = models.Book.objects.filter(pk=2).values('publish__name','publish__addr')

2、查询主键为3的书籍对应的作者

    res = models.Book.objects.filter(pk=2).values_list('author__name','author__age')
    print(res)

3、查询zz的详细数据

    res = models.Author.objects.filter(name='zz').values('authors_detail__phone','authors_detail__addr')

4、查询书籍pk为2的作者电话

    res = models.Book.objects.filter(pk=2).values('author__')

5、查询北方出版社出版的书籍

    res = models.Publish.objects.filter(name='四川科技出版社').values('book__title','book__price')
        print(res)

查询电话是432的作者姓名

    res = models.AuthorDetail.objects.filter(phone=432).values('author__name')
    print(res)

双下划线进阶操作

查询主键为2的书籍对应的出版社(不能点书籍)

    res = models.Publish.objects.filter(book__pk=2)

2.查询主键为3的书籍对应的作者

     res = models.Author.objects.filter(book__pk=2)
    print(res)  # , ]>


3、查询高得的详细数据

    res = models.AuthorDetail.objects.filter(author__name='高得')
    print(res)  # ]>

4、查询上海联合出版社出版的书籍

    res = models.Book.objects.filter(publish__name='上海联合出版社')
        print(res)

5、查询zz写过的书

    res = models.Book.objects.filter(authors__name='zz')
    print(res)
        
    # ]>

6.查询电话是982的作者姓名

    res = models.Author.objects.filter(author_detail__phone=982)
    print(res)

聚合查询:aggregate

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

统计书的总价格

    res = models.Book.objects.aggregate(Sum('price'))
     print(res)

统计书的总数

    res = models.Book.objects.aggregate(Count('pk'))
    print(res)
    #{'pk__count': 6}

分组查询:annotate

1、统计每一本书的作者个数

报错修改配置

    vim /usr/local/etc/my.cnf 
    
    [mysqld]
    
    sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'

    res =
    models.Publish.objects.annotate(min_price=Min('book__price')).values('min_price','name','book__title')
    print(res)

2、统计每个出版社卖出最便宜的书

    res = models.Publish.objects.annotate(min_price=Min('book__price')).values('min_price','name','book__title')
    print(res)

3.统计不止一个作者的图书

    res = models.Book.objects.annotate(author_num=Count('authors__pk')).filter(author_num__gt=1)
    print(res)


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

    res = models.Author.objects.annotate(price_sum=Sum('book__price')).values('name','price_sum')
    print(res)

大小分组

        """
        1.大的分组
            models.表名.objects.annotate  按照models后面的表名分组(主键)
        2.小的分组
            models.表名.objects.values('price').annotate()  按照values括号内指定的字段分组
        """

F查询

功能:获取数据库中字段原有的数据

模块导入

    from django.db.models import F

1、将所有的数据价格提升100块

    models.Book.objects.update(price=F('price')+100)
    

2、将所有书后加上爆款

    from django.db.models.functions import Concat
    from django.db.models import Values
    res = models.Book.objects.update(title=Cancat(F('title'),Value('爆款'))) 
    
    """
    F查询默认操作数字类型 如过需要操作字符串类型需要以上操作
    """

3、查询库存数大于卖出数的书籍

    res = models.Book.objects.filter(kucun__gt=F('maichu'))
    print(res)
    
    #]>

Q查询

filter括号内多个条件均为and关系


查询主键为3或着卖出数为1000的书籍

    #基本用法
    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)
    
    #进阶用法
    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)

django orm开启事物操作

    from django.db import transaction
    
      事务
        买一本 跟jason学Linux 书
        在数据库层面要做的事儿
        1. 创建一条订单数据
        2. 去产品表 将卖出数+1, 库存数-1
        from django.db.models import F
        开启事务处理
        try:
            with transaction.atomic():
                创建一条订单数据
                models.Book.objects.create(num="110110111", product_id=1, count=1)
                能执行成功
                models.Book.objects.filter(id=1).update(kucun=F("kucun") - 1, maichu=F("maichu") + 1)
        except Exception as e:
            print(e)
            transaction.rollback()  # 回滚
            transaction.commit()  # 提交
            
    from django.db import transaction
    # 方式1:装饰器
    @transaction.atomic
    # 方式2:with上下文
    with transaction.atomic():
    transaction.rollback()
    transaction.commit()        

orm查询为惰性查询,要数据才会给出数据对象,不要就不给

res = models.Book.objects.all()
res1 = models.Book.objects.filter(pk=1)

orm查询优化only与defer

only:获取指定字段对应数据,并且封装成字典

    res = models.Book.objects.values('title')
        for d in res:
            print(d,type(d))
            print(d.get('title'))

defer:获取指定字段对应数据,并封装成对象

    res = models.Book.objects.only('title')
        for obj in res:
            print(obj.title)
            print(obj.price)
            print(obj.publish_time)

总结

- 对象点击only括号内填写字段名称不会走数据库查询,但括号内不存在的字段名称则每次都会走数据库

    res = models.Book.objects.defer('title')
    for obj in res:
       print(obj.price)
        """
        与only互为反操作
        """
    res = models.Book.objects.filter(pk=3).first()
    print(res.publish.name)

	only方法会返回一个对象的数据集 对象默认只含有括号内指定字段的数据,如果对象点击括号内没有指定的字段也可以获取到数据只不过每次都需要重新走数据库查询
    defer与only刚好相反

orm查询优化select_related与prefetch_related

    res = models.Book.objects.select_related('publish')
    for obj in res:
        print(obj.publish.name)
        
        
    """
    内部是连表操作 会将外键关联的表与当前表拼接 之后将所有的数据全部封装到数据对象中
    之后对象无论查询哪个表的数据都不再走数据库查询
    select_related括号内只能放外键字段 并且不支持多对多外键字段
    """
    
    
    res = models.Book.objects.prefetch_related('publish')
    for obj in res:
       print(obj.publish.name)
        
    """
    内部是子查询操作 先查询所有的书籍数据 之后查询每本书籍数据对应的出版社数据
    之后将总数据封装到数据对象中
    """

两者括号内都只能放外键字段(一对一和一对多)
select_related内部相当于连表操作 将所有的表拼接到一起之后,再将表中所有的数据全部封装到数据对象中
prefetch_related内部相当于子查询 先查一张表之后将关联的表数据也 查询出来 再封装到数据对象中

orm常用字段

    AutoField()	#专门用于主键
    	'需要指定primary key'
    CharField()  #长整型
    	'需要指定max_lengh参数 varchar()'
    IntegerField() #整数
    DecimalField() #小数
     - 10进制小数
            - 参数:
                max_digits,小数总长度
                decimal_places,小数位长度
    DateField()
    DateTimeField()
    	"""
    	auto_now	  每次更新数据记录的时候会更新该字段
    	auto_now_add  创建数据记录的时候会把当前时间添加到数据库
    	"""
    BooleanField()  # 布尔值类型
    	"""
    	传布尔值 存0或1
    		is_delete = models.BooleanField()
    		is_alive = models.BooleanField()
    		is_buy = models.BooleanField()
    	"""
    TextField()  # 文本类型
    	"""
    	专门用于存储大段文本数据
    		比如文章内容...
    	"""
    EmailField()  # 邮箱类型
    FileField()
            - 字符串,路径保存在数据库,文件上传到指定目录
            - upload_to = ""    上传文件的保存路径
            
    """自定义字段:为了防止django版本与最新的数据库产生差异"""
    class MyCharField(models.Field):
        def __init__(self, max_length, *args, **kwargs):
            self.max_length = max_length
            super().__init__(max_length=max_length, *args, **kwargs)
    
        def db_type(self, connection):
            return 'char(%s)' % self.max_length
        
    # 重要参数
    	null = True
        default = None
        unique  如果设置为unique=True 则该字段在此表中必须是唯一的 。
        db_index  如果db_index=True 则代表着为此字段设置索引。
    # 外键字段
    	to、to_field、on_delete、related_name(给外键字段起别名 用于正反向)
        db_constraint
        """自行总结django1.X与django2.X|3.X外键字段使用的区别"""

django1.X与django2.X外键字段使用的区别

    Django1.0:hbook = models.ForeignKey('BookInfo')  # 1中默认是级联的
    Django2.0:hbook = models.ForeignKey('BookInfo', on_delete=models.CASCADE)  # 2中需要手动设置级联

你可能感兴趣的:(Django,django,python,后端)