Fixed #24687 -- Added select_related() validation for nested non-relational fields.
The removed test was added in the original select_related() validation
patch (45d4e43d2d
), but there doesn't
seem to be any reason for it.
Thanks Claude Paroz for help and review.
This commit is contained in:
parent
5171f56fae
commit
67732a9b18
|
@ -10,6 +10,7 @@ from django.contrib.gis.db.models.sql import (
|
|||
from django.contrib.gis.geometry.backend import Geometry
|
||||
from django.contrib.gis.measure import Area, Distance
|
||||
from django.db import connections
|
||||
from django.db.models.constants import LOOKUP_SEP
|
||||
from django.db.models.expressions import RawSQL
|
||||
from django.db.models.fields import Field
|
||||
from django.db.models.query import QuerySet
|
||||
|
@ -675,9 +676,10 @@ class GeoQuerySet(QuerySet):
|
|||
if geo_field not in opts.fields:
|
||||
# Is this operation going to be on a related geographic field?
|
||||
# If so, it'll have to be added to the select related information
|
||||
# (e.g., if 'location__point' was given as the field name).
|
||||
# (e.g., if 'location__point' was given as the field name, then
|
||||
# chop the non-relational field and add select_related('location')).
|
||||
# Note: the operation really is defined as "must add select related!"
|
||||
self.query.add_select_related([field_name])
|
||||
self.query.add_select_related([field_name.rsplit(LOOKUP_SEP, 1)[0]])
|
||||
# Call pre_sql_setup() so that compiler.select gets populated.
|
||||
compiler.pre_sql_setup()
|
||||
for col, _, _ in compiler.select:
|
||||
|
|
|
@ -673,7 +673,7 @@ class SQLCompiler(object):
|
|||
if not f.is_relation:
|
||||
# If a non-related field is used like a relation,
|
||||
# or if a single non-relational field is given.
|
||||
if next or (cur_depth == 1 and f.name in requested):
|
||||
if next or f.name in requested:
|
||||
raise FieldError(
|
||||
"Non-relational field given in select_related: '%s'. "
|
||||
"Choices are: %s" % (
|
||||
|
|
|
@ -217,6 +217,23 @@ Database backend API
|
|||
|
||||
* ...
|
||||
|
||||
``select_related()`` prohibits non-relational fields for nested relations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Django 1.8 added validation for non-relational fields in ``select_related()``::
|
||||
|
||||
>>> Book.objects.select_related('title')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
FieldError: Non-relational field given in select_related: 'title'
|
||||
|
||||
But it didn't prohibit nested non-relation fields as it does now::
|
||||
|
||||
>>> Book.objects.select_related('author__name')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
FieldError: Non-relational field given in select_related: 'name'
|
||||
|
||||
Miscellaneous
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ class ChildAdmin(admin.ModelAdmin):
|
|||
list_filter = ['parent', 'age']
|
||||
|
||||
def get_queryset(self, request):
|
||||
return super(ChildAdmin, self).get_queryset(request).select_related("parent__name")
|
||||
return super(ChildAdmin, self).get_queryset(request).select_related("parent")
|
||||
|
||||
|
||||
class CustomPaginationAdmin(ChildAdmin):
|
||||
|
|
|
@ -75,9 +75,7 @@ class ChangeListTests(TestCase):
|
|||
request, Child,
|
||||
*get_changelist_args(m, list_select_related=m.get_list_select_related(request))
|
||||
)
|
||||
self.assertEqual(cl.queryset.query.select_related, {
|
||||
'parent': {'name': {}}
|
||||
})
|
||||
self.assertEqual(cl.queryset.query.select_related, {'parent': {}})
|
||||
|
||||
def test_select_related_as_tuple(self):
|
||||
ia = InvitationAdmin(Invitation, custom_site)
|
||||
|
|
|
@ -62,6 +62,9 @@ class RelatedGeoModelTest(TestCase):
|
|||
qs = list(City.objects.filter(name=name).transform(srid, field_name='location__point'))
|
||||
check_pnt(GEOSGeometry(wkt, srid), qs[0].location.point)
|
||||
|
||||
# Relations more than one level deep can be queried.
|
||||
self.assertEqual(list(Parcel.objects.transform(srid, field_name='city__location__point')), [])
|
||||
|
||||
@skipUnlessDBFeature("supports_extent_aggr")
|
||||
def test_related_extent_aggregate(self):
|
||||
"Testing the `Extent` aggregate on related geographic models."
|
||||
|
|
|
@ -134,12 +134,6 @@ class SelectRelatedTests(TestCase):
|
|||
orders = [o.genus.family.order.name for o in world]
|
||||
self.assertEqual(orders, ['Agaricales'])
|
||||
|
||||
def test_single_related_field(self):
|
||||
with self.assertNumQueries(1):
|
||||
species = Species.objects.select_related('genus__name')
|
||||
names = [s.genus.name for s in species]
|
||||
self.assertEqual(sorted(names), ['Amanita', 'Drosophila', 'Homo', 'Pisum'])
|
||||
|
||||
def test_field_traversal(self):
|
||||
with self.assertNumQueries(1):
|
||||
s = (Species.objects.all()
|
||||
|
@ -206,6 +200,10 @@ class SelectRelatedValidationTests(SimpleTestCase):
|
|||
with self.assertRaisesMessage(FieldError, self.non_relational_error % ('name', '(none)')):
|
||||
list(Domain.objects.select_related('name'))
|
||||
|
||||
def test_non_relational_field_nested(self):
|
||||
with self.assertRaisesMessage(FieldError, self.non_relational_error % ('name', 'family')):
|
||||
list(Species.objects.select_related('genus__name'))
|
||||
|
||||
def test_many_to_many_field(self):
|
||||
with self.assertRaisesMessage(FieldError, self.invalid_error % ('toppings', '(none)')):
|
||||
list(Pizza.objects.select_related('toppings'))
|
||||
|
|
Loading…
Reference in New Issue