Commit 0e16c3e3 authored by Gabe Jackson's avatar Gabe Jackson Committed by Tim Graham
Browse files

Fixed #23396 -- Ensured ValueQuerySets are not checked by check_related_objects.

parent 3a34e45f
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -1052,6 +1052,15 @@ class QuerySet(object):
        """
        return self.query.has_filters()

    def is_compatible_query_object_type(self, opts):
        model = self.model
        return (
            model == opts.concrete_model or
            opts.concrete_model in model._meta.get_parent_list() or
            model in opts.get_parent_list()
        )
    is_compatible_query_object_type.queryset_only = True


class InstanceCheckMeta(type):
    def __instancecheck__(self, instance):
@@ -1209,6 +1218,13 @@ class ValuesQuerySet(QuerySet):
                    % self.__class__.__name__)
        return self

    def is_compatible_query_object_type(self, opts):
        """
        ValueQuerySets do not need to be checked for compatibility.
        We trust that users of ValueQuerySets know what they are doing.
        """
        return True


class ValuesListQuerySet(ValuesQuerySet):
    def iterator(self):
+11 −15
Original line number Diff line number Diff line
@@ -1094,19 +1094,15 @@ class Query(object):
        Checks the type of object passed to query relations.
        """
        if field.rel:
            # testing for iterable of models
            if hasattr(value, '__iter__'):
                # Check if the iterable has a model attribute, if so
                # it is likely something like a QuerySet.
                if hasattr(value, 'model') and hasattr(value.model, '_meta'):
                    model = value.model
                    if not (model == opts.concrete_model
                            or opts.concrete_model in model._meta.get_parent_list()
                            or model in opts.get_parent_list()):
            # QuerySets implement is_compatible_query_object_type() to
            # determine compatibility with the given field.
            if hasattr(value, 'is_compatible_query_object_type'):
                if not value.is_compatible_query_object_type(opts):
                    raise ValueError(
                        'Cannot use QuerySet for "%s": Use a QuerySet for "%s".' %
                            (model._meta.model_name, opts.object_name))
                else:
                        (value.model._meta.model_name, opts.object_name)
                    )
            elif hasattr(value, '__iter__'):
                for v in value:
                    self.check_query_object_type(v, opts)
            else:
+8 −0
Original line number Diff line number Diff line
@@ -3487,6 +3487,14 @@ class RelatedLookupTypeTests(TestCase):
        with self.assertNumQueries(0):
            ObjectB.objects.filter(objecta__in=ObjectA.objects.all())

    def test_values_queryset_lookup(self):
        """
        #23396 - Ensure ValueQuerySets are not checked for compatibility with the lookup field
        """
        self.assertQuerysetEqual(ObjectB.objects.filter(
            objecta__in=ObjectB.objects.all().values_list('pk')
        ).order_by('pk'), ['<ObjectB: ob>', '<ObjectB: pob>'])


class Ticket14056Tests(TestCase):
    def test_ticket_14056(self):