zoukankan      html  css  js  c++  java
  • Django:表多对多查询、聚合分组、FQ查询、事务

    1表多对多的关系查询

    • 准备工作创建表结构
    from django.db import models
    
    # Create your models here.
    
    class Publisher(models.Model):
        name = models.CharField(max_length=32, verbose_name="名称")
    
        def __str__(self):
            return self.name
    
    
    class Book(models.Model):
        title = models.CharField(max_length=32)
        price = models.DecimalField(max_digits=6, decimal_places=2)  # 9999.99
        sale = models.IntegerField()
        kucun = models.IntegerField()
        pub = models.ForeignKey(Publisher, null=True,
                                on_delete=models.CASCADE)
    
        def __str__(self):
            return self.title
    
    
    class Author(models.Model):
        name = models.CharField(max_length=32, )
        books = models.ManyToManyField(Book)
    
        def __str__(self):
            return self.name
    
    • 导入数据(表关系)

    • 基于对象查询
    import os
    
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day62exercise.settings")
    import django
    
    django.setup()
    
    from app01 import models
    #基于对象查询
    
    
    #多对多外键在作者
    # 作者 ----> 书  对象
    # 查询作者id为1的作者出版的书
    obj = models.Author.objects.get(pk=1)
    print(obj.books.all())
    # 书 ---->  作者 对象
    #查询书名为"八卦掌"的所有作者
    #当不设置related_name  用法  类_set(类为小写)
    obj = models.Book.objects.get(title="八卦掌")
    print(obj.author_set.all())
    
    #当在关联外键设置,related_name = "authors"
    obj = models.Book.objects.get(title="八卦掌")
    print(obj.authors.all())
    
    
    # 作者--->书 ----> 出版社
    #查询作者id为1,写的所有书的出版社
    obj = models.Author.objects.get(pk=1)
    print(obj.books.all().values("pub__name").distinct())
    
    
    #出版社 ---->书 ------>作者
    #查找在大灰狼出版社出过书的作者
    obj = models.Publisher.objects.get(name="大灰狼出版社")
    print(obj.book_set.all().values("author__name"))
    
    
    
    • 关系管理对象的方法

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

      • 它存在于下面两种情况:

        1.外键关系的反向查询

        2.多对多关联关系

      • 简单来说就是当 点后面的对象 可能存在多个的时候就可以使用以下的方法。

    • create()

      创建一个新的对象,保存对象,并将它添加到关联对象集之中,返回新创建的对象。

    • add()

      把指定的model对象添加到关联对象集中。

      添加对象

    • remove()

      从关联对象集中移除执行的model对象

    • clear()

      从关联对象集中移除一切对象。

    #all() #获取所关联所有对象
    obj = models.Author.objects.get(pk=2)
    print(obj.books.all())
    
    
    #set() 设置多对多关系  参数可以传入[id,id],[对象,对象]
    #把 id为2的作者关联的书的id设置为3,4
    # 法1:传入id
    obj = models.Author.objects.get(pk=2)
    obj.books.set([3,4])
    # print(obj.books.all())
    # 法2:传入对象
    obj.books.set(models.Book.objects.filter(pk__in =[1,2,3]))
    print(obj.books.all())
    
    
    #add() 添加多对多关系 参数可以传入(d,id),(对象,对象)
    #id为2的作者关联的书的id添加4,5
    # 法1:传入id
    obj.books.add(4,5)
    # 法2:传入对象
    obj.books.add(*models.Book.objects.filter(pk__in=[4,5]))#   *   号作用是打散
    
    
    
    #remove 删除多对多的关系   参数可以传入(d,id),(对象,对象)
    #id为2的作者关联的书的id 4,5 给移除
    # 法1:传入id
    obj = models.Author.objects.get(pk=2)
    obj.books.remove(4,5)
    # 法2:传入对象
    obj.books.remove(*models.Book.objects.filter(pk__in=[4,5]))  #     *   号作用是打散
    
    
    
    # create()
    obj = models.Author.objects.get(pk=2)
    ret = obj.books.create(title="打狗棒法",pub_id=2,price=56,sale=23,kucun=44)
    # print(ret)
    
    #获取id为1的book对象,创建author
    res = models.Book.objects.get(pk=1)
    print(res)
    rep = res.authors.create(name="关羽")
    
    #clear() #清除多对多关系
    #清楚作者id为5的与书的关系
    res = models.Author.objects.get(pk=5)
    res.books.clear()
    
    
    
    • clear() 和remove() 方法避免的坑
    class Book(models.Model):
        title = models.CharField(max_length=32)
        price = models.DecimalField(max_digits=6, decimal_places=2)  # 9999.99
        sale = models.IntegerField()
        kucun = models.IntegerField()
        pub = models.ForeignKey(Publisher, 
                                on_delete=models.CASCADE)
                                
    #ForeignKey字段没有在pub字段设置null=True.
    #当删除一个Publisher字段(此字段有数据关联Book表),执行:                        
    res = models.Publisher.objects.get(pk=5).book_set.clear()
    print(res)
    
    #此时会报错没有clear()和remove()方法:
    #>>> models.Publisher.objects.first().book_set.clear()
    #Traceback (most recent call last):
    # File "<input>", line 1, in <module>
    #AttributeError: 'RelatedManager' object has no attribute 'clear'
    
    #显示没有此方法
    
    #当ForeignKey字段设置null=True时,执行
    res = models.Publisher.objects.get(pk=5).book_set.clear()
    print(res)
    可以删除!
    
    • Publisher记录删除,关联Book表pub_id 显示为null

    注意:

    • 对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。

    2.2聚合和分组

    • 还是引用上表进行操作!

    1.聚合

    • aggregate()QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。用到的内置函数:

      from django.db.models import Avg, Sum, Max, Min, Count
      
      import os
      
      os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day62exercise.settings")
      import django
      
      django.setup()
      
      from app01 import models
      from django.db.models import Avg, Sum, Max, Min, Count
      #统计Book表 id大于3的价格最高的字典
      ret = models.Book.objects.filter(pk__gt=3).aggregate(Max("price"))
      print(ret)
      #{'avg': 179.211111}
      #统计Book表, 书的平均价格
      ret = models.Book.objects.all().aggregate(avg=Avg("price"))
      print(ret)
      #{'avg': 179.211111}
      #统计Book表,平均卖书的数量,和最高卖书数量
      ret = models.Book.objects.all().aggregate(avg=Avg("sale"),max=Max("sale"))
      print(ret)
      #{'avg': 111.0, 'max': 550}
      

    2.分组

    #统计每个出版社出的书的平均价格
    res = models.Book.objects.values("pub__name").annotate(avg=Avg("price")).values("avg","pub__name")
    print(res)
    
    # 统计每一本书的作者个数
    ret = models.Book.objects.annotate(作者数 = Count("author")).values("title","作者数")
    print(ret)
    
    
    #统计每个出版社出的书的平均价格
    ret = models.Book.objects.values("pub__name").annotate(avg=Avg("price")).values("avg","pub__name")
    print(ret)
    #统计每个出版社最便宜的书的价格
    ret = models.Book.objects.values("pub__name").annotate(min=Min("price")).values("min","pub__name")
    print(ret)
    # 法1
    ret = models.Book.objects.values("pub_id").annotate(min=Min("price"))
    print(ret)
    # 法2
    ret = models.Publisher.objects.values("name").annotate(min=Min("book__price"))
    print(ret)
    
    # 统计不止一个作者的图书
    ret = models.Book.objects.annotate(count = Count("author")).filter(count__gt=1)
    print(ret)
    
    #同级各个作者出的书的总价
    ret = models.Author.objects.values("name").annotate(sum = Sum("books__price")).values()
    print(ret)
    
    

    3.F查询和Q查询

    • 我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?
    • Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
    F查询:
    import os
    
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day62exercise.settings")
    import django
    
    django.setup()
    
    from app01 import models
    
    #F查询
    # ret = models.Book.objects.filter(price__gt=10000)
    # print(ret)
    
    
    from django.db.models import F
    # 比较书的销量大于库存的书
    ret = models.Book.objects.filter(sale__gt=F("kucun"))
    print(ret)
    
    #更新字段
    
    # 书的价格更新为100 ,只更新1个
    ret = models.Book.objects.get(pk=1)
    ret.sale = 100
    ret.save()
    # 更新所有sale字段为100
    models.Book.objects.all().update(sale=100)
    
    #取某个字段的值进行操作
    models.Book.objects.all().update(sale=F("sale")*2+10)
    
    
    • 进阶:

      把所有书名后面加上(第一版)
      from django.db.models.functions import Concat
      from django.db.models import Value
      
      models.Book.objects.all().update(title=Concat(F('title'),Value("("),Value("第一版"),Value(")")))
      
      
    Q查询:
    • filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR语句),你可以使用Q对象
    • 你可以使用&与,|或,~非
    from django.db.models import Q
    #查询pk大于3的所有Book记录
    ret = models.Book.objects.filter(Q(pk__gt=3))
    print(ret)
    
    #查询pk<2 或 pk>5 的所有Book记录
    ret = models.Book.objects.filter(Q(pk__gt=5)|Q(pk__lt=2))
    print(ret)
    
    #查询2<=pk<=5并且price大于50的所有记录
    ret = models.Book.objects.filter(~Q(Q(pk__gt=5)|Q(pk__lt=2))&Q(price__gt=50))
    print(ret)
    

    4.事务

    • 数据库具有四大特性:原子性、一致性、隔离性、持久性

      • 原子性:事务包含所有操作要么全部成功,要么全部失败回滚
      • 一致性:事务的执行之前和执行字后都必须处于一致性状态,每一次数据变更就会导致数据库的状态迁移
      • 隔离性:多个用户并发访问数据库,比如访问同一张表,多个并发事务之间,应当相互隔离。
      • 持久性:一旦提交,对于数据库数据改变是永久性,即使数据库发生故障也不能丢失已提交事务所完成的改变。
    • 模块导入

      from django.db import transaction
      
    try:
        with transaction.atomic():
            models.Publisher.objects.create(name="xxxxxx")
            # int("stasd")
            models.Publisher.objects.create(name="xx2")
    except Exception as e:
        print(e)
    

    5.原生SQL执行

    import os
    
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day62exercise.settings")
    import django
    
    django.setup()
    
    
    from django.db import connection,connections
    cursor = connection.cursor()
    
    cursor.execute("""select * from app01_publisher where id=%s""",(3,))
    # all = cursor.fetchall()
    # print(all)
    row = cursor.fetchone()
    print(row)
    

    6.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语句。

    7.在Python脚本中调用Django环境

    • 和django项目同级目录下
    import os
    
    if __name__ == "__main__":
        os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day62exercise.settings")
        import django
        django.setup()
        from app01 import models
        book =models.Book.objects.all()
        print(book)
    

    8.QuerySet方法大全

    ##################################################################
    # PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #
    ##################################################################
    
    def all(self)
        # 获取所有的数据对象
    
    def filter(self, *args, **kwargs)
        # 条件查询
        # 条件可以是:参数,字典,Q
    
    def exclude(self, *args, **kwargs)
        # 条件查询
        # 条件可以是:参数,字典,Q
    
    def select_related(self, *fields)
        性能相关:表之间进行join连表操作,一次性获取关联的数据。
    
        总结:
        1. select_related主要针一对一和多对一关系进行优化。
        2. select_related使用SQL的JOIN语句进行优化,通过减少SQL查询的次数来进行优化、提高性能。
    
    def prefetch_related(self, *lookups)
        性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。
    
        总结:
        1. 对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化。
        2. prefetch_related()的优化方式是分别查询每个表,然后用Python处理他们之间的关系。
    
    def annotate(self, *args, **kwargs)
        # 用于实现聚合group by查询
    
        from django.db.models import Count, Avg, Max, Min, Sum
    
        v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))
        # SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id
    
        v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)
        # SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1
    
        v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1)
        # SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1
    
    def distinct(self, *field_names)
        # 用于distinct去重
        models.UserInfo.objects.values('nid').distinct()
        # select distinct nid from userinfo
    
        注:只有在PostgreSQL中才能使用distinct进行去重
    
    def order_by(self, *field_names)
        # 用于排序
        models.UserInfo.objects.all().order_by('-id','age')
    
    def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
        # 构造额外的查询条件或者映射,如:子查询
    
        Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
        Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
        Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
        Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])
    
     def reverse(self):
        # 倒序
        models.UserInfo.objects.all().order_by('-nid').reverse()
        # 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序
    
    
     def defer(self, *fields):
        models.UserInfo.objects.defer('username','id')
        或
        models.UserInfo.objects.filter(...).defer('username','id')
        #映射中排除某列数据
    
     def only(self, *fields):
        #仅取某个表中的数据
         models.UserInfo.objects.only('username','id')
         或
         models.UserInfo.objects.filter(...).only('username','id')
    
     def using(self, alias):
         指定使用的数据库,参数为别名(setting中的设置)
    
    
    ##################################################
    # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
    ##################################################
    
    def raw(self, raw_query, params=None, translations=None, using=None):
        # 执行原生SQL
        models.UserInfo.objects.raw('select * from userinfo')
    
        # 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
        models.UserInfo.objects.raw('select id as nid from 其他表')
    
        # 为原生SQL设置参数
        models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])
    
        # 将获取的到列名转换为指定列名
        name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
        Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)
    
        # 指定数据库
        models.UserInfo.objects.raw('select * from userinfo', using="default")
    
        ################### 原生SQL ###################
        from django.db import connection, connections
        cursor = connection.cursor()  # cursor = connections['default'].cursor()
        cursor.execute("""SELECT * from auth_user where id = %s""", [1])
        row = cursor.fetchone() # fetchall()/fetchmany(..)
    
    
    def values(self, *fields):
        # 获取每行数据为字典格式
    
    def values_list(self, *fields, **kwargs):
        # 获取每行数据为元祖
    
    def dates(self, field_name, kind, order='ASC'):
        # 根据时间进行某一部分进行去重查找并截取指定内容
        # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
        # order只能是:"ASC"  "DESC"
        # 并获取转换后的时间
            - year : 年-01-01
            - month: 年-月-01
            - day  : 年-月-日
    
        models.DatePlus.objects.dates('ctime','day','DESC')
    
    def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
        # 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间
        # kind只能是 "year", "month", "day", "hour", "minute", "second"
        # order只能是:"ASC"  "DESC"
        # tzinfo时区对象
        models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
        models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))
    
        """
        pip3 install pytz
        import pytz
        pytz.all_timezones
        pytz.timezone(‘Asia/Shanghai’)
        """
    
    def none(self):
        # 空QuerySet对象
    
    
    ####################################
    # METHODS THAT DO DATABASE QUERIES #
    ####################################
    
    def aggregate(self, *args, **kwargs):
       # 聚合函数,获取字典类型聚合结果
       from django.db.models import Count, Avg, Max, Min, Sum
       result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid'))
       ===> {'k': 3, 'n': 4}
    
    def count(self):
       # 获取个数
    
    def get(self, *args, **kwargs):
       # 获取单个对象
    
    def create(self, **kwargs):
       # 创建对象
    
    def bulk_create(self, objs, batch_size=None):
        # 批量插入
        # batch_size表示一次插入的个数
        objs = [
            models.DDD(name='r11'),
            models.DDD(name='r22')
        ]
        models.DDD.objects.bulk_create(objs, 10)
    
    def get_or_create(self, defaults=None, **kwargs):
        # 如果存在,则获取,否则,创建
        # defaults 指定创建时,其他字段的值
        obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})
    
    def update_or_create(self, defaults=None, **kwargs):
        # 如果存在,则更新,否则,创建
        # defaults 指定创建时或更新时的其他字段
        obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1})
    
    def first(self):
       # 获取第一个
    
    def last(self):
       # 获取最后一个
    
    def in_bulk(self, id_list=None):
       # 根据主键ID进行查找
       id_list = [11,21,31]
       models.DDD.objects.in_bulk(id_list)
    
    def delete(self):
       # 删除
    
    def update(self, **kwargs):
        # 更新
    
    def exists(self):
       # 是否有结果
    
    
    QuerySet方法大全
    
  • 相关阅读:
    php多态
    ssl certificate problem: self signed certificate in certificate chain
    test plugin
    open specific port on ubuntu
    junit vs testng
    jersey rest service
    toast master
    use curl to test java webservice
    update folder access
    elk
  • 原文地址:https://www.cnblogs.com/xujunkai/p/11847553.html
Copyright © 2011-2022 走看看