zoukankan      html  css  js  c++  java
  • Django---Django的ORM的一对多操作(外键操作),ORM的多对多操作(关系管理对象),ORM的分组聚合,ORM的F字段查询和Q字段条件查询,Django的事务操作,额外(Django的终端打印SQL语句,脚本调试)

    Django---Django的ORM的一对多操作(外键操作),ORM的多对多操作(关系管理对象),ORM的分组聚合,ORM的F字段查询和Q字段条件查询,Django的事务操作,额外(Django的终端打印SQL语句,脚本调试)

    一丶Django的ORM外键操作

    通过对象查找

    ### 正向查找
    	#  获得图书对象
    	book_obj=models.Book.objects.get(pk=1)
        
    	ret=book_obj.pub  #pub是Book表的外键字段,存在Book表中. 通过pub可以拿到所关联的对象
    	print(ret,type(ret))   # 1---沙河出版社   <class 'app02.models.Publisher'>
        
    	ret2=book_obj.pub_id # pub_id是在Book表生成的字段. 得到的是一个具体字段值.仅此而已
    	print(ret2,type(ret2)) # 1 <class 'int'>
        
      #PS: 个人理解,通过多的一方的外键,主动去查找一的一方. 为正向查找,  
    	
        
        
    ### 反向查找
    	# 通过一的一方(没有设置外键的表),去查询被外键关联的多的一方的表.
    	# 反查 默认是:类名的小写_set,可以设置外键字段的related_name属性,起别名,	也可以设置related_query_name属性设置别名.  
        # related_query_name 只针对于字段查询约束, 对于对象查询不生效
        
        
    	# 获取 publisher对象
    	pub_obj=models.Publisher.objects.get(pk=1)
    		
    	## 不指定 related_name ,在外键字段上不设置:related_name的'别名' , 反查默认是:类名的小写_set
    		# 得到一个关系管理对象
    		ret=pub_obj.book_set  
    		print(ret,type(ret))#app02.Book.None,<class 'django.db.models...RelatedManager'>
    		  
    		# 通过 关系管理对象, 反向查询出所有关联的对象
    		ret2=pub_obj.book_set.all() # 得到一个对象列表 QuerySet
    		print(ret2,type(ret2)) # QuerySet  ,  class 'django.db.models.query.QuerySet'>
    
            
    	## 指定 related_name='books' 
            # 得到一个关系管理对象
            ret=pub_obj.books  # books 是设置related_name属性后的值
            print(ret,type(ret))#app02.Book.None,<class 'django.db.models...RelatedManager'>	 
    
            # 通过 关系管理对象, 反向查询出所有关联的对象
            ret2 = pub_obj.books.all()  # 得到一个对象列表 QuerySet
            print(ret2, type(ret2))  # QuerySet  ,  class 'django.db.models.query.QuerySet'>
    

    通过字段查找

    ### 基于字段的查询,跨表查询使用 双下划线 __字段
    
    	## 有外键字段,通过Book表的查询外键关联的对象的字段
        	
        	ret=models.Book.objects.filter(pub__name='沙河出版社')  # QuerySet
    		
        
        ## 没有外键字段 .通过 Author表对象反查 Book图书表的某个字段.
        	# 默认是: 类名小写__被查字段,
    		ret=models.Publisher.objects.filter(book__title='新Linux') # QuerySet
    		
    		# 外键设置: related_name='books'时,使用 books__被查字段
    		ret=models.Publisher.objects.filter(books__title='新Linux') # QuerySet
    		
             # 外键设置: related_query_name='booo'时,使用 booo__被查字段
             ret=models.Publisher.objects.filter(books__title='新Linux') # QuerySet
    
    # PS:  设置了 : related_query_name='booo', related_query_name的优先级高于related_name
    

    通过关系管理对象

    ### 外键  关系管理对象
        publisher_obj=models.Publisher.objects.get(pk=1)
        print(publisher_obj.books,type(publisher_obj.books)) # app02.Book.None <class 'django.db.models...RelatedManager'>
    
        
        
    ### all()  # 通过关系管理对象获得所有关联对象
        print(publisher_obj.books.all())
    
        
        
    ### set() 设置一对多关系  # 只能是 对象,或者对象列表 .  不能是ID
        publisher_obj.books.set(models.Book.objects.filter(pk__in=[2,3]))  # 可以是对象列表
        publisher_obj.books.set(models.Book.objects.filter(pk=2)) # 可以是一个对象
    
    
        
    ### add()  # 需要添加对象,  ID不行
        publisher_obj.books.add(*models.Book.objects.filter(pk__in=[2,3])) # 添加对象列表, 需要打散
        publisher_obj.books.add(models.Book.objects.filter(pk=3).first()) # 添加一个对象
    
        
    
    ### remove() 和 clear()   # 由于表中的外键字段不允许为空, 所以 直接删除会报错.  需要更改数据库的外键字段可以为空  
        publisher_obj.books.remove(*models.Book.objects.filter(pk__in=[2,3]))
        publisher_obj.books.clear()
        ##PS :remove() 和 clear()   使用两个方法时,需要设置外键字段的允许为空 ,null=True时存在。
    
     
    
    ### create()  创建一个关联对象, 默认是当前的pub_id 也可以设置
        publisher_obj.books.create(title='XXXX',price='23',num=321,sale=213,pub_id=3)
    
    

    二丶Django的ORM多对多

    "关联管理器"是在一对多或者多对多的关联上下文中使用的管理器。

       它存在于下面两种情况:

             1.外键关系的反向查询

             2.多对多关联关系

    # 从一的一方去拿多的一方的数据,总是先得到一个关系管理对象. 在通过关系管理对象去获得多的一方的数据
    

    多对多查询

    # 基于对象查询
        author_obj=models.Author.objects.get(pk=1)
        # 正向
        print(author_obj.name)
        print(author_obj.books)  # 关系管理对象,
        print(author_obj.books.all()) #通过关系管理对象获得所关联的信息
    
        # 反向
        book_obj=models.Book.objects.get(pk=1)
        print(book_obj.title)
        print(book_obj.authors) # authors 起的别名. 多对多字段设置 # related_name='authors'
        print(book_obj.authors.all())
    
    
    #### 基于字段查询
        # 都是通过关系管理对象进行操作
        
        # 反查  通过起的外键字段的别名  # 通过别名 __(双下划线)字段,即:authors__name='XXX'
        ret=models.Book.objects.filter(authors__name='alex')
        
        # 正向查 通过外键字段    # 通过多对多字段 books ___双下划线(字段),即:books__title='XX'
        ret=models.Author.objects.filter(books__title='金Python') 
        
    

    方法

    ## create()  创建一个所关联的对象,保存对象,建立多对多关系, 并将其与关联的对象添加到第三张表中
        
        # 创建图书数据,并将其与作者关联,添加多对多关系.
        author_obj.books.create(title='新新心心念念',price='32',sale=70,num=30,pub_id=1)
        
        # 创建一个作者数据,并将其与图书关联,添加多对多关系
        book_obj.authors.create(name='金山皮',)
    
    
    
    
    ## add() 把指定的model对象添加到关联对象 ,在原有的基础上增加 , 添加一条. 可以是 : ID , 对象 
    	author_obj.books.add(4,5)  # 添加多个对象的ID可以直接 逗号,分隔.
        author_obj.books.add(*[4,])  # 添加多个对象的ID ,多个添加不能是列表,使用列表必须被 * 打散
        author_obj.books.add(models.Book.objects.get(pk=3),models.Book.objects.get(pk=1))  # 可以添加一个对象 逗号,分隔.
        author_obj.books.add(*models.Book.objects.all())  # QuerySet也是需要被打散
    
    
        
        
    
    ## set() 更新model对象关联的对象,在第三个表清除当前对象的所关联对象,重新设置设置多对多关系.必须是列表
    	#       参数可以是一个列表(关联对象的id) # ID
    		author_obj.books.set([1,4])
            
    	#      参数可以是对象列表, ,(最终转换成关联对象的id)
    		author_obj.books.set(models.Book.objects.filter(pk__in=[2,3]))# 对象列表
    
        ## PS: 多对多set使用set方法时,必须是一个对象列表,
    
        # 错误设置多对多关系的写法: 'Book' object is not iterable
        author_obj.books.set(models.Book.objects.filter(pk=1).first())# 对象列表
            
            
    ## remove() 移除与关联对象信息  , 可以是 : ID , 对象
    	author_obj.books.remove(4,5) # 移除多个ID, 逗号分隔
    	author_obj.books.remove(models.Book.objects.get(pk=5),models.Book.objects.get(pk=4))  # 可以添加一个对象 逗号,分隔.
    	author_obj.books.remove(*models.Book.objects.filter(pk__in=[1,2,3]))  # 对象列表,需要打散
        
        
        
    ## clear() 清除与关联对象一切信息 ,清空多对多关系
    	author_obj.books.clear()  # 与当前这个作者的多对多关系全部删除
    
    

    三丶聚合

          'aggregate()'是'QuerySet' 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。

    # 导入内置函数
    from django.db.models import Avg, Sum, Max, Min, Count
    
    # Count() 按照那个字段进行统计,得到是一个字典对象,必须给参数.
    # title_count 是起别名
    ret=models.Book.objects.aggregate(title_count=Count('title'))
    print(ret['title_count']) # 字典取值
    
    # Max() 求这个字段的最大值
    ret=models.Book.objects.aggregate(book_max=Max('price'))
    print(ret)
    
    # Min() 求这个字段的最小值
    ret=models.Book.objects.aggregate(book_min=Min('price'))
    print(ret)
    
    # Sum() 求这个字段的最小值
    ret=models.Book.objects.aggregate(book_sum=Sum('price'))
    print(ret)
    
    # Avg() 求这个字段的最小值
    ret=models.Book.objects.aggregate(book_avg=Avg('price'))
    print(ret)
    
    ## 联合使用多个聚合
    ret=models.Book.objects.aggregate(book_avg=Avg('price'),book_max=Max('price'))
    print(ret)
    

    四丶分组

    与SQL的分组同理,对某一字段进行分组.

    # SQL :  
    	# 按照部门Id分组
    	select dept_id,avg(salary) from employee group by dept_id
    	
    # Django-ORM:  # 按照 values() 中的字段进行分组,联合字段进行分组. 
    	from django.db.models import Avg
    	models.Dept.objects.annotate(avg=Avg("employee__salary")).values("name", "avg")
    

    orm分组

    #### annotate()  统计每一本书的作者个数
     # 默认分组
    ret = models.Book.objects.annotate(author_count=Count('authors')) # annotate 注释
    for el in ret:
        print(el.__dict__,type(el)) # el是对象
        print(el.author_count)  ##  author_count 被封装到对象中,成为对象的一个字段
    
    #   values() 是查询出所有字段,以键值对的形式
    ret = models.Book.objects.annotate(author_count=Count('authors')).values()  # annotate 注释
    for i in ret:
        print(i,type(i)) # values 是字典形式
        print(i['author_count'])
        
    
      
    
    
    ### 统计出每个出版社买的最便宜的书的价格
    # 通过 作者表,正向查
    ret=models.Publisher.objects.annotate(min=Min('booo__price'))
    for el in ret:
        print(el,el.min)
    # 通过 图书表 反向查, 按照出版社分组,取价格最小值
    ret=models.Book.objects.values('pub__name').annotate(min=Min('price'))
    print(ret)
    
        
        
        
        
        
    ####  统计不止一个作者的图书
    
    	# 合适的方法
    ret=models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1)
    print(ret)  # 左连接  
    
    	# 不合适的方法
    ret=models.Author.objects.values('books__title').annotate(author_num=Count('id')).filter(author_num__gt=1) # 右连接
    print(ret)
    
    
    ####  根据一本图书作者数量的多少对查询集 QuerySet进行排序
    	# .reverse() 反转
        # order_by 根据那个字段进行排序, 反向查询可以通过 authors 进行查询
    ret=models.Book.objects.annotate(author_num=Count('authors')).order_by('author_num').reverse()
    for el in ret:
        print(el.author_num)
        
        
    #### 查询各个作者出的书的总价格
    ret=models.Author.objects.annotate(sum_price=Sum('books__price'))
    for el in ret:
        print(el,el.sum_price)
    
    ret=models.Author.objects.annotate(sum_price=Sum('books__price')).values('name','sum_price')
    print(ret)
    

    五丶F和Q查询

    F查询

    ### 用于 字段之间的比较
        # 查询 销售量大于库存量的图书
        ret=models.Book.objects.filter(sale__gte=F('num'))
        for el in ret.values():
            print(el)
    
    ### Django 支持F()对象之间 算数
    	# 更改库的某个字段的值,在原有值的基础上,进行数值操作,并保存
        ret=models.Book.objects.filter(pk=1).update(sale=F('sale')*2+100)
        print(ret,type(ret))
     
    
    ### 额外:
    	# 修改char类型的字段
        from django.db.models.functions import Concat
        from django.db.models import Value
        # 可以对querySet操作update方法
    ret=models.Book.objects.filter(pk=1).update(title=Concat(F('title'),Value("("),Value("Chinese版"),Value(")")))
    
    ret=models.Book.objects.filter(authors__name='二狗').update(title=Concat(F('title'),Value("("),Value("Chinese版"),Value(")")))
    print(ret)
    

    Q查询

    ### filter() 等方法中的关键字参数查询都是一起进行“AND” 的。
    	# 组合& 和|操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询。
        
        #三种操作Q对象, 与& 或| 非~
    ret=models.Book.objects.filter(Q(pk__gt=3)&Q(title__contains='a'))  # & 与操作
    
    ret=models.Book.objects.filter(Q(pk__gt=5)|Q(title__contains='a'))  # | 或操作
    
    ret=models.Book.objects.filter(Q(pk__gt=5)|~Q(title__contains='a')) # ~ 或操作
    
    
    ### 查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。
    
    ret=models.Book.objects.filter(Q(price__range=[10,30])|Q(num__lte=50),title__icontains='a')
    
    

    六丶Django的事务

    from django.db import transaction
    from django.db.models import F
    import time
    
    ### 开启原子事务
    # 事务具有原子性, 隔离行, 一致性 ,持久性ACID
    # 一般用于银行转账
    try:
        with transaction.atomic(): # 开启一个事务
            models.Book.objects.filter(pk=1).update(sale=F('sale') + 50)
            
            time.sleep(3)
            exit(0)  # 退出操作, 事务的回滚到原始状态.
            models.Book.objects.filter(pk=2).update(sale=F('sale') - 50)
            
    except Exception as e:
        print(e)
    
        
        
    ##PS: 异常处理一定要在开启事务外,在事务内开启异常处理会出现数据丢失现象
    

    七丶预备数据和其他操作

    提前预备model.py

    from django.db import models
    
    
    # Create your models here.
    ## 自定义char类型
    class MyCharField(models.Field):
        '''
            自定义 char类型字段
        '''
    
        def __init__(self, max_length, *args, **kwargs):
            self.max_length = max_length
            super(MyCharField, self).__init__(max_length=max_length, *args, **kwargs)
    
        def db_type(self, connection):
            '''
              限定生成数据库表的字段类型为char,长度为max_length指定的值
            :param connection:
            :return:
            '''
            return f'char({self.max_length})'
    
    
    class User(models.Model):
        pid = models.AutoField(primary_key=True)  # 主键,自增
        name = models.CharField(max_length=32, db_column='username', unique=True, verbose_name='姓名:', help_text='请输入姓名')
        age = models.IntegerField(null=True, blank=True)  # blank表单校验约束为空
        birth = models.DateTimeField(auto_now=True)  # auto_now 修改就更新表, auto_now_add 只记录第一创建的时间
        phone = MyCharField(max_length=11, null=True, blank=True)
        gender = models.BooleanField(default=True, choices=((True, '男'), (False, '女')))
    
        def __str__(self):
            return f'{self.name}<---->{self.birth}'
    
    
        class Meta:
            # 在数据库中生成的表,默认app名称+下划线+类名
            db_table='person'
    
            # 在admin中显示的表名称
            verbose_name='个人信息'
    
            # verbose_name+s
            verbose_name_plural='所有的用户信息'
    
    
            # 联合索引
            index_together=[
                ('name','age'),   # 两个字段都存在索引,遵循最左前缀原则.
                                  # 联合索引指的是表上的多个列合并起来做一个索引
            ]
    
            # 联合唯一索引
            unique_together = (("name", "age"),)  # 应为两个存在的字段
    
    
    
    
    class Publisher(models.Model):
        name=models.CharField(max_length=32)
    
        def __str__(self):
            return f'{self.pk}---{self.name}'
    
    
    class Book(models.Model):
        title=models.CharField(max_length=32)
        price=models.DecimalField(max_digits=5,decimal_places=2)
        sale=models.IntegerField()
        num=models.IntegerField()
        hot_heat=MyCharField(max_length=32)
    
    
        pub=models.ForeignKey('Publisher',on_delete=models.CASCADE,related_name = 'books',related_query_name = 'booo')
    
        def __str__(self):
            return f'{self.pk}---{self.title}'
    
    
    class Author(models.Model):
        name=models.CharField(max_length=32)
        books=models.ManyToManyField('Book',related_name='authors')
    
        def __str__(self):
            return f'{self.name}'
    

    如何Django终端打印SQL语句

    ​ 在Django项目的settings.py文件中,配置如下:

    LOGGING = {
        'version': 1,
        'disable_existing_loggers': False,
        'handlers': {
            'console':{
                'level':'DEBUG',
                'class':'logging.StreamHandler',
            },
        },
        'loggers': {
            'django.db.backends': {
                'handlers': ['console'],
                'propagate': True,
                'level':'DEBUG',
            },
        }
    }
    
    
    #即为你的Django项目配置上一个名为django.db.backends的logger实例即可查看翻译后的SQL语句。 
    

    在Python脚本中调用Django环境

    import os
    
    if __name__ == '__main__':
        os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings") # 导入Django的配置文件
        import django	# 导入Django模块
        django.setup()	# 配置Django
    
        from app01 import models # 导入models文件
    
        books = models.Book.objects.all() # 调试脚本代码
        print(books)					# 结果
    

    https://www.cnblogs.com/maple-shaw/articles/9323320.html
    https://www.cnblogs.com/maple-shaw/articles/9403501.html

  • 相关阅读:
    leetcode-62-Unique Paths
    [leetcode-64-Minimum Path Sum]
    [leetcode-198-House Robber]
    [leetcode-120-Triangle]
    [leetcode-53-Maximum Subarray]
    [leetcode-303-Range Sum Query
    [leetcode-123-Best Time to Buy and Sell Stock III]
    绑定下拉框
    解决URL参数中文乱码
    html 图片拖动不出来的脚本
  • 原文地址:https://www.cnblogs.com/dengz/p/11466731.html
Copyright © 2011-2022 走看看