Django extra + where:如何转义标识符
Posted
技术标签:
【中文标题】Django extra + where:如何转义标识符【英文标题】:Django extra + where: how to escape identifiers 【发布时间】:2021-11-16 18:43:36 【问题描述】:我在 Django 中有一个带有 where
子句的 extra
过滤器,但表名是动态的。
filtered_queryset = queryset.extra(
where=[
f'(table_name.modified_on, table_name.id) > (%s, %s)',
],
params=(after_ts, after_id),
)
我怎样才能最好地避免 f-string 以确保它不对 SQL 注入开放?
我希望 SQL 具有元组比较,而不是多个 >
和 >=
与 AND
。从之前的测试来看,它似乎更有可能使用多列索引。
(这是一些自定义分页代码的一部分,其中光标本质上是一个日期时间和 id 的元组)
【问题讨论】:
根据文档,extra 已被弃用,他们希望在需要的情况下获得反馈:docs.djangoproject.com/en/3.2/ref/models/querysets/#extra 当然,您始终可以通过拥有有效表名列表并在其中查找表名来自行解决,但您可能不想这样做 【参考方案1】:为什么不把它写成过滤器呢?
filtered_queryset = queryset.filter(
Q(modified_on__gt=after_ts) |
Q(Q(modified_on__gte=after_ts) & Q(id__gt=after_id))
)
PS:有点不清楚您的查询要做什么,我认为是这样,但也许您想过滤其他内容。
【讨论】:
我在这个问题中添加了更多细节,说明我为什么要避免这种情况 我不知道如何强制 Django 使用某种 SQL,所以恐怕我无法进一步帮助您。 :-(【参考方案2】:首先,根据已知表名列表验证您的 table_name
变量。
另外,使用双引号作为标识符分隔符,这允许table_name
成为 SQL 保留关键字,或者包含空格或标点符号。只要使用标识符分隔符,这些在 SQL 中都是合法的。
f'("table_name".modified_on, "table_name".id) > (%s, %s)',
见https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
但是,如果您的表名包含文字 "
字符,这可能会导致麻烦,因为 "
符号将被解释为终止标识符分隔符。因此,您可以自行过滤table_name
,将文字"
替换为""
。
【讨论】:
【参考方案3】:与this answer 一样,extra
可以通过使用annotate
来避免,并且表名取自 Queryset 内部的模型,这样就不必担心转义任何标识符:
from django.db.models import F, Func, TextField
col_a_col_b = Func(F('col_a'), F('col_b'), function='ROW', output_type=TextField())
col_a_col_b_from = Func(col_a_value, col_b_value, function='ROW')
filtered_queryset = queryset
.annotate(col_a_col_b=col_a_col_b)
.filter(col_a_col_b__gt=col_a_col_b_from)
.order_by('col_a', 'col_b')
(显然在 Django 3.2+ 中可以使用alias
代替extra
,并且避免了output_field
hack)
【讨论】:
以上是关于Django extra + where:如何转义标识符的主要内容,如果未能解决你的问题,请参考以下文章