mirror of https://github.com/django/django.git
Fixed #9997 -- Fixed use of ValuesQuerySets as rvalues in filters.
Previous behaviour was pretty stupid. Let's never speak of it again. New behaviour both works and is documented. git-svn-id: http://code.djangoproject.com/svn/django/trunk@9759 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
14b3f03015
commit
d579e716fe
|
@ -798,6 +798,20 @@ class ValuesQuerySet(QuerySet):
|
||||||
|
|
||||||
super(ValuesQuerySet, self)._setup_aggregate_query()
|
super(ValuesQuerySet, self)._setup_aggregate_query()
|
||||||
|
|
||||||
|
def as_sql(self):
|
||||||
|
"""
|
||||||
|
For ValueQuerySet (and subclasses like ValuesListQuerySet), they can
|
||||||
|
only be used as nested queries if they're already set up to select only
|
||||||
|
a single field (in which case, that is the field column that is
|
||||||
|
returned). This differs from QuerySet.as_sql(), where the column to
|
||||||
|
select is set up by Django.
|
||||||
|
"""
|
||||||
|
if ((self._fields and len(self._fields) > 1) or
|
||||||
|
(not self._fields and len(self.model._meta.fields) > 1)):
|
||||||
|
raise TypeError('Cannot use a multi-field %s as a filter value.'
|
||||||
|
% self.__class__.__name__)
|
||||||
|
return self._clone().query.as_nested_sql()
|
||||||
|
|
||||||
class ValuesListQuerySet(ValuesQuerySet):
|
class ValuesListQuerySet(ValuesQuerySet):
|
||||||
def iterator(self):
|
def iterator(self):
|
||||||
self.query.trim_extra_select(self.extra_names)
|
self.query.trim_extra_select(self.extra_names)
|
||||||
|
|
|
@ -1136,6 +1136,7 @@ The above code fragment could also be written as follows::
|
||||||
inner_q = Blog.objects.filter(name__contains='Cheddar').values('pk').query
|
inner_q = Blog.objects.filter(name__contains='Cheddar').values('pk').query
|
||||||
entries = Entry.objects.filter(blog__in=inner_q)
|
entries = Entry.objects.filter(blog__in=inner_q)
|
||||||
|
|
||||||
|
|
||||||
.. versionchanged:: 1.1
|
.. versionchanged:: 1.1
|
||||||
In Django 1.0, only the latter piece of code is valid.
|
In Django 1.0, only the latter piece of code is valid.
|
||||||
|
|
||||||
|
@ -1144,6 +1145,21 @@ accesses the internal ``query`` attribute and requires a ``ValuesQuerySet``.
|
||||||
If your code doesn't require compatibility with Django 1.0, use the first
|
If your code doesn't require compatibility with Django 1.0, use the first
|
||||||
form, passing in a queryset directly.
|
form, passing in a queryset directly.
|
||||||
|
|
||||||
|
If you pass in a ``ValuesQuerySet`` or ``ValuesListQuerySet`` (the result of
|
||||||
|
calling ``values()`` or ``values_list()`` on a queryset) as the value to an
|
||||||
|
``__in`` lookup, you need to ensure you are only extracting one field in the
|
||||||
|
result. For example, this will work (filtering on the blog names)::
|
||||||
|
|
||||||
|
inner_qs = Blog.objects.filter(name__contains='Ch').values('name')
|
||||||
|
entries = Entry.objects.filter(blog__name__in=inner_qs)
|
||||||
|
|
||||||
|
This example will raise an exception, since the inner query is trying to
|
||||||
|
extract two field values, where only one is expected::
|
||||||
|
|
||||||
|
# Bad code! Will raise a TypeError.
|
||||||
|
inner_qs = Blog.objects.filter(name__contains='Ch').values('name', 'id')
|
||||||
|
entries = Entry.objects.filter(blog__name__in=inner_qs)
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
This ``query`` attribute should be considered an opaque internal attribute.
|
This ``query`` attribute should be considered an opaque internal attribute.
|
||||||
|
|
|
@ -1022,6 +1022,22 @@ nothing).
|
||||||
>>> print Annotation.objects.filter(notes__in=Note.objects.filter(note="xyzzy")).query
|
>>> print Annotation.objects.filter(notes__in=Note.objects.filter(note="xyzzy")).query
|
||||||
SELECT ...
|
SELECT ...
|
||||||
|
|
||||||
|
Bug #9997 -- If a ValuesList or Values queryset is passed as an inner query, we
|
||||||
|
make sure it's only requesting a single value and use that as the thing to
|
||||||
|
select.
|
||||||
|
>>> Tag.objects.filter(name__in=Tag.objects.filter(parent=t1).values('name'))
|
||||||
|
[<Tag: t2>, <Tag: t3>]
|
||||||
|
|
||||||
|
# Multi-valued values() and values_list() querysets should raise errors.
|
||||||
|
>>> Tag.objects.filter(name__in=Tag.objects.filter(parent=t1).values('name', 'id'))
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
TypeError: Cannot use a multi-field ValuesQuerySet as a filter value.
|
||||||
|
>>> Tag.objects.filter(name__in=Tag.objects.filter(parent=t1).values_list('name', 'id'))
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
TypeError: Cannot use a multi-field ValuesListQuerySet as a filter value.
|
||||||
|
|
||||||
Bug #9985 -- qs.values_list(...).values(...) combinations should work.
|
Bug #9985 -- qs.values_list(...).values(...) combinations should work.
|
||||||
>>> Note.objects.values_list("note", flat=True).values("id").order_by("id")
|
>>> Note.objects.values_list("note", flat=True).values("id").order_by("id")
|
||||||
[{'id': 1}, {'id': 2}, {'id': 3}]
|
[{'id': 1}, {'id': 2}, {'id': 3}]
|
||||||
|
|
Loading…
Reference in New Issue