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

  • 相关阅读:
    Java 8 Lambda 表达式
    OSGi 系列(十二)之 Http Service
    OSGi 系列(十三)之 Configuration Admin Service
    OSGi 系列(十四)之 Event Admin Service
    OSGi 系列(十六)之 JDBC Service
    OSGi 系列(十)之 Blueprint
    OSGi 系列(七)之服务的监听、跟踪、声明等
    OSGi 系列(六)之服务的使用
    OSGi 系列(三)之 bundle 事件监听
    OSGi 系列(三)之 bundle 详解
  • 原文地址:https://www.cnblogs.com/dengz/p/11466731.html
Copyright © 2011-2022 走看看