Django4.0 执行原生SQL查询-执行原生查询
若管理器方法 raw()
能用于执行原生 SQL 查询,就会返回模型实例:
Manager.raw(raw_query, params=(), translations=None)
该方法接受一个原生 SQL 查询语句,执行它,并返回一个 django.db.models.query.RawQuerySet
实例。这个 RawQuerySet
能像普通的 QuerySet
一样被迭代获取对象实例。
最好用例子来解释。假设你有以下模型:
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
将查询字段映射为模型字段
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 层面使用 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
。然而,这个例子实际上执行了三次查询。只有 first names
是由 raw()
查询检出的 —— last names
是在它们被打印时按需检出。
只有一个字段你不能省略 —— 主键字段。Django 用主键来区分模型实例,所以必须在原生查询语句中包含主键。若你忘了包含主键会抛出 FieldDoesNotExist
异常。
添加注释
你可以执行带有模型中未定义字段的查询语句。例如,我们能用 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()
如果你需要执行参数化的查询,可以使用 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'"