W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
有時候,甚至 ?Manager.raw()
? 都無法滿足需求:你可能要執(zhí)行不明確映射至模型的查詢語句,或者就是直接執(zhí)行 ?UPDATE
?, ?INSERT
?或 ?DELETE
?語句。
這些情況下,你總是能直接訪問數(shù)據(jù)庫,完全繞過模型層。
對象 ?django.db.connection
? 代表默認數(shù)據(jù)庫連接。要使用這個數(shù)據(jù)庫連接,調(diào)用 ?connection.cursor()
? 來獲取一個指針對象。然后,調(diào)用 ?cursor.execute(sql, [params])
? 來執(zhí)行該 SQL 和 ?cursor.fetchone()
?,或 ?cursor.fetchall()
? 獲取結(jié)果數(shù)據(jù)。
例如:
from django.db import connection
def my_custom_sql(self):
with connection.cursor() as cursor:
cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])
cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])
row = cursor.fetchone()
return row
要避免 SQL 注入,你絕對不能在 SQL 字符串中用引號包裹 ?%s
? 占位符。
注意,若要在查詢中包含文本的百分號,你需要在傳入?yún)?shù)使用兩個百分號:
cursor.execute("SELECT foo FROM bar WHERE baz = '30%'")
cursor.execute("SELECT foo FROM bar WHERE baz = '30%%' AND id = %s", [self.id])
若你同時使用不止一個數(shù)據(jù)庫,你可以使用 ?django.db.connections
? 獲取指定數(shù)據(jù)庫的連接(和指針)。 ?django.db.connections
? 是一個類字典對象,它允許你通過連接別名獲取指定連接:
from django.db import connections
with connections['my_db_alias'].cursor() as cursor:
# Your code here...
默認情況下,Python DB API 返回的結(jié)果不會包含字段名,這意味著你最終會收到一個 ?list
?,而不是一個 ?dict
?。要追求較少的運算和內(nèi)存消耗,你可以以 ?dict
?返回結(jié)果,通過使用如下的玩意:
def dictfetchall(cursor):
"Return all rows from a cursor as a dict"
columns = [col[0] for col in cursor.description]
return [
dict(zip(columns, row))
for row in cursor.fetchall()
]
另一個選項是使用來自 Python 標準庫的 ?collections.namedtuple()
?。 ?namedtuple
?是一個類元組對象,可以通過屬性查找來訪問其包含的字段;也能通過索引和迭代。結(jié)果都是不可變的,但能通過字段名或索引訪問,這很實用:
from collections import namedtuple
def namedtuplefetchall(cursor):
"Return all rows from a cursor as a namedtuple"
desc = cursor.description
nt_result = namedtuple('Result', [col[0] for col in desc])
return [nt_result(*row) for row in cursor.fetchall()]
這有個例子,介紹了三者之間的不同:
>>> cursor.execute("SELECT id, parent_id FROM test LIMIT 2");
>>> cursor.fetchall()
((54360982, None), (54360880, None))
>>> cursor.execute("SELECT id, parent_id FROM test LIMIT 2");
>>> dictfetchall(cursor)
[{'parent_id': None, 'id': 54360982}, {'parent_id': None, 'id': 54360880}]
>>> cursor.execute("SELECT id, parent_id FROM test LIMIT 2");
>>> results = namedtuplefetchall(cursor)
>>> results
[Result(id=54360982, parent_id=None), Result(id=54360880, parent_id=None)]
>>> results[0].id
54360982
>>> results[0][0]
54360982
connection 和 cursor 實現(xiàn)了 PEP 249 中介紹的大部分標準 Python DB-API。
若你并不熟悉 Python DB-API,要注意 ?cursor.execute()
? 中的 SQL 語句使用了占位符 "?%s
?",而不是直接在 SQL 中添加參數(shù)。若你使用這個技巧,潛在的數(shù)據(jù)庫庫會自動在需要時轉(zhuǎn)義參數(shù)。
也要注意,Django 期望 "?%s
?" 占位符,而 不是 "??
?" 占位符,后者由 SQLite Python 綁定使用。這是為了一致性和正確性。
將指針作為上下文的管理器:
with connection.cursor() as c:
c.execute(...)
相當于:
c = connection.cursor()
try:
c.execute(...)
finally:
c.close()
以給定名稱調(diào)用數(shù)據(jù)庫存儲流程。要提供一個序列 (?params
?) 或字典 (?kparams
?) 作為輸入?yún)?shù)。大多數(shù)數(shù)據(jù)庫不支持 ?kparams
?。對于 Django 內(nèi)置后端來說,只有 Oracle 支持。
例如,在一個 Oracle 數(shù)據(jù)庫中指定存儲流程:
CREATE PROCEDURE "TEST_PROCEDURE"(v_i INTEGER, v_text NVARCHAR2(10)) AS
p_i INTEGER;
p_text NVARCHAR2(10);
BEGIN
p_i := v_i;
p_text := v_text;
...
END;
這將調(diào)用該存儲流程:
with connection.cursor() as cursor:
cursor.callproc('test_procedure', [1, 'test'])
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: