day054学习笔记

今日内容

基于双下划线的跨表查询(left-join?)

  • filter()
  • values()
  • 若存在外键属性则通过外键__寻找,否则通过小写类名__
   # 一对一
    # 正向连表  靠属性
    # 查询旭东的家庭住址
    # ret = models.Author.objects.filter(name='旭东').values('ad__addr')
    # print(ret)
    # select app01_authordetail.addr from app01_author inner join app01_authordetail on app01_author.ad_id = app01_authordetail.id where app01_author.name='旭东';
	# 反向连表靠 类名小写
    # ret = models.AuthorDetail.objects.filter(author__name='旭东').values('addr')
    # print(ret)

    # 一对多
    # 查询东京出版社出版了哪些书
    # select app01_book.title from app01_publish inner join app01_book on app01_publish.id = app01_book.publishs_id where app01_publish.name='东京出版社';

    # ret = models.Publish.objects.filter(name='东京出版社').values('book__title')
    # print(ret)
    # ret = models.Book.objects.filter(publishs__name='东京出版社').values('title')
    # print(ret)


    # 多对多
    # 查询一下金龙2写了哪些书

    ret = models.Book.objects.filter(authors__name='金龙2').values('title')
    print(ret)

    ret = models.Author.objects.filter(name='金龙2').values('book__title')
    print(ret)

聚合查询

  • 聚合函数--aggregate( *args, **kwargs )
    • Avg
    • Max
    • Min
    • Count
    • Sum
    
    aggregate聚合查询,结果是普通字典,queryset的结束符
    from django.db.models import Avg,Max,Min,Count,Sum
    
    obj = models.Book.objects.all().aggregate(a=Max('price')) #{'price__avg': 200.0}
    print(obj)
	Book.objects.aggregate(Avg('price'), Max('price'), Min('price')) 

分组查询(每个出版社出版的书的最高价格)

  • 方式1:按照values中字段联合分组

    ret = models.Book.objects.values('publishs_id').annotate(m=Max('price'))
    

    总结:values写在annotate前面,意思是以values括号内的字段作为分组的依据,annotate里面是你要做的统计结果,这样,返回结果为queryset类型数据,里面是字典

  • 方式2:默认按照对象主键分组

    ret = models.Publish.objects.annotate(m=Max('book__price')).values('m','name')
    

    总结: annotate直接写在了objects后面,意思是按照前面表的所有的数据(默认是id值)作为分组依据,结果返回的是前面这个表的所有models对象(model对象中包含了每个对象自己的统计结果),在通过values来取值,取值时可以直接写字段和统计结果的别名,也是queryset类型,里面是字典

  • 其它实例

        查询每个作者的姓名以及出版的书的最高价格
    
    
        ret = models.Book.objects.values('authors__name','authors__id').annotate(m=Max('price'))  # group by authors__name,authors__id
        print(ret)
    
        ret = models.Author.objects.annotate(m=Max('book__price')).values('name','m')
        print(ret)
    

F查询

  • F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值

    # 查询一下点赞数大于评论数的所有书籍
    
    ret = models.Book.objects.filter(dianzan__gt=F('comment')).values('title')
    
  • Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。

    # 查询评论数大于收藏数2倍的书籍
    Book.objects.filter(commentNum__lt=F('keepNum')*2)
    
    #本表字段进行四则运算
    models.Book.objects.all().update(
        price=F('price')+20  #支持四则运算
    )
    

Q查询

  • filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR 语句),你可以使用Q 对象

    from django.db.models import Q
    Q(title__startswith='Py')
    
    • Q 对象可以使用&(与)|(或)、~(非) 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。

      bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon"))
      
      
    • 可以组合&| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询

      bookList=Book.objects.filter(Q(authors__name="yuan") & ~Q(publishDate__year=2017)).values_list("title")
      bookList=Book.objects.filter(Q(Q(authors__name="yuan") & ~Q(publishDate__year=2017))&Q(id__gt=6)).values_list("title") #可以进行Q嵌套,多层Q嵌套等,其实工作中比较常用
      
      
    • 查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。

      bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),
                                    title__icontains="python"  #也是and的关系,但是Q必须写在前面
                                   )
      
      

orm执行原生sql语句(了解)

  • raw()管理器( 用于原始的SQL查询,并返回模型的实例 )

    • 注意:raw()语法查询必须包含主键

    • 返回一个django.db.models.query.RawQuerySet 实例。 这个RawQuerySet 实例可以像一般的QuerySet那样,通过迭代来提供对象实例

      ret = models.Book.objects.raw('select * from app01_book;')
      for i in ret:
          print(i.title)
      print(ret)
      
      
    • raw()查询可以查询其他表的数据

    • raw()方法自动将查询字段映射到模型字段。还可以通过translations参数指定一个把查询的字段名和ORM对象实例的字段名互相对应的字典

      d = {'tname': 'haha'}
          ret = models.Student.objects.raw('select * from app02_teacher', translations=d)
          for i in ret:
              print(i.id, i.sname, i.haha)
      
      
    • 原生SQL还可以使用参数,注意不要自己使用字符串格式化拼接SQL语句,防止SQL注入!

      d = {'tname': 'haha'}
          ret = models.Student.objects.raw('select * from app02_teacher where id > %s', translations=d, params=[1,])
          for i in ret:
              print(i.id, i.sname, i.haha)
      
      
  • 直接执行自定义SQL

    直接从django提供的接口中获取数据库连接,然后像使用pymysql模块一样操作数据库

    from django.db import connection, connections
    cursor = connection.cursor()  # cursor = connections['default'].cursor()
    cursor.execute("""SELECT * from auth_user where id = %s""", [1])
    ret = cursor.fetchone()
    
    
  • 直接使用pymysql

    conn = pymysql.connect(
         host='127.0.0.1',
         port=3306,
         user='root',
         password='123',
         database='orm02',
         charset='utf8'
    )
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    cursor.execute('select * from app01_book;')
    print(cursor.fetchall())
    
    

django外部脚本调用models数据库操作

import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm02.settings")
    import django
    django.setup()
    
	from app01 import models
    ret = models.Book.objects.all().values('title')
    print(ret)

ORM事务和锁

  • 行级锁

    select_for_update(nowait=False, skip_locked=False) #注意必须用在事务里面
    
    models.Book.objects.select_for_update().filter(id=1)
    
    
  • 事务

    • 全局开启( 需要将配置项ATOMIC_REQUESTS设置为True )

      DATABASES = {
          'default': {
              'ENGINE': 'django.db.backends.mysql',
              'NAME': 'mxshop',
              'HOST': '127.0.0.1',
              'PORT': '3306',
              'USER': 'root',
              'PASSWORD': '123',
              'OPTIONS': {
                  "init_command": "SET default_storage_engine='INNODB'",
             #'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", #配置开启严格sql模式
      
      
              }
              "ATOMIC_REQUESTS": True, #全局开启事务,绑定的是http请求响应整个过程
              "AUTOCOMMIT":False, #全局取消自动提交,慎用
          },
        'other':{
          'ENGINE': 'django.db.backends.mysql', 
                  ......
        } #还可以配置其他数据库
      }
      
      
    • 上面这种方式是统一个http请求对应的所有sql都放在一个事务中执行(要么所有都成功,要么所有都失败)。是全局性的配置, 如果要对某个http请求放水(然后自定义事务),可以用non_atomic_requests修饰器,那么他就不受事务的管控了

      from django.db import transaction
      
      @transaction.non_atomic_requests
      def my_view(request):
          do_stuff()
      
      @transaction.non_atomic_requests(using='other')
      def my_other_view(request):
          do_stuff_on_the_other_database()
      
    • 局部使用事务

      • 给函数做装饰器来使用

        from django.db import transaction
        
        @transaction.atomic
        def viewfunc(request):
            # This code executes inside a transaction.
            do_stuff()
        
      • 作为上下文管理器来使用,其实就是设置事务的保存点

        from django.db import transaction
        
        def viewfunc(request):
            # This code executes in autocommit mode (Django's default).
            do_stuff()
        
            with transaction.atomic():   #保存点
                # This code executes inside a transaction.
                do_more_stuff()
        
            do_other_stuff()
        
        #一旦把atomic代码块放到try/except中,完整性错误就会被自然的处理掉了,比如下面这个例子:
        
        from django.db import IntegrityError, transaction
        
        @transaction.atomic
        def viewfunc(request):
            create_parent()
        
            try:
                with transaction.atomic():
                    generate_relationships()
            except IntegrityError:
                handle_exception()
        
            add_children()
        
posted @ 2019-11-26 21:20  寂静四月  阅读(75)  评论(0编辑  收藏  举报