Django中的ORM
通過《如何建立一個Django網站》大概清楚瞭如何建立一個簡單的 Django 網站,並瞭解了Django 中模板和模型使用方法。本篇文章主要在此基礎上,瞭解 Django 中 ORM 相關的用法。
一個 blog 的應用中 mysite/blog/models.py 有以下實體:
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def __str__(self): # __unicode__ on Python 2
return self.name
class Author(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField()
def __str__(self): # __unicode__ on Python 2
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author)
n_comments = models.IntegerField()
n_pingbacks = models.IntegerField()
rating = models.IntegerField()
def __str__(self): # __unicode__ on Python 2
return self.headline
建立物件
>>> from blog.models import Blog
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
>>> b.save()
你也可以修改實體,然後儲存:
>>> b5.name = 'New name'
>>> b5.save()
儲存外來鍵資訊
下面例子更新 entry 例項的 blog 屬性:
>>> from blog.models import Entry
>>> entry = Entry.objects.get(pk=1)
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()
下面是更新一個 ManyToManyField 欄位:
>>> from blog.models import Author
>>> joe = Author.objects.create(name="Joe")
>>> entry.authors.add(joe)
>>>
>>> john = Author.objects.create(name="John")
>>> paul = Author.objects.create(name="Paul")
>>> george = Author.objects.create(name="George")
>>> ringo = Author.objects.create(name="Ringo")
>>> entry.authors.add(john, paul, george, ringo)
查詢物件
Django 中查詢資料庫需要 Manager 和 QuerySet 兩個物件。從資料庫裡檢索物件,可以通過模型的 Manage 來建立 QuerySet,一個 QuerySet 表現為一個資料庫中物件的結合,他可以有0個一個或多個過濾條件,在 SQL裡 QuerySet 相當於 select 語句用 where 或 limit 過濾。你通過模型的 Manage 來獲取 QuerySet。
Manager
Manager 物件附在模型類裡,如果沒有特指定,每個模型類都會有一個 objects 屬性,它構成了這個模型在資料庫所有基本查詢。
Manager 的幾個常用方法:
-
all
:返回一個包含模式裡所有資料庫記錄的 QuerySet -
filter
:返回一個包含符合指定條件的模型記錄的 QuerySet -
exclude
:和 filter 相反,查詢不符合條件的那些記錄 -
get
:獲取單個符合條件的記錄(沒有找到或者又超過一個結果都會丟擲異常) -
order_by
:改變 QuerySet 預設的排序
你可以通過模型的 Manager 物件獲取 QuerySet 物件:
>>> Blog.objects
<django.db.models.manager.Manager object at ...>
>>> b = Blog(name='Foo', tagline='Bar')
>>> b.objects
Traceback:
...
AttributeError: "Manager isn't accessible via Blog instances."
獲取所有的 blog 內容:
>>> all_entries = Entry.objects.all()
#正向排序
Entry.objects.all().order_by("headline")
#反向排序
Entry.objects.all().order_by("-headline")
獲取 headline 為 Python 開頭的 blog :
Entry.objects.filter(headline__startswith="Python")
#支援鏈式操作
Entry.objects.filter(headline__startswith="Python").exclude(pub_date__gte=datetime.now()).filter(pub_date__gte=datetime(2014, 1, 1))
QuerySet 類
QuerySet 接受動態的關鍵字引數,然後轉換成合適的 SQL 語句在資料庫上執行。
QuerySet 的幾個常用方法:
distinct
values
values_list
select_related
-
filter
:返回一個包含符合指定條件的模型記錄的 QuerySet -
extra
:增加結果集以外的欄位
延時查詢
每次你完成一個 QuerySet,你獲得一個全新的結果集,不包括前面的。每次完成的結果集是可以貯存,使用或複用:
>>> q1 = Entry.objects.filter(headline__startswith="What")
>>> q2 = q1.exclude(pub_date__gte=datetime.date.today())
>>> q3 = q1.filter(pub_date__gte=datetime.date.today())
三個 QuerySets 是分開的,第一個是 headline 以 “What” 單詞開頭的結果集,第二個是第一個的子集,即 pub_date 不大於現在的,第三個是第一個的子集 ,pub_date 大於現在的。
QuerySets 是延遲的,建立 QuerySets 不會觸及到資料庫操作,你可以多個過濾合併到一起,直到求值的時候 django才會開始查詢。如:
>>> q = Entry.objects.filter(headline__startswith="What")
>>> q = q.filter(pub_date__lte=datetime.date.today())
>>> q = q.exclude(body_text__icontains="food")
>>> print(q)
雖然看起來執行了三個過濾條件,實際上最後執行 print q
的時候,django 才開始查詢執行 SQL 到資料庫。
可以使用 python 的陣列限制語法限定 QuerySet,如:
>>> Entry.objects.all()[:5]
>>> Entry.objects.all()[5:10]
>>> Entry.objects.all().order_by("headline")[:4]
>>> Entry.objects.all().order_by("headline")[4:8]
一般的,限制 QuerySet 返回新的 QuerySet,不會立即求值查詢,除非你使用了 “step” 引數
>>> Entry.objects.all()[:10:2]
>>> Entry.objects.order_by('headline')[0]
>>> Entry.objects.order_by('headline')[0:1].get()
欄位過濾
欄位查詢是指定 SQL 語句的 WHERE 條件從句,通過 QuerySet 的方法 filter()
, exclude()
和 get()
指定查詢關鍵字。
格式為:field__lookuptype=value
。
lookuptype 有以下幾種:
-
gt
: 大於 -
gte
: 大於等於 -
in
: 包含 -
lt
: 小於 -
lte
: 小於等於 -
exact
: -
iexact
: -
contains
:包含查詢,區分大小寫 -
icontains
:不區分大小寫 -
startswith
:匹配開頭 -
endswith
:匹配結尾 -
istartswith
:匹配開頭,不區分大小寫 -
iendswith
:匹配結尾,不區分大小寫
>>> Entry.objects.filter(pub_date__lte='2006-01-01')
等價於:
SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';
當實體中存在 ForeignKey 時,其外來鍵欄位名稱為模型名稱加上 ‘_id’:
>>> Entry.objects.filter(blog_id=4)
下面是一些舉例:
a、exact
>>> Entry.objects.get(headline__exact="Man bites dog")
相當於:
SELECT ... WHERE headline = 'Man bites dog';
如果查詢沒有提供雙下劃線,那麼會預設 __exact
:
Entry.objects.get(id__exact=14) # Explicit form
Entry.objects.get(id=14) # __exact is implied
#主鍵查詢
Entry.objects.get(pk=14) # pk implies id__exact
b、iexact——忽略大小寫
>>> Blog.objects.get(name__iexact="beatles blog")
將要匹配 blog 名稱為 “Beatles Blog”, “beatles blog”, 甚至是 “BeAtlES blOG”。
c、contains——包含查詢,區分大小寫
Entry.objects.get(headline__contains='Lennon')
轉化為 SQL:
SELECT ... WHERE headline LIKE '%Lennon%';
如果有百分號,則會進行轉義:
Entry.objects.filter(headline__contains='%')
轉義為:
SELECT ... WHERE headline LIKE '%\%%';
d、in 查詢
# Get blogs with id 1, 4 and 7
Entry.objects.filter(pk__in=[1,4,7])
跨關係查詢
跨關係查詢是針對有主外來鍵依賴關係的物件而言的,例如上面的 Author 和 Entry 物件是多對多的對映,可以通過 Entry 物件來過濾 Author的 name:
獲取所有 blog 名稱為 Beatles Blog 的 Entry 列表:
>>> Entry.objects.filter(blog__name='Beatles Blog')
也可以反向查詢:
>>> Blog.objects.filter(entry__headline__contains='Lennon')
如果跨越多層關係查詢,中間模型沒有值,django會作為空對待不會發生異常。
Blog.objects.filter(entry__authors__name='Lennon')
Blog.objects.filter(entry__authors__name__isnull=True)
Blog.objects.filter(entry__authors__isnull=False,
entry__authors__name__isnull=True)
也支援多條件跨關係查詢:
Blog.objects.filter(entry__headline__contains='Lennon',
entry__pub_date__year=2008)
或者:
Blog.objects.filter(entry__headline__contains='Lennon').filter(
entry__pub_date__year=2008)
使用 Extra 調整 SQL
用extra可以修復QuerySet生成的原始SQL的各個部分,它接受四個關鍵字引數。如下:
-
select
:修改select語句 -
where
:提供額外的where子句 -
tables
:提供額外的表 -
params
:安全的替換動態引數
增加結果集以外的欄位:
queryset.extra(select={'成年':'age>18'})
提供額外的 where 條件:
queryset.extra(where=["first like '%小明%' "])
提供額外的表:
queryset.extra(tables=['myapp_person'])
安全的替換動態引數:
##'%s' is not replaced with normal string
matches = Author.objects.all().extra(where=["first = '%s' "], params= [unknown-input ( ) ])
F 關鍵字引數
前面給的例子裡,我們建立了過濾,比照模型欄位值和一個固定的值,但是如果我們想比較同一個模型裡的一個欄位和另一個欄位的值,django 提供 F()
——專門取物件中某列值的操作。
>>> from django.db.models import F
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))
當然,還支援加減乘除和模計算:
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2)
>>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))
>>>
>>> Entry.objects.filter(authors__name=F('blog__name'))
對於日期型別欄位,可以使用 timedelta 方法:
>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
還支援位操作 .bitand()
和 .bitor()
:
>>> F('somefield').bitand(16)
主鍵查詢
Django 支援使用 pk 代替主鍵:
>>> Blog.objects.get(id__exact=14) # Explicit form
>>> Blog.objects.get(id=14) # __exact is implied
>>> Blog.objects.get(pk=14) # pk implies id__exact
pk 還可以用於其他的查詢型別:
# Get blogs entries with id 1, 4 and 7
>>> Blog.objects.filter(pk__in=[1,4,7])
# Get all blog entries with id > 14
>>> Blog.objects.filter(pk__gt=14)
>>> Entry.objects.filter(blog__id__exact=3) # Explicit form
>>> Entry.objects.filter(blog__id=3) # __exact is implied
>>> Entry.objects.filter(blog__pk=3) # __pk implies __id__exact
Q 關鍵字引數
QuerySet 可以通過一個叫 Q 的關鍵字引數封裝類進一步引數化,允許使用更復雜的邏輯查詢。其結果 Q對 象可以作為 filter 或 exclude 方法的關鍵字引數。
例子:
from django.db.models import Q
Q(question__startswith='What')
支援 & 和 | 操作符: |
Q(question__startswith='Who') | Q(question__startswith='What')
上面的查詢翻譯成 sql 語句:
WHERE question LIKE 'Who%' OR question LIKE 'What%'
取反操作:
Q(question__startswith='Who') | ~Q(pub_date__year=2005)
也可以用在 filter()
、exclude()
、get()
中:
Poll.objects.get(
Q(question__startswith='Who'),
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)
翻譯成 sql 語句為:
SELECT * from polls WHERE question LIKE 'Who%'
AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
刪除物件
>>>entry = Entry.objects.get(pk=1)
>>>entry.delete()
>>>Blog.objects.all().delete()
>>>Entry.objects.filter(pub_date__year=2005).delete()
關係物件
當物件之間存在對映關係或者關聯時,該如何查詢呢?
當你在模型裡定義一個關係時,模型例項會有一個方便的 API 來訪問關係物件。以下分幾種對映關係分別描述。
One-to-many關係
如果一個物件有ForeignKey,這個模型例項訪問關係物件通過簡單的屬性:
>>> e = Entry.objects.get(id=2)
>>> e.blog # Returns the related Blog object.
你可以憑藉外來鍵屬性獲取和賦值,修改外來鍵值知道執行 save()
方法才會儲存到資料庫:
>>> e = Entry.objects.get(id=2)
>>> e.blog = some_blog
>>> e.save()
如果關聯的物件可以為空,則可以將關聯物件職位 None,刪除關聯:
>>> e = Entry.objects.get(id=2)
>>> e.blog = None
>>> e.save() # "UPDATE blog_entry SET blog_id = NULL ...;"
子查詢:
>>> e = Entry.objects.get(id=2)
>>> print(e.blog) # Hits the database to retrieve the associated Blog.
>>> print(e.blog) # Doesn't hit the database; uses cached version.
也可以使用 select_related() 方法,該方法會提前將關聯物件查詢出來:
>>> e = Entry.objects.select_related().get(id=2)
>>> print(e.blog) # Doesn't hit the database; uses cached version.
>>> print(e.blog) # Doesn't hit the database; uses cached version.
你也可以通過 模型_set
來訪問關係物件的另一邊,在 Blog 物件並沒有維護 Entry 列表,但是你可以通過下面方式從 Blog 物件訪問 Entry 列表:
>>> b = Blog.objects.get(id=1)
>>> b.entry_set.all() # Returns all Entry objects related to Blog.
# b.entry_set is a Manager that returns QuerySets.
>>> b.entry_set.filter(headline__contains='Lennon')
>>> b.entry_set.count()
模型_set
可以通過 related_name
屬性來修改,例如將 Entry 模型中的定義修改為:
blog = ForeignKey(Blog, related_name='entries')
上面的查詢就會變成:
>>> b = Blog.objects.get(id=1)
>>> b.entries.all() # Returns all Entry objects related to Blog.
# b.entries is a Manager that returns QuerySets.
>>> b.entries.filter(headline__contains='Lennon')
>>> b.entries.count()
Many-to-many關係
e = Entry.objects.get(id=3)
e.authors.all() # Returns all Author objects for this Entry.
e.authors.count()
e.authors.filter(name__contains='John')
a = Author.objects.get(id=5)
a.entry_set.all() # Returns all Entry objects for this Author.
One-to-one關係
class EntryDetail(models.Model):
entry = models.OneToOneField(Entry)
details = models.TextField()
ed = EntryDetail.objects.get(id=2)
ed.entry # Returns the related Entry object.
當反向查詢時:
e = Entry.objects.get(id=2)
e.entrydetail # returns the related EntryDetail object
這時候如果沒有關聯物件,則會丟擲 DoesNotExist
異常。
並且還可以修改:
e.entrydetail = ed
相關文章
- django中orm的使用DjangoORM
- Django ORMDjangoORM
- Django ORM QuerySetDjangoORM
- django mysqlclient ormDjangoMySqlclientORM
- 03.Django-ORMDjangoORM
- Django ORM 多表操作DjangoORM
- 如何在Django ORM中進行not查詢?DjangoORM
- Django中ORM外來鍵和表的關係(Django程式設計-4)DjangoORM程式設計
- Django中ORM找出內容不為空的資料DjangoORM
- Django ORM 單表操作DjangoORM
- django的orm有什麼優點DjangoORM
- Django之ORM連表操作DjangoORM
- Django-ORM-單表操作DjangoORM
- django ORM 中 select_related 與 prefetch_related 的使用場景DjangoORM
- Django-基礎-2-ORMDjangoORM
- Django-ORM 之查詢排序DjangoORM排序
- django指令碼orm中使用原生sqlDjango指令碼ORMSQL
- Django-ORM---查詢集介紹DjangoORM
- django | 常見 SQL 及其對應的 ORM 寫法DjangoSQLORM
- Django-ORM 之指定欄位別名DjangoORM
- day52:django:ORM單表/多表操作DjangoORM
- 如何在 django 中用 orm 寫子查詢DjangoORM
- Django之ORM常用欄位和引數DjangoORM
- Django中的ORM如何通過資料庫中的表格資訊自動化生成Model 模型類?DjangoORM資料庫模型
- 請不要以python思維對待django ORMPythonDjangoORM
- Django基礎二靜態檔案和ORMDjangoORM
- Django ORM 引發的資料庫 N+1 效能問題DjangoORM資料庫
- ORM資料增刪改查 django請求生命週期 django路由層 反向解析ORMDjango路由
- Django中的QuerySetDjango
- Django中的ForeignKeyDjango
- Django基礎四之測試環境和ORM查詢DjangoORM
- beego orm中時區的問題GoORM
- 深入 Django 模型層:資料庫設計與 ORM 實踐指南Django模型資料庫ORM
- Django框架:8、聚合查詢、分組查詢、F與Q查詢、ORM查詢最佳化、ORM事務操作、ORM常用欄位型別、ORM常用欄位引數Django框架ORM型別
- 靈活使用 SQLAlchemy 中的 ORM 查詢SQLORM
- Java中Stalactite ORM簡介JavaORM
- django 中 mysql 的 charset 配置DjangoMySql
- Django 中 Aggregation聚合的使用Django
- JSON欄位型別在ORM中的使用JSON型別ORM