• Django文档阅读之执行原始SQL查询


    Django提供了两种执行原始SQL查询的方法:可以使用Manager.raw()来执行原始查询并返回模型实例,或者可以完全避免模型层直接执行自定义SQL。

    每次编写原始SQL时都要关注防止SQL注入

    一、raw()方法

    raw()方法可以用来执行返回模型实例原始的SQL查询:

    Manager.rawraw_queryparams = Nonetranslations = None

    此方法接受原始SQL查询,执行它并返回 django.db.models.query.RawQuerySet实例。RawQuerySet可以像正常一样迭代实例 QuerySet以提供对象实例。

    Person.objects.raw('SELECT * FROM myapp_person')

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

    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''')
    

    只要名称匹配,就会正确创建模型实例。

    或者,您可以使用translations参数to 将查询中的字段映射到模型字段 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]
    Django使用主键来标识模型实例,因此它必须始终包含在原始查询中。一InvalidQuery,如果你忘了,包括主键,将引发异常。

    将参数传递给raw()

    如果需要执行参数化查询,可以使用以下params 参数raw()

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

    params是参数的列表或字典。无论您的数据库引擎如何,您都将%s 在查询字符串中使用占位符作为列表,或者%(key)s 使用字典的占位符(当然,这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'"

    不要犯这些错误。

    使用params 参数并保留占位符不加引号可以保护您免受SQL注入攻击,这是攻击者将任意SQL注入数据库的常见漏洞。如果使用字符串插值或引用占位符,则存在SQL注入的风险。

    直接执行自定义

    该对象django.db.connection表示默认数据库连接。要使用数据库连接,请调用connection.cursor()以获取游标对象。然后,调用执行SQL和返回结果行。

    cursor.execute(sql,[params])

    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注入,不得%s 在SQL字符串中占位符周围包含引号

    请注意,如果要在查询中包含文字百分号,则必须在传递参数的情况下将它们加倍:

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

    默认情况下,Python DB API将返回没有字段名称的结果,这意味着您最终会得到一个list值,而不是一个dict在较小的性能和内存成本下,您可以使用以下内容返回结果dict

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

    另一种选择是使用collections.namedtuple()Python标准库。namedtuple是一个类似元组的对象,其字段可通过属性查找访问; 它也是可索引和可​​迭代的。结果是不可变的,可以通过字段名称或索引访问,这可能很有用:

    from collections import namedtuple
    
    def namedtuplefetchall(cursor):
        "Return all rows from a cursor as a namedtuple"
        desc = cursor.description
        nt_result = namedtuple('Result', [col[0] for col in desc])
        return [nt_result(*row) for row in cursor.fetchall()]

    以下是三者之间差异的一个例子:

    >>> cursor.execute("SELECT id, parent_id FROM test LIMIT 2");
    >>> cursor.fetchall()
    ((54360982, None), (54360880, None))
    
    >>> cursor.execute("SELECT id, parent_id FROM test LIMIT 2");
    >>> dictfetchall(cursor)
    [{'parent_id': None, 'id': 54360982}, {'parent_id': None, 'id': 54360880}]
    
    >>> cursor.execute("SELECT id, parent_id FROM test LIMIT 2");
    >>> results = namedtuplefetchall(cursor)
    >>> results
    [Result(id=54360982, parent_id=None), Result(id=54360880, parent_id=None)]
    >>> results[0].id
    54360982
    >>> results[0][0]
    54360982
     
  • 相关阅读:
    动态路由--RIP
    子网划分
    VLAN划分
    OSI七层参考模型
    Linux(CentOS 7)配置国内yum源和epel源
    PXE网络装机之centos7(批量自动装机)
    lvs+keepalived高可用集群
    SSH远程登录
    用canvas画一个房子
    使用json改写网站
  • 原文地址:https://www.cnblogs.com/roygood/p/10124509.html
Copyright © 2020-2023  润新知