在模型查询API不够用的状况下,你可使用原始的SQL语句。Django 提供两种方法使用原始SQL进行查询:一种是使用Manager.raw()方法,进行原始查询并返回模型实例;另外一种是彻底避开模型层,直接执行自定义的SQL语句。mysql
警告sql
编写原始的SQL语句时,应该格外当心。 每次使用的时候,都要确保转义了参数中任何用户能够控制的字符,以防受到SQL注入攻击。数据库
raw() 管理器方法用于原始的SQL查询,并返回模型的实例:django
Manager.raw(raw_query, params=None, translations=None)后端
这个方法执行原始的SQL查询,并返回一个django.db.models.query.RawQuerySet 实例。这个RawQuerySet 实例能够像通常的查询集那样,经过迭代来提供对象实例。缓存
这里最好经过例子展现一下, 假设存在如下模型:app
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() 拥有其它更强大的使用方法。性能
模型表的名称fetch
在上面的例子中,Person表的名称是从哪里获得的?
一般,Django经过将模型的名称和模型的“应用标签”(你在manage.py startapp中使用的名称)进行关联,用一条下划线链接他们,来组合表的名称。在这里咱们假定Person模型存在于一个叫作myapp的应用中,因此表就应该叫作myapp_person。
更多细节请查看db_table选项的文档,它也可让你自定义表的名称。
警告
传递给 .raw()方法的sql语句并无任何检查。django默认它会返回一个数据集,但这不是强制性的。 若是查询的结果不是数据集,则会产生一个错误。
警告
若是你在mysql上执行查询,注意在类型不一致的时候,mysql的静默类型强制可能致使意想不到的结果发生。 若是你在一个字符串类型的列上查询一个整数类型的值,mysql会在比较前强制把每一个值的类型转成整数。例如,若是你的表中包含值'abc'和'def',你查询WHERE mycolumn=0,那么两行都会匹配。要防止这种状况,在查询中使用值以前,要作好正确的类型转换。
警告
虽然RawQuerySet能够像普通的QuerySet同样迭代,RawQuerySet并无实现能够在 QuerySet上使用的全部方法。例如,__bool__()和__len__()在RawQuerySet中没有被定义,因此全部RawQuerySet转化为布尔值的结果都是True。RawQuerySet中没有实现他们的缘由是,在没有内部缓存的状况下会致使性能降低,并且增长内部缓存不向后兼容。
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') ...
Django会根据名字进行匹配。 这意味着你可使用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对象是一个延迟的模型实例。这意味着被省略的字段,在访问时才被加载。例如:
>>> 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。然而,这个例子实际上执行了3次查询。 只有first_name字段在raw()查询中获取,last_name字符在执行打印命令时才被获取。
只有一种字段不能够被省略——就是主键。 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占位符,或者对于字典使用%(key)s占位符(key替换成字典中相应的key值),不管你的数据库引擎是什么。这些占位符将用params 参数中的值替换。
注意
SQLite后端不支持字典,你必须以列表的形式传递参数。
警告
不要在原始查询中使用字符串格式化!
它相似于这种样子:
>>> query = 'SELECT * FROM myapp_person WHERE last_name = %s' % lname >>> Person.objects.raw(query)
千万不要。
使用params参数能够彻底防止SQL注入攻击,它是一种广泛的漏洞,使攻击者能够向你的数据库中注入任何SQL语句。若是你使用字符串格式化,迟早会受到SQL注入攻击。 只要你记住默认使用 params 参数,就能够免于攻击。
有时Manager.raw()方法并不十分好用,你不须要将查询结果映射成模型,或者你须要执行UPDATE、 INSERT以及DELETE查询。
在这些状况下,你能够直接访问数据库,彻底避开模型层。
django.db.connection对象提供了常规数据库链接的方式。为了使用数据库链接,先要调用connection.cursor()方法来获取一个游标对象以后,调用cursor.execute(sql, [params])来执行sql语句,调用cursor.fetchone()或者cursor.fetchall()来返回结果行。
例如:
from django.db import connection def my_custom_sql(self): cursor = connection.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
注意若是你的查询中包含百分号字符,你须要写成两个百分号字符,以便能正确传递参数:
cursor.execute("SELECT foo FROM bar WHERE baz = '30%'") cursor.execute("SELECT foo FROM bar WHERE baz = '30%%' AND id = %s", [self.id])
若是你使用了不止一个数据库,你可使用django.db.connections来获取针对特定数据库的链接(以及游标)对象。django.db.connections是一个相似于字典的对象,容许你经过它的别名获取特定的链接。
from django.db import connections cursor = connections['my_db_alias'].cursor() # Your code here...
默认状况下,Python DB API会返回不带字段的结果,这意味着你获得的是一个列表,而不是一个字典。花费一点性能代价以后,你能够返回一个字典形式的结果,像这样:
def dictfetchall(cursor): "Returns all rows from a cursor as a dict" desc = cursor.description return [ dict(zip([col[0] for col in desc], row)) for row in cursor.fetchall() ]
下面是一个体现两者区别的例子:
>>> cursor.execute("SELECT id, parent_id FROM test LIMIT 2"); >>> cursor.fetchall() ((54360982L, None), (54360880L, None)) >>> cursor.execute("SELECT id, parent_id FROM test LIMIT 2"); >>> dictfetchall(cursor) [{'parent_id': None, 'id': 54360982L}, {'parent_id': None, 'id': 54360880L}]
链接和游标主要实现 PEP 249中描述的Python DB API标准——除非它涉及到事务处理。
若是你不熟悉Python DB-API,注意cursor.execute()中的SQL语句使用占位符"%s",而不是直接在SQL中添加参数。若是你使用这种方法,底层数据库的库会在必要时自动转义你的参数。
还要注意Django 使用"%s"占位符,而不是 SQLite Python 使用的"?"占位符。这是一致性和可用性的缘故。
Changed in Django 1.7.
PEP 249并无说明游标是否能够做为上下文管理器使用。在Python 2.7以前,因为魔术方法查询(Python ticket #9220)中的一个意想不到的行为,游标能够用做上下文管理器。Django 1.7 明确添加容许使用游标做为上下文管理器的支持。
将游标做为上下文管理器使用:
with connection.cursor() as c: c.execute(...)
等价于:
c = connection.cursor() try: c.execute(...) finally: c.close()