为啥这个 Django 原始 SQL 查询不返回输出?
Posted
技术标签:
【中文标题】为啥这个 Django 原始 SQL 查询不返回输出?【英文标题】:Why is this Django raw SQL query not returning output?为什么这个 Django 原始 SQL 查询不返回输出? 【发布时间】:2019-08-17 00:22:36 【问题描述】:我尝试在 Django 中运行一个原始 SQL 查询。当我显示 RawQuerySet 对象时,它显示了正确的查询,但没有返回任何输出。
我已尝试将参数转换为字符串,并尝试将引号附加到参数,但没有奏效。
我也尝试过相同的查询,但我对参数进行了硬编码。那行得通。
我也打开了 dbshell 来尝试查看查询是否返回输出。它也很好用。
这是我在我的 dbshell 中运行的:
select id FROM recommender_item WHERE
id in (select item_id from
recommender_item_likes where user_id = 1)
and color = 'Black';
请注意,以下查询无效:
select id FROM recommender_item WHERE
id in (select item_id from
recommender_item_likes where user_id = 1)
and color = Black;
这是我要运行的实际查询:
Item.objects.raw('select id FROM recommender_item WHERE
id in (select item_id from recommender_item_likes where
user_id = %s) and %s = %s', [request.user.id, user_pref, pref_choice,])
这是与硬编码参数相同的查询:
Item.objects.raw('select id FROM recommender_item WHERE
id in (select item_id from recommender_item_likes where user_id = %s)
and color = "Black"', [request.user.id])
我的模板中的输出应该只是这个 id 列表: 1、64、437、1507、1685
但是,现在它只返回 []
这分别是两种情况下的 RawQuerySet 对象:
<RawQuerySet: select id FROM recommender_item WHERE
id in (select item_id from recommender_item_likes where user_id = 1)
and color = Black>
和
<RawQuerySet: select id FROM recommender_item WHERE
id in (select item_id from recommender_item_likes where user_id = 1)
and color = "Black">
正在执行的实际 SQL 查询,从 Django 调试工具栏中检索:
select id FROM recommender_item WHERE
id in (select item_id from recommender_item_likes where
user_id = '1') and '''color''' = '''"Black"'''
models.py
class Item(models.Model):
#id = models.UUIDField(primary_key = True, default = uuid.uuid4, help_text = 'Unique ID for this particular item')
item_type = models.CharField(max_length = 200, null = True, blank = True)
price = models.CharField(max_length = 200, null = True, blank = True)
color = models.CharField(max_length = 200, null = True, blank = True)
image_URL = models.CharField(max_length = 1000, null = True, blank = True)
fit = models.CharField(max_length = 200, null = True, blank = True)
occasion = models.CharField(max_length = 200, null = True, blank = True)
brand = models.CharField(max_length = 200, null = True, blank = True)
pattern = models.CharField(max_length = 200, null = True, blank = True)
fabric = models.CharField(max_length = 200, null = True, blank = True)
length = models.CharField(max_length = 200, null = True, blank = True)
likes = models.ManyToManyField(User, blank = True, related_name = 'item_likes')
【问题讨论】:
如果你格式化你的代码行以防止水平滚动,它会更容易提供帮助。 对不起!编辑代码使其更易于阅读 我认为RawQuerySet.__repr__
的输出具有误导性。如果您检查实际的查询(使用django.db.connection.queries
),您会发现与 RawQuerySet 输出相反,在发送到数据库的 SQL 中引用了字符串参数。不过,不确定这对您来说意味着什么;看来,如果您的硬编码查询有效,那么参数化查询也应该有效。
所以我发现实际查询是通过 django 调试工具栏执行的。是这样的:select id FROM recommender_item WHERE id in (select item_id from recommender_item_likes where user_id = '1') and '''color''' = '''"Black"'''
我不知道这是否会有所帮助。
我现在已经尝试过使用 Sqlite,它工作得很好,字符串参数被单引号引起来,例如WHERE color = 'black'
(尽管 DDT 显示三重引号而不是单引号)。我正在使用 Django 2.1.7;也许早期版本存在问题。你的版本是什么?
【参考方案1】:
这个查询应该得到用户喜欢的所有黑色物品:
Item.objects.filter(likes=request.user, color='Black')
如果您只需要原始查询中的 id,请添加 .values('id')
。
但我仍然觉得你原来的问题更有趣。我向 Postgresql 发出带有字符串参数的原始查询没有问题。我将不得不尝试使用 Sqlite。
顺便说一句,ORM 查询强调 likes
是用词不当; likers
或类似名称似乎更合适。
您可以在filter()
中展开字典:
filter_field = 'color'
filter_string = 'black'
filter_dict = filter_field: filter_string
Item.objects.filter(**filter_dict)
【讨论】:
我会将字段名称更新为likers
。这说得通。不过,您将如何提供 color
和 'Black'
作为变量?
这是否意味着我也可以这样做:filter_field = variable_that_holds_color
和 filter_string
一样?
Python 不关心变量的名称。也可以直接运行Item.objects.filter(**variable_that_holds_field_name: variable_that_holds_color)
。
谢谢!这种方法有效,而且绝对比我的原始 SQL 查询更健壮。【参考方案2】:
好的,在玩了很多 shell 和调试工具栏之后,我找到了一种有点多余的方法来实现这一点。创建六个单独的字符串:
str1 = 'select id FROM recommender_item WHERE id in (select item_id from recommender_item_likes where user_id ='
str2 = str(request.user.id)
str3 = ') and '
str4 = user_pref
str5 = ' = '
str6 = "'"+pref_choice+"'"
q = str1 + str2+ str3 + str4 + str5 + str6
然后我这样传递这个变量:
Item.objects.raw(q)
这给了我所需的输出。
鉴于模型发生变化,您应该能够做到这一点:
Item.objects.filter(likes=request.user)
或者
request.user.item_likes.all()
【讨论】:
虽然我没有您想要的答案,但这并不是最好的方法。您应该可以使用f'select ... str(request.user.id)) and user_pref ...'
来避免创建多个字符串。你为什么要尝试做原始的sql?查询看起来很简单,您应该可以使用 ORM 来完成。
是的,这绝对不是最好的方法。我在使用 ORM 构建相同的动态查询时遇到了麻烦。无法弄清楚如何为我的字段名称使用变量。
如果您发布模型,我可以提供帮助。
@schillingt 在问题中添加了 models.py。
@schillingt 我没有尝试 fstrings。值得一试。以上是关于为啥这个 Django 原始 SQL 查询不返回输出?的主要内容,如果未能解决你的问题,请参考以下文章
为啥通过 django QuerySet 进行查询比在 Django 中使用游标慢得多?