Django文件閱讀之執行原始SQL查詢

2022-07-14 23:15:18 字數 4619 閱讀 8923

django提供了兩種執行原始sql查詢的方法:可以使用manager.raw()來執行原始查詢並返回模型例項,或者可以完全避免模型層直接執行自定義sql。

每次編寫原始sql時都要關注防止sql注入

一、raw()方法

raw()方法可以用來執行返回模型例項原始的sql查詢:

manager.raw

raw_query

,params = none

,translations = none

)此方法接受原始sql查詢,執行它並返回django.db.models.query.rawqueryset例項。rawqueryset可以像正常一樣迭代

此例項queryset以提供物件例項。

raw()自動將查詢中的字段對映到模型上的字段。

查詢中字段的順序無關緊要。

換句話說,以下兩個查詢的工作方式相同:

......

匹配是通過名稱完成的。

這意味著您可以使用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''')

只要名稱匹配,就會正確建立模型例項。

或者,您可以使用translations引數to 

將查詢中的字段對映到模型字段 

raw()

這是乙個字典,將查詢中字段的名稱對映到模型上字段的名稱。

例如,上面的查詢也可以寫成:

>>> name_map = 

>>> person.objects.raw('select * from some_other_table', translations=name_map)

raw()支援索引,因此如果您只需要第乙個結果,您可以編寫:

但是,索引和切片不在資料庫級別執行。

如果person資料庫中

有大量物件,則在sql級別限制查詢會更有效:

django使用主鍵來標識模型例項,因此它必須始終包含在原始查詢中。一invalidquery,如果你忘了,包括主鍵,將引發異常。

如果需要執行引數化查詢,可以使用以下params引數raw()

>>> lname = 'doe'

params是引數的列表或字典。

無論您的資料庫引擎如何,

您都將%s在查詢字串中

使用佔位符作為列表,或者

%(key)s使用字典的佔位符(當然,這key將替換為字典鍵)。

這些佔位符將替換為引數中的

params引數。

不要在原始查詢上使用字串格式或在sql字串中引用佔位符!

將上述查詢編寫為:

>>> person.objects.raw(query)

您可能還認為應該像這樣編寫查詢(帶引號%s):

不要犯這些錯誤。

使用params引數並保留佔位符不加引號可以保護您免受

sql注入攻擊

,這是攻擊者將任意sql注入資料庫的常見漏洞。

如果使用字串插值或引用佔位符,則存在sql注入的風險。

該物件django.db.connection表示預設資料庫連線。

要使用資料庫連線,請呼叫

connection.cursor()以獲取游標物件。

然後,呼叫

執行sql和

或返回結果行。

cursor.execute(sql,[params])

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注入,不得%s在sql字串中

的佔位符

周圍包含引號

。請注意,如果要在查詢中包含文字百分號,則必須在傳遞引數的情況下將它們加倍:

cursor.execute("select foo from bar where baz = '30%'")

cursor.execute("select foo from bar where baz = '30%%' and id = %s", [self.id])

預設情況下,python db api將返回沒有欄位名稱的結果,這意味著您最終會得到乙個list值,而不是乙個dict

在較小的效能和記憶體成本下,您可以使用以下內容返回結果

dict

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()

]

另一種選擇是使用collections.namedtuple()python標準庫。

anamedtuple是乙個類似元組的物件,其字段可通過屬性查詢訪問; 

它也是可索引和可​​迭代的。

結果是不可變的,可以通過欄位名稱或索引訪問,這可能很有用:

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)

[, ]

>>> 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

django執行原始查詢sql,並返回Dict字典

很多時候執行sql語句,資料比django的model來的快,但並不想關心返回的字段,例如你可以執行 select from product這種sql,這裡個方法將會返回與資料庫列名相同的鍵值對 格式是這樣子的 result 當然你還可以 import json json.dumps result ...

openstack 官方文件閱讀之cells(二)

首次安裝 由於只有乙個api資料庫,因此它的連線資訊儲存在nova.conf檔案中。api database connection mysql pymysql root secretmysql dbserver nova api?charset utf8 由於可能存在多個 cell 資料庫 實際上每...

Pytorch閱讀文件之flatten函式

展平乙個連續範圍的維度,輸出型別為tensor torch.flatten input start dim 0,end dim 1 tensor parameters input tensor 輸入為tensor start dim int 展平的開始維度 end dim int 展平的最後維度 e...