1. 程式人生 > >Django model對象接口

Django model對象接口

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對象接口