乐趣区

关于python:Django笔记二十一之使用原生SQL查询数据库

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 connection


def 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 connections
with connections['my_db_alias'].cursor() as cursor:
    pass

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

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

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

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

退出移动版