zoukankan      html  css  js  c++  java
  • python---django中orm的使用(5)数据库的基本操作(性能相关:select_related,和prefetch_related重点)(以及事务操作)

    
    
    ##################################################################
    # PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #   公共方法:通过操作属性,来返回一个新的queryset查询集
    ##################################################################

    def all(self) # 获取所有的数据对象 def filter(self,
    *args, **kwargs) # 条件查询 # 条件可以是:参数,字典,Q def exclude(self, *args, **kwargs) #排除 # 条件查询 # 条件可以是:参数,字典,Q
    class UserType(models.Model):
        title = models.CharField(
            max_length=32
        )
    
    class Person(models.Model):
        user = models.CharField(
            max_length=32
        )
    
        ut = models.ForeignKey(
            to='UserType'
        )
    测试用数据库设计

    性能相关:select_related,和prefetch_related

    select_related:表之间进行join连表操作,一次性获取关联的数据。

    普通方法获取数据:

       persion_list = models.Person.objects.all() for item in persion_list: #是已经从person表中获取的数据 print(item.id,item.ut_id,item.user) #需要再次从usertype中再去查询一次,又发了一次请求,执行的次数增加了,降低了数据库性能 print(item.ut.title)
    使用select_related获取数据:
    persion_list = models.Person.objects.all() #一次性获取到所有数据(数据量大,占内存,只有需要连表时,才会去使用),
       #包含连表(select_related中,可以一次添加多个)的信息 select_related('','',...) persion_list
    = models.Person.objects.all().select_related('ut')   #select * from person left join usertype where person.ut_id = usertype.id  

    for item in persion_list: #是已经从person表中获取的数据 print(item.id,item.ut_id,item.user) #不会再去数据库中查询,因为数据已经取出来放在内存中 print(item.ut.title)

    但是连表查询也会消耗一部分时间(连表查询性能低),在某些情况下,使用空间换取时间没来获得较小的查询时间。将连表放在同一张表中.

    prefetch_related:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。分别在多个表中查询得到结果,然后重组。

    使用prefetch_related获取数据:
        persion_list = models.Person.objects.all()
        #一次性获取到所有数据(数据量大,占内存,只有需要连表时,才会去使用),
       #prefetch_related('','',...)
        persion_list = models.Person.objects.all().prefetch_related('ut')
      #相当于先去person表中select * from person ...(条件)
      #然后再去关联表中取数据:select * from usertype where id in [persion_list中的id列表],
      #Django将两者数据数据相结合,然后返回给用户,
      #此方法不使用连表,也是一次获取所有数据,高效
    for item in persion_list: #是已经从person表中获取的数据 print(item.id,item.ut_id,item.user) #不会再去数据库中查询,因为数据已经取出来放在内存中 print(item.ut.title)

    聚合查询:annotate(分组查询group by)请看:python---django中orm的使用(2)

    去重函数:distinct

    models.Persion.objects.all().distinct('name')   #查询姓名不重复的人

    排序函数:order_by,可以设置多个值,排序先到后

    persion_list = models.Person.objects.all().order_by('ut_id','-id') #-id代表id降序排序,默认升序排列

    倒序函数:reverse将获取的数据集翻转倒序

    persion_list = models.Person.objects.all().order_by('ut_id','-id').reverse()

    获取单独字段:only和排除某些字段defer

    only:
    persion_list = models.Person.objects.only('id','user') #select id,user from persion 只去获取这两个字段值。 注意:但是如果在后面获取数据的时候,获取其他值(不包含在id,user字段),也可以获取到,只是会再次去执行SQL语句 for item in persion_list: print(item.ut_id,item.id,item.user) #其中item.ut_id不在我们only字段中,所以会再次执行SQL语句去获取数据
      #SELECT "app02_person"."id", "app02_person"."ut_id" FROM "app02_person" WHERE "app02_person"."id" = ?; args=(item.id,)
    defer:
    persion_list = models.Person.objects.defer('ut_id') #效果一致同上面only

    切换使用的数据库using:参数为使用的别名 def using(self, alias):

    默认操作下会有models.Person.object.all().using("default') #在setting中设置
    settings文件
    DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), },
      'default1': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(BASE_DIR, 'db.sqlite4'),
        },
      'default2': { 
        'ENGINE': 'django.db.backends.mysql', #数据库引擎设置
        'NAME': 'test', #数据库名称
        'USER': 'root', #数据库用户名
        'PASSWORD':'root', #数据库密码
        'HOST':'', #主机地址,默认localhost
        'PORT':'3306' #数据库端口
      }
    }

    使用方式:

    models.Person.object.all().using("default1') 使用第2种数据库去取数据
    models.Person.object.all().using("default2') 使用第3种数据库引擎下数据库去取数据

    额外的查询:extra:构造额外的查询条件或者映射

    子查询:
    #persion_list = models.Person.objects.extra(select={'new_tag':'select title from app02_usertype where id= %s'},select_params=(1,))#无法实现动态 #SELECT (select title from app02_usertype where id= 1) AS "new_tag", "app02_person"."id", "app02_person"."user", "app02_person"."ut_id" FROM "app02_person"; args=(1,) 条件查询: persion_list = models.Person.objects.extra(tables=['app02_usertype'],where=['app02_person.ut_id = app02_usertype.id']) #SELECT "app02_person"."id", "app02_person"."user", "app02_person"."ut_id" FROM "app02_person" , "app02_usertype" WHERE (app02_person.ut_id = app02_usertype.id); args=()
    
    
    ##################################################
    # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #    公共方法返回一个查询子集
    ##################################################


    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对象

    raw:执行原生SQL:

    import pymysql
    
    #创建连接
    conn = pymysql.connect(host='127.0.0.1',port=3306,user='root',password='root',db='test',charset="utf8")
    #创建游标
    cursor = conn.cursor()
    
    effect_row = cursor.execute("insert  into info (name,age,sex) values ('ls',33,'男')")
    
    conn.commit()
    
    cursor.close()
    
    conn.close()
    pymysql操作数据库
    import pymysql
    
    conn = pymysql.connect(host="127.0.0.1",port=3306,user="root",password="root",db="prct",charset="utf8")
    
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    #执行存储过程
    cursor.callproc('p1')
    
    ret = cursor.fetchall()
    
    conn.commit()
    cursor.close()
    conn.close()
    
    print(ret)
    pymysql操作数据库2  存储过程http://www.cnblogs.com/ssyfj/p/8545601.html
        from django.db import connection,connections
        cursor = connections['default'].cursor()
        cursor.execute("select * from app01_user where id > %s",[1])
        #select * from app01_user where id > 1; args=[1]
        row = cursor.fetchall()
    
        print(row) #[(2, u'u674eu56db'), (3, u'u738bu4e94')]
    Django执行原生SQL
    执行原生SQL:
        res = models.Person.objects.raw('select * from app02_person')
        for item in res:
            print(item)  #Person object
    
    
        res = models.Person.objects.raw('select * from app01_user')  #注意是可以去获取其他数据表的结果集,最好不要这样做,自己对应自己,不要乱用,易混淆
        #select * from app01_user; args=()
        #而且需要为这些联合表中重复字段设置别名,以免重复select id as nid,username from app01_user
      for item in res:
            print(item)  #Person object
    设置参数:
    models.Person.objects.raw('select id as nid,username from app01_user where nid >%s',params=[4,])
    转换列名:
    name_map = {'username': 'un', }
        res = models.Person.objects.raw('select id as nid,username from app01_user', translations=name_map)
    指定数据库
    models.Person.objects.raw('select * from app02_person', using="default")
    ####################################
    # METHODS THAT DO DATABASE QUERIES #   进行数据库查询的方法   
    ####################################

    def count(self): # 获取个数 def
    get(self, *args, **kwargs): # 获取单个对象 def create(self, **kwargs): # 创建对象

    def first(self):
       # 获取第一个
    
    def last(self):
       # 获取最后一个

    def delete(self):
       # 删除
    
    def update(self, **kwargs):
        # 更新
    
    def set(self,**kwargs):
      #更新,将原来数据清除,重新设置为现在的数据(一对多,多对多)
    def exists(self): # 是否有结果
     
    聚合函数:aggregatepython---django中orm的使用(2)
    # 聚合函数,获取字典类型聚合结果
       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}
    aggregate
    get_or_create:存在则获取,不存在则创建并返回该记录
    
        obj, created = models.Person.objects.get_or_create(user='root1',defaults={'ut_id': '1',})
      # defaults 指定创建时,其他字段的值
      # 创建执行:INSERT INTO "app02_person" ("user", "ut_id") VALUES ('root1', 1); args=[u'root1', 1]
      # 查询执行:
    SELECT "app02_person"."id", "app02_person"."user", "app02_person"."ut_id" FROM "app02_person" WHERE "app02_person"."user" = 'root1'; args=(u'root1',)
        print(obj)  #对应的记录(已有,则是原来的记录,原来没有,则是刚刚创建的数据记录)
        print(created)  #若是刚刚创建则为True,否则为False
    update_or_create:如果存在,则更新,否则创建
    
        obj, created = models.Person.objects.update_or_create(user='root1',defaults={'ut_id': '2',})
        #BEGIN; args=None
        #SELECT "app02_person"."id", "app02_person"."user", "app02_person"."ut_id" FROM "app02_person" WHERE "app02_person"."user" = 'root1'; args=(u'root1',)
        #UPDATE "app02_person" SET "user" = 'root1', "ut_id" = 2 WHERE "app02_person"."id" = 5; args=(u'root1', 2, 5)

     set补充:上面数据库是基于外键一对多,一个人的类型只能有一个,一个类型可以有多个人

    下面进行多对多表修改:

    class Tag(models.Model):
        title = models.CharField(
            max_length=32
        )
    
    class User(models.Model):
        user = models.CharField(
            max_length=32
        )
    
        ut = models.ManyToManyField(
            to='Tag'
        )
    User对应Tag(多对多)

    其中测试set:

        obj2 = models.User.objects.filter(id=1).first()
        obj2.ut.set(2,3)
    
        #若是原来是1,2则会被修改为2,3

    也可以用外键进行测试需要使用外键方来进行修改,否则一些方法无法找到:

        obj2 = models.Person.objects.filter(id__gt=2)
        obj1.ut.all()
        #错误,没有该属性,person是多,对应只有一,是一个单独的对象
      #obj1.ut.all() #AttributeError at /test2 'UserType' object has no attribute 'all'
      print(type(obj1))
      #<class 'app02.models.Person'>

       obj2
    = models.UserType.objects.filter(id=1).first()  print(obj2.person_set.all()) #正确,usertype是一,对应多,获取的数据是一个数据集,queryset
        # print(type(obj2))  
       #<QuerySet [<Person: Person object>, <Person: Person object>, <Person: Person object>, <Person: Person object>,<Person: Person object>]>, <class 'app02.models.UserType'>)
     

    ORM的事务操作

    一:导入模块

    from django.db import transaction

    二:简单使用

            try:
                with transaction.atomic():
                    表的增删改操作
        
            except Exception as e:
                return HttpResponse("出现错误....")
            return HttpResponse("ok")
  • 相关阅读:
    支付系统整体架构
    犹太”安息日”
    JWT(JSON Web Token) 【转载】
    详解布隆过滤器的原理、使用场景和注意事项
    缓存一致性策略以及雪崩、穿透问题 【零壹技术栈】
    RPC概念及分类【转载】
    RDLC 微软报表 导出Excel时产生多个工作表 (worksheet)
    asp.net 5 (mvc 6) 获取网站的物理路径
    Asp.net 5 (MVC6) Areas 分区
    MVC6 OWin Microsoft Identity 自定义验证
  • 原文地址:https://www.cnblogs.com/ssyfj/p/8698865.html
Copyright © 2011-2022 走看看