Django 提供了两种形式来执行原生 SQL 代码。

一种是应用 raw() 函数,一种是 应用 connection.cursor()。

然而官网还是举荐在应用原生 SQL 之前,尽量的先去摸索一下 QuerySet 提供的各种 API。

目前而言,官网文档提供的各种 API 是可能满足绝大多数的利用场景的。

以下是本篇笔记的目录:

  1. raw()
  2. connection.cursor()
  3. 多数据库操作

1、raw()

这个办法能够用来操作原生 SQL,而后返回 model 实例:

咱们以 Blog 作为示例,应用的代码如下:

for blog in Blog.objects.raw("select * from blog_blog"):    print(blog)

下面这段代码的作用成果跟 Blog.objects.all() 获取的后果是一样的,然而在一些操作上又不太一样,比方 all().count() 能够获取到 总数,然而 raw() 就不能进行这种操作。

须要留神的是,raw() 并不会去检测输出的 SQL 代码,即便咱们应用 Blog 这个model 来查问 Entry 的数据,也可能返回后果,不过返回的都是 Entry 表的属性:

for blog in Blog.objects.raw("select * from blog_entry"):    print(blog.__dict__)  # __dict__ 输入的都是 blog_entry 的字段

也就是说 在 Blog.objects.raw() 中,真正起作用的只有 raw() 这一个函数,后面的 Blog 不过是一个架子,或者路径,是为了引出 raw() 这个函数。

自定义返回字段

在 raw() 函数的 SQL 代码里,咱们能够自定义选中的字段,如果须要应用没有选中的字段,那么零碎会再次拜访数据库来获取,这个操作过程就跟后面介绍的 defer() 函数一样。

for item in Blog.objects.raw("select id from blog_blog"):    print(item.__dict__)    print(item.id, item.name)    print(item.__dict__){'_state': <django.db.models.base.ModelState object at 0x7fbd4165b6a0>, 'id': 2}2 hunter{'_state': <django.db.models.base.ModelState object at 0x7fbd4165b6a0>, 'id': 2, 'name': 'hunter'}

能够看到,返回的后果中先输入的数据只有 id,前面,当咱们拜访 name 字段的时候,又去获取了 name 字段的数据。

自定义字段必须蕴含主键

当咱们自定义字段返回的时候,必须是要蕴含主键字段的,否则在咱们获取信息的时候会报错,比方上面的操作:

for blog in Blog.objects.raw("select name from blog_blog"):    print(blog.__dict__)

在 print(blog.__dict__) 的时候就会报错,数据中没有主键信息

自定义返回新字段

能够跟 QuerySet 的 annotate 操作一样,自定义新字段返回,获取的时候能够间接依据属性值返回,比方:

entry = Entry.objects.raw("select *, date_format(pub_date, '%%Y-%%m') as date_1 from blog_entry")[0]print(entry.date_1)

传递变量

给输出的 SQL 语句传递变量:

name = "python"Blog.objects.raw("select * from blog_blog where name = '%s'", [name])

2、connection.cursor()

Django 推出了一种更加间接执行 SQL 的形式,用到的模块是 django.db.connction,用到的 cursor 和 pymysql 的库是一样的用法,官网给出的示例如下:

from django.db import connectiondef 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 中,须要对一些符号进行本义之后才能够应用,比方:

cursor.execute("SELECT foo FROM bar WHERE baz = '30%'")cursor.execute("SELECT foo FROM bar WHERE baz = '30%%' AND id = %s", [self.id])

其中,第二条语句的 % 须要进行本义

适配fetch的数据

通过 fetchone 和 fetchall() 返回的数据是只有 value 值的,没有对应的字段 key,如果能够适当的就义性能和内存,来换取获取数据的便当和准确性,官网提供了这样一种形式:

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

在咱们执行完 cursor.execute(sql) 之后,把 cursor 作为参数传入 dictfetchall 函数中,就能够返回一个字典列表

介绍一下 cursor.description,这个返回的是一个元组数据,外面的元素也是一个元组,元组的第一个元素就是咱们 select 的字段名。

所以 columns = [col[0] for col in cursor.description] 这一行代码获取的是指定的所有字段名

应用示例:

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()    ]with connection.cursor() as cursor:    sql = "select id, name from blog_blog"    cursor.execute(sql)    results = dictfetchall(cursor)print(results)# 返回的后果:# [{'id': 20, 'name': 'name_3'}, {'id': 21, 'name': 'name_4'}, {'id': 1, 'name': 'name_5'}]

在我应用的过程中,咱们应用的是上下文管理器的形式来获取游标:

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

所以,应用完之后,不须要手动敞开,他与上面的用法成果是统一的:

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

但举荐的还是上下文管理器的形式,更优雅。

3、多数据库操作

如果零碎用到了多个数据库,那么在应用 cursor 的时候,就须要应用到 django.db.connections 模块:

from django.db import connectionswith connections['my_db_alias'].cursor() as cursor:    pass

以上就是本篇笔记全部内容,下一篇笔记将简略介绍一下多数据库的操作。

本文首发于自己微信公众号:Django笔记。

原文链接:Django笔记二十一之应用原生SQL查询数据库

如果想获取更多相干文章,可扫码关注浏览: