Django4.0 執(zhí)行原生SQL查詢-執(zhí)行原生查詢

2022-03-16 18:03 更新

若管理器方法 ?raw()? 能用于執(zhí)行原生 SQL 查詢,就會返回模型實例:

?Manager.raw(raw_query, params=(), translations=None)?

該方法接受一個原生 SQL 查詢語句,執(zhí)行它,并返回一個 ?django.db.models.query.RawQuerySet? 實例。這個 ?RawQuerySet ?能像普通的 ?QuerySet ?一樣被迭代獲取對象實例。

最好用例子來解釋。假設你有以下模型:

class Person(models.Model):
    first_name = models.CharField(...)
    last_name = models.CharField(...)
    birth_date = models.DateField(...)

然后你可以像這樣執(zhí)行自定義 SQL:

>>> for p in Person.objects.raw('SELECT * FROM myapp_person'):
...     print(p)
John Smith
Jane Jones

將查詢字段映射為模型字段

?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''')

只要名字對上了,模型實例就會被正確創(chuàng)建。
或者,你可以用 ?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 層面使用 limit 子句:

>>> first_person = Person.objects.raw('SELECT * FROM myapp_person LIMIT 1')[0]

延遲模型字段

也可以省略字段:

>>> people = Person.objects.raw('SELECT id, first_name FROM myapp_person')

該查詢返回的 Person 對象即延遲模型實例。這意味著查詢語句中省略的字段按需加載。例子:

>>> 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
...           p.last_name) # This will be retrieved on demand
...
John Smith
Jane Jones

表面上,看起來該查詢同時檢出了 ?first name? 和 ?last name?。然而,這個例子實際上執(zhí)行了三次查詢。只有 ?first names? 是由 ?raw()? 查詢檢出的 —— ?last names? 是在它們被打印時按需檢出。
只有一個字段你不能省略 —— 主鍵字段。Django 用主鍵來區(qū)分模型實例,所以必須在原生查詢語句中包含主鍵。若你忘了包含主鍵會拋出 ?FieldDoesNotExist? 異常。

添加注釋

你可以執(zhí)行帶有模型中未定義字段的查詢語句。例如,我們能用 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()

如果你需要執(zhí)行參數化的查詢,可以使用 ?raw()? 的 ?params ?參數:

>>> lname = 'Doe'
>>> Person.objects.raw('SELECT * FROM myapp_person WHERE last_name = %s', [lname])

?params ?是一個參數字典。你將用一個列表替換查詢字符串中 ?%s? 占位符,或用字典替換 ?%(key)s? 占位符(?key ?被字典 ?key ?替換),不論你使用哪個數據庫引擎。這些占位符會被 ?params ?參數的值替換。

不要對原生查詢或 SQL 字符串中的引號占位符使用字符串格式化!

臨時將上述查詢寫作:

>>> query = 'SELECT * FROM myapp_person WHERE last_name = %s' % lname
>>> Person.objects.raw(query)

你可能認為你需要將查詢寫成這樣(用單引號包裹 %s):

>>> query = "SELECT * FROM myapp_person WHERE last_name = '%s'"


以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號