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)

可是連表查詢也會消耗一部分時間(連表查詢性能低),在某些狀況下,使用空間換取時間沒來得到較小的查詢時間。將連表放在同一張表中.python

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)mysql

去重函數:distinctsql

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

排序函數:order_by,能夠設置多個值,排序先到後數據庫

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

倒序函數:reverse將獲取的數據集翻轉倒序django

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

獲取單獨字段:only和排除某些字段deferapp

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' #數據庫端口
  }
}

使用方式:ide

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  存儲過程
 
 
    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'\u674e\u56db'), (3, u'\u738b\u4e94')]
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")
相關文章
相關標籤/搜索