Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。

交流群:467338606

网站:http://python.usyiyi.cn/django/index.html

进行原始的sql查询

模型查询API不够用的情况下,你可以使用原始的sql语句。django提供两种方法使用原始sql进行查询:一种是使用Manager.raw()方法,进行原始查询并返回模型实例;另一种是完全避开模型层,直接执行自定义的sql语句

警告

编写原始的sql语句时,应该格外小心。每次使用的时候,都要确保转义了参数中的任何控制字符,以防受到sql注入攻击。更多信息请参阅防止sql注入

进行原始查询

raw()方法用于原始的sql查询,并返回模型的实例:

Manager.raw(raw_query, params=None, 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

当然,这个例子不是特别有趣,和直接使用Person.objects.all()的结果一模一样。但是,raw()拥有其它更强大的使用方法。

模型表的名称

在上面的例子中,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转化为布尔值的结果都是TrueRawQuerySet中没有实现他们的原因是,在没有内部缓存的情况下会导致性能下降,而且增加内部缓存不向后兼容。

将查询字段映射到模型字段

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()方法中使用翻译参数。翻译参数是一个字典,将表中的字段名称映射为模型中的字段名称、例如,上面的查询可以写成这样:

>>> 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对象是一个延迟的模型实例(请见 defer())。这意味着被省略的字段,在访问时才被加载。例如:

>>> 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_namelast_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() 方法中传递参数

如果你需要参数化的查询,可以向raw() 方法传递params参数。

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

params是存放参数的列表或字典。你可以在查询语句中使用%s占位符,或者对于字典使用%(key)占位符(key会被替换成字典中键为key的值),无论你的数据库引擎是什么。这样的占位符会被替换成参数表中正确的参数。

注意

SQLite后端不支持字典,你必须以列表的形式传递参数。

警告

不要在原始查询中使用字符串格式化!

它类似于这种样子:

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

使用参数化查询可以完全防止sql注入,一种普遍的漏洞使攻击者可以向你的数据库中注入任何sql语句。如果你使用字符串格式化,早晚会受到sql输入的攻击。只要你记住默认使用参数化查询,就可以免于攻击。

直接执行自定义sql

有时Manager.raw()方法并不十分好用,你不需要将查询结果映射成模型,或者你需要执行UPDATEINSERT以及DELETE查询。

在这些情况下,你可以直接访问数据库,完全避开模型层。

django.db.connection对象提供了常规数据库连接的方式。为了使用数据库连接,调用connection.cursor()方法来获取一个游标对象之后,调用cursor.execute(sql, [params])来执行sql语句,调用cursor.fetchone()或者curser.fetchall()来返回结果行。

例如:

from django.db import connectiondef 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.descriptionreturn [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绑定的“?”占位符。这是一致性和可用性的缘故。

Django 1.7中的改变。

PEP 249并没有说明游标是否可以作为上下文管理器使用。在python2.7之前,游标可以用作上下文管理器,由于魔术方法lookups中意想不到的行为(Python ticket #9220)。Django 1.7 显式添加了对允许游标作为上下文管理器使用的支持。

将游标作为上下文管理器使用:

with connection.cursor() as c:c.execute(...)

等价于:

c = connection.cursor()
try:c.execute(...)
finally:c.close()

django 1.8 官方文档翻译: 2-5-2 进行原始的sql查询相关推荐

  1. django 1.8 官方文档翻译: 2-5-7 自定义查找

    自定义查找 New in Django 1.7. Django为过滤提供了大量的内建的查找(例如,exact和icontains).这篇文档阐述了如何编写自定义查找,以及如何修改现存查找的功能.关于查 ...

  2. django 1.8 官方文档翻译:2-1-1 模型语法

    模型 模型是你的数据的唯一的.权威的信息源.它包含你所储存数据的必要字段和行为.通常,每个模型对应数据库中唯一的一张表. 基础: 每个模型都是django.db.models.Model 的一个Pyt ...

  3. django 1.8 官方文档翻译:13-3 日志

    日志 日志快速入门 Django 使用Python 内建的logging 模块打印日志.该模块的用法在Python 本身的文档中有详细的讨论.如果你从来没有使用过Python 的logging 框架( ...

  4. django 1.8 官方文档翻译: 2-6-4 数据库访问优化

    Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质. 交流群:467338606 网站:http://python.usyiyi.cn/django/index.html ...

  5. django 1.8 官方文档翻译: 2-1-1 模型语法(初稿)

    Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质. 交流群:467338606 网站:http://python.usyiyi.cn/django/index.html ...

  6. django 1.8 官方文档翻译: 1-2-1 编写你的第一个Django应用,第1部分

    编写你的第一个 Django 程序 第1部分 让我们通过例子来学习. 在本教程中,我们将引导您创建一个基本的投票应用. 它将包含两部分: 一个公共网站,可让人们查看投票的结果和让他们进行投票. 一个管 ...

  7. django 1.8 官方文档翻译: 2-5-6 多数据库

    多数据库 这篇主题描述Django 对多个数据库的支持.大部分Django 文档假设你只和一个数据库打交道.如果你想与多个数据库打交道,你将需要一些额外的步骤. 定义你的数据库 在Django中使用多 ...

  8. django 1.8 官方文档翻译: 1-1-2 快速安装指南

    快速安装指南 在你开始使用 Django 之前,你需要先安装它.我们有一个 完整安装指南 它涵盖了所有的安装步骤和可能遇到的问题:本指南将会给你一个最简单.简洁的安装指引. 安装 Python 作为一 ...

  9. django 1.8 官方文档翻译: 3-4-2 基于类的内建通用视图

    基于类的内建通用视图 编写Web应用可能是单调的,因为你需要不断的重复某一种模式. Django尝试从model和 template层移除一些单调的情况,但是Web开发者依然会在view(视图)层经历 ...

最新文章

  1. 全栈工程师15年经验分享:40个改变编程技能的小技巧
  2. 头脑风暴-如何减少软件项目对于人的依赖性。
  3. linux mint 19 与windows时间不同步
  4. 【Flutter】手机应用类型 ( Android | iOS | Native 应用 | Web 应用 | Hybrid 应用 | ReactNative 应用 | Flutter 应用 )
  5. 关于成为一名优秀的软件测试工程师
  6. java有var吗_java – Var和Var之间的区别
  7. android 上下偏差怎么写_详解 Android 热更新升级如何突破底层结构差异?
  8. python 定义变量_第三章(第2节):变量和常量
  9. 仿手环运动app的html,连接手环.html
  10. Atitit 互操作之道 接口之道 attilax著
  11. Parallels Toolbox for mac(万能工具箱)中文版
  12. Python自动化办公:xlwt万字教程
  13. CIE 国际照明委员会
  14. 协同感知综述:从异质单体到分层合作
  15. ElasticSearch使用学习
  16. Camera | 4.瑞芯微平台MIPI摄像头应用程序编写
  17. 网络安全就业前景怎么样?好找工作吗?
  18. 如何将数据从一台主机发送到另一台主机上(详解)
  19. Azure Key Vault 简介
  20. ALSA-ASOC音频驱动框架简述

热门文章

  1. 114实名认证未通过_企业微信怎么实名认证?实名认证后还可以改吗?
  2. c语言欺凌,以下哪种行为属于“校园欺凌”?A取绰号B暴力殴打同学C恶意辱骂D企图教唆集体...
  3. libevent源码深度剖析九
  4. linux下异步IO的简单例子
  5. Redis的过期键删除策略和内存淘汰机制
  6. C语言表达式的求解规则,C语言实现整数四则运算表达式的计算
  7. 【Spring】模块
  8. python 用selenium自动启动百度并搜索关键词
  9. Xamarin.Android 隐藏软键盘
  10. 站内搜索引擎初探:haystack全文检索,whoosh搜索引擎,jieba中文分词