extra()方法
結果集修改器,一種提供額外查詢參數的機制
raw()方法
示例
執行原始查詢
管理器的 raw() 方法可以用於執行原始 SQL 並返回模型實例:
- Manager. raw( raw_query, params=None, translations=None)
這個方法執行一個原始 SQL 查詢並返回一個 RawQuerySet 實例。這個 RawQuerySet 實例是可迭代的,就象通常的查詢集一樣可以提供對象實例。
來個例子就容易明白了,假設你有下面的模型:
class Person(models.Model): first_name = models.CharField(...) last_name = models.CharField(...) birth_date = models.DateField(...)
你可以象這樣執行定制的 SQL
>>> for p in Person.objects.raw('SELECT * FROM myapp_person'): ... print p John Smith Jane Jones
當然這個例子不是十分有趣,只是相當於運行了 Person.objects.all() 。但是, raw() 有一大把的相當給力的參數。
模型表名稱
上例中 Person 表的名稱是什么?
缺省情況下, Django 把模型的“應用標簽”(你在 manage.py startapp 中使用的名稱)和模型的類名稱用下划線連接起來作為一個數據庫表名。在上例中,我們假設 Person 模型在名稱 myapp 的應用中,所以它的表名是 myapp_person 。
詳情參見 db_table 選項的文檔。這個選項還可以讓你手動指定數據庫表名。
Warning
傳遞給 .raw() 的 SQL 語句是不經過 Django 查驗的。 Django 會指望從數據庫中返回一組行,但這不是強迫性的。如果查詢沒有返回行,那么可以會產生一個(可能是隱性的)錯誤。
映射查詢字段到模型字段
raw() 自動映射查詢中的字段到模型字段。
查詢中字段的順序是無所謂的。亦即下面兩句話是一樣的:
>>> Person.objects.raw('SELECT id, first_name, last_name, birth_date FROM myapp_person') ... >>> Person.objects.raw('SELECT last_name, birth_date, first_name, id FROM myapp_person') ...
映射是根據名字進行的。你可以使用 SQL 的 AS 子句來進行映射,因此如果你有一些其他包含 Person 數據的表,那么可以輕易地與 Person 實例相映射:
>>> Person.objects.raw('''SELECT first AS first_name, ... last AS last_name, ... bd AS birth_date, ... pk as id, ... FROM some_other_table''')
當名稱一致時,模型實例就會正確創建。
還有一種方法,你可以使用 raw() 的 translations 參數來進行映射。這個參數是一個映射查詢字段名稱和模型字段名稱的字典。上述的查詢也可以寫成:
>>> name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'} >>> Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)
索引查找
raw() 支持索引,因此如果你只需要第一個結果,可以這樣:
>>> first_person = Person.objects.raw('SELECT * from myapp_person')[0]
但是,索引和切片不是在數據庫級別執行的。如果你在數據庫中有大量的 Person 對象,那么在 SQL 級別來限制查詢更有效率:
>>> first_person = Person.objects.raw('SELECT * from myapp_person LIMIT 1')[0]
延遲模型字段
字段也可以是不完全的:
>>> people = Person.objects.raw('SELECT id, first_name FROM myapp_person')
本例中查詢返回的 Person 對象將會是延遲模型實例( (參見 defer() )。也就是說在查詢中省略的字段會在需要是載入。例如:
>>> for p in Person.objects.raw('SELECT id, first_name FROM myapp_person'): ... print p.first_name, # This will be retrieved by the original query ... print p.last_name # This will be retrieved on demand ... John Smith Jane Jones
從表面看,好像查詢同時獲取了名和姓。但是,這個例子實際發出了三個查詢。原始查詢只獲取了名,而兩個姓是在打印時分別獲取的。
唯一不能省略的字段是主鍵。 Django 使用主鍵來判別模型實例,所以在一個原始查詢中必須包含主鍵,否則會拋出一個 InvalidQuery 異常。
加入統計
你也可以執行包含模型中沒有的字段的查詢。例如,我們可以使用 PostgreSQL 的 age() 函數 來讓數據庫統計出人員的年齡:
>>> people = Person.objects.raw('SELECT *, age(birth_date) AS age FROM myapp_person') >>> for p in people: ... print "%s is %s." % (p.first_name, p.age) John is 37. Jane is 42. ...
向 raw() 傳遞參數
如果你需要執行帶參數的查詢,你可以使用 params 參數:
>>> lname = 'Doe' >>> Person.objects.raw('SELECT * FROM myapp_person WHERE last_name = %s', [lname])
params 是一個參數列表。在查詢字符串中你要使用 %s 占位符(不管你用何種數據庫引擎)。占位符會被 params 列表中的參數代替。
Warning
不要在原始查詢中使用格式化字符串!
你可能將上文的查詢寫成這樣:
>>> query = 'SELECT * FROM myapp_person WHERE last_name = %s' % lname >>> Person.objects.raw(query)
千萬不要這樣做。
使用 params 列表可以保護你避開 SQL 注入攻擊 (通過漏洞把惡意 SQL 注入你的數據庫)。如果你使用了字符串插入,那么盡早你會成為 SQL 注入的受害者,因此請謹記使用 params 列表。
執行自定義SQL
(
這種方式完全不依賴model,前兩種還是要依賴於model;直接執行自定義SQL,這種方式可以完全避免數據模型,而是直接執行原始的SQL語句。
)
示例
執行自定義sql:
from django.db import connection
cursor=connection.cursor()
#插入操作
cursor.execute("insert into hello_author(name) values('郭敬明')")
#更新操作
cursor.execute('update hello_author set name='abc' where name='bcd'')
#刪除操作
cursor.execute('delete from hello_author where name='abc'')
#查詢操作
cursor.execute('select * from hello_author')
raw=cursor.fetchone() #返回結果行游標直讀向前,讀取一條
cursor.fetchall() #讀取所有
擴展
有時候,甚至 Manager.raw() 還是不夠用:你可能需要執行不明確對應模型的查詢或者直接執行 UPDATE 、 INSERT 或 DELETE 查詢。
在這種情況下,你可以直接操作數據庫,完全繞開模型層。
django.db.connection 對象表現缺省數據庫連接, django.db.transaction 表現缺省數據庫事務。要使用數據庫連接可以調用 connection.cursor() 來得到一個指針對象。然后,就可以調用 cursor.execute(sql, [params]) 來執行 SQL 和 cursor.fetchone() 或 cursor.fetchall() 來返回結果行。在執行一個改變數據的操作后,你應當調用 transaction.commit_unless_managed() 來確保你的改變被提交。如果只是獲取數據就不必提交了。例如:
def my_custom_sql(): from django.db import connection, transaction cursor = connection.cursor() # Data modifying operation - commit required cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz]) transaction.commit_unless_managed() # Data retrieval operation - no commit required cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz]) row = cursor.fetchone() return row
如果你使用多個數據庫,那么可以使用 django.db.connections 來獲得指定數據庫的連接(和指針)。 django.db.connections 是一個類似字典的對象,允許你通過別名來獲得指定數據庫的連接:
from django.db import connections cursor = connections['my_db_alias'].cursor() # Your code here... transaction.commit_unless_managed(using='my_db_alias')
事務和原始 SQL
當你執行了原始 SQL 之后, Django 會自動把當前事務放入黑箱。你必須確保事務正確結束。
Django 1.3 版之前,當使用原始 SQL 時必須通過 transaction.set_dirty() 手動把事務放入黑箱。
在Django的ORM中,想使用事務操作時,要先導入一個Django的內置模塊
from django.db import transaction def index(request): from django.db import transaction try: with transaction.atomic(): models.Userinfo.objects.create(username="python001",email="python001@qq.com") models.Group.objects.create(title="python002") except Exception as e: return HttpResponse("出現錯誤....") return HttpResponse("ok")
連接和指針
連接 和 指針 大多數執行標准 Python DB-API (除非是為了 事務處理 )。如果你不熟悉 Python DB-API ,那么要注意 cursor.execute() 中的 SQL 語句使用占位符 "%s" 比直接在 SQL 中添加參數要好。如果你使用了這個技術,那么基礎數據庫會根據需要為你的參數自動添加引號和轉義符。這樣做是符合一致性原則的,並且是一種聰明的作法。(還要注意 Django 使用 "%s" 作為占位符,而 不是 "?" 。)