Django model對象接口
阿新 • • 發佈:2018-06-13
any fetch pytho pizza dfa _for xtra 相同 ood
Django model查詢
# 直接獲取表對應字段的值,列表嵌元組形式返回
Entry.objects.values_list(‘id‘, ‘headline‘)
#<QuerySet [(1, ‘First entry‘), ...]>
from django.db.models.functions import Lower
# 使用函數。對查詢的結果進行處理,這裏將對應字段headline的值全部轉為小寫。
# 更多對結果處理對函數都在該模塊內
Entry.objects.values_list(‘id‘, Lower(‘headline‘))
# 查詢的結果被處理了
#<QuerySet [(1, ‘first entry‘), ...]>
Entry.objects.values_list(‘id‘)
# 對於單個字段的值的結果,默認是這樣的結果,也是元組形式返回,通常這並不是你想要的結果
#<QuerySet[(1,), (2,), (3,), ...]>
# 使用關鍵字參數flat,就只能將單個字段的結果以列表形式直接返回了
Entry.objects.values_list(‘id‘, flat=True)
# <QuerySet [1, 2, 3, ...]>
# 當有多個字段需要返回時,也可以以命名元組形式返回
# 使用關鍵字參數named
Entry.objects.values_list(‘id‘ , ‘headline‘, named=True)
#<QuerySet [Row(id=1, headline=‘First entry‘), ...]>
# 當你需要只獲取指定字段的值,可以使用get方法獲取,前提是你能保證存在pk=1這條記錄,否則會拋出異常
Entry.objects.values_list(‘headline‘, flat=True).get(pk=1)
# 這個顯示就是pk為1的字段headline對應的值
# ‘First entry‘
# 使用values得到是字典形式的結果,也支持函數參數對值進行處理
Blog.objects.values()
# <QuerySet [{‘id‘: 1, ‘name‘: ‘Beatles Blog‘, ‘tagline‘: ‘All the latest Beatles news.‘}]>
# 對指定key的結果轉換為小寫輸出
from django.db.models.functions import Lower
Blog.objects.values(lower_name=Lower(‘name‘))
#<QuerySet [{‘lower_name‘: ‘beatles blog‘}]>
# 使用count方法將相同的字段數據進行計數統計
from django.db.models import Count
Blog.objects.values(‘author‘, entries=Count(‘entry‘))
# <QuerySet [{‘author‘: 1, ‘entries‘: 20}, {‘author‘: 1, ‘entries‘: 13}]>
# 如果需要使用group by形式分組查詢結果。
Blog.objects.values(‘author‘).annotate(entries=Count(‘entry‘))
# <QuerySet [{‘author‘: 1, ‘entries‘: 33}]>
# 當獲取一個外鍵字段值時。如字段名稱foo為外鍵字段。values查詢時。使用foo 和foo_id是等價當效果
Entry.objects.values()
# <QuerySet [{‘blog_id‘: 1, ‘headline‘: ‘First Entry‘, ...}, ...]>
Entry.objects.values(‘blog‘)
# <QuerySet [{‘blog‘: 1}, ...]>
Entry.objects.values(‘blog_id‘)
# <QuerySet [{‘blog_id‘: 1}, ...]>
# 以下這種方式查詢是等價的
Blog.objects.values().order_by(‘id‘)
Blog.objects.order_by(‘id‘).values()
# 合並查詢結果
qs1 = Author.objects.values_list(‘name‘)
qs2 = Entry.objects.values_list(‘headline‘)
qs1.union(qs2).order_by(‘name‘)
# 合並多個查詢結果
qs1.union(qs2, qs3)
# 返回交集查詢結果
qs1.intersection(qs2, qs3)
# 返回差集查詢結果
qs1.difference(qs2, qs3)
select_related()查詢優化
# 對於有外鍵關聯的表查詢。如果不使用select_related查詢。那麽最終只會查詢單條沒有任何關聯的結果
# 例如。獲取ID為5的entry記錄,第一次會查詢一次數據庫
e = Entry.objects.get(id=5)
# 當訪問這條記錄對應的外鍵關聯字段時。還會再次查詢數據庫。顯然。這不是我們想要的。我們希望查詢上面的記錄同時把對應的blog也一並查詢
b = e.blog
# 使用這種方式。明確告知查詢外鍵字段blog對應的實例對象結果
# select_related不指定參數時。則獲取所以與之外鍵關聯的對象
e = Entry.objects.select_related(‘blog‘).get(id=5)
# 這時再去訪問blog對象時。不會進行第二次數據庫查詢。
b = e.blog
# 下面兩個語句。filter和select_related順序先後都一樣。沒什麽區別。
Entry.objects.filter(pub_date__gt=timezone.now()).select_related(‘blog‘)
Entry.objects.select_related(‘blog‘).filter(pub_date__gt=timezone.now())
from django.db import models
class City(models.Model):
# ...
pass
class Person(models.Model):
# ...
hometown = models.ForeignKey( City,
on_delete=models.SET_NULL, blank=True,
null=True,
)
class Book(models.Model):
# ...
author = models.ForeignKey(Person, on_delete=models.CASCADE)
# 下面這種查詢方式將會把City對象緩存起來。
b = Book.objects.select_related(‘author__hometown‘).get(id=4)
p = b.author # 不從數據庫查詢獲取,直接從上面查詢的緩存獲取結果
c = p.hometown # 不從數據庫查詢獲取,直接從上面查詢的緩存獲取結果
b = Book.objects.get(id=4) # 沒有使用select_related查詢
p = b.author # 這時會從數據庫查詢獲取結果
c = p.hometown # 這時會從數據庫查詢獲取結果
# select_related同樣支持鏈式操作
select_related(‘foo‘, ‘bar‘)
# 等價於
select_related(‘foo‘).select_related(‘bar‘)
prefetch_related()查詢優化
from django.db import models
class Topping(models.Model):
name = models.CharField(max_length=30)
class Pizza(models.Model):
name = models.CharField(max_length=50)
toppings = models.ManyToManyField(Topping)
def __str__(self):
return "%s (%s)" % (
self.name,
", ".join(topping.name for topping in self.toppings.all()),
)
class Restaurant(models.Model):
pizzas = models.ManyToManyField(Pizza, related_name=‘restaurants‘)
best_pizza = models.ForeignKey(Pizza, related_name=‘championed_by‘, on_delete=models.CASCADE)
# 在大量查詢集的情況下。沒有進行優化。那麽每次調用
# Pizza.__str__()時(print會自動觸發該方法),會每觸發一次,查詢一次數據庫
Pizza.objects.all()
# ["Hawaiian (ham, pineapple)", "Seafood (prawns, smoked salmon)"...
# 將上面的方式優化為下面這種
Pizza.objects.all().prefetch_related(‘toppings‘)
# 這時再調用self.toppings.all()將從查詢緩存中獲取數據。不是去查詢數據庫
# 如果這時再對緩存數據進行子查詢,那麽依然會查詢數據庫。而不是從緩存過濾查詢集
pizzas = Pizza.objects.prefetch_related(‘toppings‘)
# 下面的子句過濾會從數據庫查詢獲取,這時的prefetch_related鏈式調用並沒有效果,反而降低了性能,所以使用這個功能需要格外小心
[list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]
#
Restaurant.objects.prefetch_related(‘pizzas__toppings‘)
# 下面這種方式會產生三次查詢
Restaurant.objects.prefetch_related(‘best_pizza__toppings‘)
# 應該優化寫成這種形式,這樣會優化查詢次數為2次
Restaurant.objects.select_related(‘best_pizza‘).prefetch_related(‘best_pizza__toppings‘)
- select_related 和 prefetch_related。前者適用於單條數據的查詢集緩存。後者使用於大的查詢集緩存
- 普通的foreign key用select_related,many to many用prefetch_related
extra,擴展sql表達式,該功能會被遺棄,使用RawSQL替代
# 使用這種方式要避免SQL註入攻擊
qs.extra(select={‘val‘: "select col from sometable where othercol = %s"}, select_params=(someparam,),)
# 與下面這種方式等價
qs.annotate(val=RawSQL("select col from sometable where othercol = %s", (someparam,)))
# 以查詢結果方式展示,is_recent相當於查詢的別名
Entry.objects.extra(select={‘is_recent‘: "pub_date > ‘2006-01-01‘"})
# SQL等價於
# SELECT blog_entry.*, (pub_date > ‘2006-01-01‘) AS is_recent FROM blog_entry
Blog.objects.extra( select={
‘entry_count‘: ‘SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id= blog_blog.id‘
}, )
# 等價於
# SELECT blog_blog.*, (SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id =blog_blog.id) AS entry_count FROM blog_blog;
Entry.objects.extra(where=["foo=‘a‘ OR bar = ‘a‘", "baz = ‘a‘"])
# SELECT * FROM blog_entry WHERE (foo=‘a‘ OR bar=‘a‘) AND (baz=‘a‘)
# 對需要排序的查詢。需要使用正確的查詢字段
q = Entry.objects.extra(select={‘is_recent‘: "pub_date > ‘2006-01-01‘"})
q = q.extra(order_by = [‘-is_recent‘])
# 對於參數查詢,推薦使用這種查詢方式
Entry.objects.extra(where=[‘headline=%s‘], params=[‘Lennon‘])
# 不推薦使用下面這種方式查詢
Entry.objects.extra(where=["headline=‘Lennon‘"])
defer 延遲查詢方法。
- 當你需要訪問該字段的值時才去查詢。實際上有的類型values。指定查詢字段,只不過這裏是取反,參數裏的字段不查。而values是查參數裏的字段
- 對主鍵貌似無效
class Organization(models.Model):
"""
組織關系架構表
"""
name = models.CharField(max_length=255, verbose_name=‘組織名稱‘)
parent = models.ForeignKey(‘self‘, verbose_name=‘所屬組織‘, blank=True,
null=True, on_delete=models.SET_NULL)
company = models.ForeignKey(‘Company‘, verbose_name=‘所屬公司‘, blank=True,
null=True, on_delete=models.SET_NULL)
admin = models.ForeignKey(‘User‘, verbose_name=‘管理負責人‘, blank=True,
null=True, on_delete=models.SET_NULL,
related_name=‘organization_admin‘)
members = models.ManyToManyField(‘User‘, verbose_name=‘組織成員‘, blank=True,
related_name=‘organization_members‘)
label = models.ManyToManyField(‘Label‘, verbose_name=‘標簽集合‘, blank=True)
Organization.objects.defer(‘id‘).query.__str__()
# SELECT `organization`.`id`, `organization`.`name`, `organization`.`parent_id`, `organization`.`company_id`, `organization`.`admin_id` FROM `organization`
Organization.objects.defer(‘company‘).query.__str__()
# SELECT `organization`.`id`, `organization`.`name`, `organization`.`parent_id`, `organization`.`admin_id` FROM `organization`
Organization.objects.defer(‘company‘, ‘admin‘).query.__str__()
# SELECT `organization`.`id`, `organization`.`name`, `organization`.`parent_id` FROM `organization`
# 看出效果了麽?
- 如果對於外鍵部分字段想延遲查詢,也可以使用defer
# headline為外鍵字段。select_related會獲取外鍵對應對表的數據查詢。使用defer會過濾掉暫時不需要外鍵某些表的數據字段
Blog.objects.select_related().defer("entry__headline", "entry__body")
- 當你不想花過多時間考慮在需要過濾掉哪些字段需要延遲查詢時,可以考慮通過創建同名表model。
# 兩個表名一樣
class CommonlyUsedModel(models.Model):
f1 = models.CharField(max_length=10)
class Meta:
managed = False # 這個屬性告訴Django在遷移數據庫配置時,不要把它考慮進去。否則如果存在兩張一樣的表,Django遷移會出錯的
db_table = ‘app_largetable‘
class ManagedModel(models.Model):
f1 = models.CharField(max_length=10)
f2 = models.CharField(max_length=10)
class Meta:
db_table = ‘app_largetable‘
# 下面這兩種查詢方式完全一樣的查詢結果。第一種不需要關註哪些需要延遲查詢的字段。
CommonlyUsedModel.objects.all()
ManagedModel.objects.all().defer(‘f2‘)
# 註意。當在使用defer查詢再調用save方法時。保存的只有已經加載的字段數據。延遲的字段值不會保存,也就是說,這裏的f2即使有對應的f2關鍵字參數賦值。也不會更新f2的值到數據庫
only 相對於defer作的優化。也是上面這個例子的一個解決方案
- 顧名思義。只查詢某些字段
# 第一種,排除兩個字段。實際也就剩余name字段需要現在查詢
Person.objects.defer("age", "biography")
# 第二種是直接查詢name字段,其它字段不管
Person.objects.only("name")
Organization.objects.only(‘company‘, ‘admin‘).query.__str__()
# SELECT `organization`.`id`, `organization`.`company_id`, `organization`.`admin_id` FROM `organization`
# 註意。在鏈式查詢時。只會保留最後一個only的查詢字段。其余被排除
Organization.objects.only(‘company‘).only(‘admin‘).query.__str__()
# SELECT `organization`.`id`, `organization`.`admin_id` FROM `organization`
# defer在前與在後調用區別
# defer在only後。那麽only與defer重合的字段會被延遲。只查詢only與defer的差集字段
Organization.objects.only(‘company‘).defer(‘admin‘).query.__str__()
# SELECT `organization`.`id`, `organization`.`company_id` FROM `organization`
Organization.objects.only(‘company‘, ‘admin‘).defer(‘admin‘).query.__str__()
# SELECT `organization`.`id`, `organization`.`company_id` FROM `organization`
Organization.objects.only(‘company‘, ‘admin‘).defer(‘company‘).query.__str__()
# SELECT `organization`.`id`, `organization`.`admin_id` FROM `organization`
# defer在前已經看不透了。。。
Organization.objects.defer(‘company‘, ‘admin‘).only(‘company‘).query.__str__()
‘SELECT `organization`.`id`, `organization`.`name`, `organization`.`parent_id`, `organization`.`company_id`, `organization`.`admin_id` FROM `organization`‘
Organization.objects.defer(‘company‘, ‘admin‘).only(‘admin‘).query.__str__()
‘SELECT `organization`.`id`, `organization`.`name`, `organization`.`parent_id`, `organization`.`company_id`, `organization`.`admin_id` FROM `organization`‘
Organization.objects.defer(‘admin‘).only(‘admin‘).query.__str__()
‘SELECT `organization`.`id`, `organization`.`name`, `organization`.`parent_id`, `organization`.`company_id`, `organization`.`admin_id` FROM `organization`‘
Organization.objects.defer(‘admin‘).only(‘admin‘,‘company‘).query.__str__()
‘SELECT `organization`.`id`, `organization`.`company_id` FROM `organization`‘
select_for_update(nowait=False, skip_locked=False, of=())
- 返回一個查詢集前將會進行行級鎖,直到這個事務完成
意味著在事務完成前,所有匹配的行會被加鎖。不允許修改。直到該事務完成。才會釋放鎖
Django model對象接口