Ticket #11670: 11670.field-lookup-collisions.7.diff

File 11670.field-lookup-collisions.7.diff, 7.6 KB (added by Julien Phalip, 12 years ago)
  • django/db/models/sql/query.py

    diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
    index ed2bc06..7c2e2dc 100644
    a b class Query(object):  
    10611061        if not parts:
    10621062            raise FieldError("Cannot parse keyword query %r" % arg)
    10631063
    1064         # Work out the lookup type and remove it from 'parts', if necessary.
    1065         if len(parts) == 1 or parts[-1] not in self.query_terms:
    1066             lookup_type = 'exact'
    1067         else:
    1068             lookup_type = parts.pop()
     1064        # Work out the lookup type and remove it from the end of 'parts',
     1065        # if necessary.
     1066        lookup_type = 'exact' # Default lookup type
     1067        num_parts = len(parts)
     1068        if (len(parts) > 1 and parts[-1] in self.query_terms
     1069            and arg not in self.aggregates):
     1070            # Traverse the lookup query to distinguish related fields from
     1071            # lookup types.
     1072            try:
     1073                lookup_model = self.model
     1074                for counter, field_name in enumerate(parts):
     1075                    lookup_field = lookup_model._meta.get_field(field_name)
     1076                    if (counter + 1) < num_parts:
     1077                        # Unless we're at the end of the list of lookups, let's
     1078                        # attempt to continue traversing relations.
     1079                        lookup_model = lookup_field.rel.to
     1080            except (FieldDoesNotExist, AttributeError):
     1081                # The traversing didn't reach the end because at least one of
     1082                # the lookups wasn't a field.
     1083                lookup_type = parts.pop()
    10691084
    10701085        # By default, this is a WHERE clause. If an aggregate is referenced
    10711086        # in the value, the filter will be promoted to a HAVING
  • tests/modeltests/lookup/models.py

    diff --git a/tests/modeltests/lookup/models.py b/tests/modeltests/lookup/models.py
    index 7c264a4..bcdd3d7 100644
    a b class Tag(models.Model):  
    2727    name = models.CharField(max_length=100)
    2828    class Meta:
    2929        ordering = ('name', )
     30
     31class Season(models.Model):
     32    year = models.PositiveSmallIntegerField()
     33    gt = models.IntegerField(null=True, blank=True)
     34
     35    def __unicode__(self):
     36        return unicode(self.year)
     37
     38class Game(models.Model):
     39    season = models.ForeignKey(Season, related_name='games')
     40    home = models.CharField(max_length=100)
     41    away = models.CharField(max_length=100)
     42
     43    def __unicode__(self):
     44        return u"%s at %s" % (self.away, self.home)
     45
     46class Player(models.Model):
     47    name = models.CharField(max_length=100)
     48    games = models.ManyToManyField(Game, related_name='players')
     49
     50    def __unicode__(self):
     51        return self.name
     52 No newline at end of file
  • tests/modeltests/lookup/tests.py

    diff --git a/tests/modeltests/lookup/tests.py b/tests/modeltests/lookup/tests.py
    index 9c2b0c6..b49abfb 100644
    a b from operator import attrgetter  
    66from django.core.exceptions import FieldError
    77from django.test import TestCase, skipUnlessDBFeature
    88
    9 from .models import Author, Article, Tag
     9from .models import Author, Article, Tag, Game, Season, Player
    1010
    1111
    1212class LookupTests(TestCase):
    class LookupTests(TestCase):  
    610610        a16.save()
    611611        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'b(.).*b\1'),
    612612            ['<Article: barfoobaz>', '<Article: bazbaRFOO>', '<Article: foobarbaz>'])
     613
     614class LookupCollisionTests(TestCase):
     615
     616    def setUp(self):
     617        # Here we're using 'gt' as a code number for the year, e.g. 111=>2009.
     618        season_2009 = Season.objects.create(year=2009, gt=111)
     619        season_2009.games.create(home="Houston Astros", away="St. Louis Cardinals")
     620        season_2010 = Season.objects.create(year=2010, gt=222)
     621        season_2010.games.create(home="Houston Astros", away="Chicago Cubs")
     622        season_2010.games.create(home="Houston Astros", away="Milwaukee Brewers")
     623        season_2010.games.create(home="Houston Astros", away="St. Louis Cardinals")
     624        season_2011 = Season.objects.create(year=2011, gt=333)
     625        season_2011.games.create(home="Houston Astros", away="St. Louis Cardinals")
     626        season_2011.games.create(home="Houston Astros", away="Milwaukee Brewers")
     627        hunter_pence = Player.objects.create(name="Hunter Pence")
     628        hunter_pence.games = Game.objects.filter(season__year__in=[2009, 2010])
     629        pudge = Player.objects.create(name="Ivan Rodriquez")
     630        pudge.games = Game.objects.filter(season__year=2009)
     631        pedro_feliz = Player.objects.create(name="Pedro Feliz")
     632        pedro_feliz.games = Game.objects.filter(season__year__in=[2011])
     633        johnson = Player.objects.create(name="Johnson")
     634        johnson.games = Game.objects.filter(season__year__in=[2011])
     635
     636    def test_lookup_collision(self):
     637        """
     638        Ensure that genuine field names don't collide with built-in lookup
     639        types ('year', 'gt', 'range', 'in' etc.).
     640        Refs #11670.
     641        """
     642        # Games in 2010
     643        self.assertEqual(Game.objects.filter(season__year=2010).count(), 3)
     644        self.assertEqual(Game.objects.filter(season__year__exact=2010).count(), 3)
     645        self.assertEqual(Game.objects.filter(season__gt=222).count(), 3)
     646        self.assertEqual(Game.objects.filter(season__gt__exact=222).count(), 3)
     647
     648        # Games in 2011
     649        self.assertEqual(Game.objects.filter(season__year=2011).count(), 2)
     650        self.assertEqual(Game.objects.filter(season__year__exact=2011).count(), 2)
     651        self.assertEqual(Game.objects.filter(season__gt=333).count(), 2)
     652        self.assertEqual(Game.objects.filter(season__gt__exact=333).count(), 2)
     653        self.assertEqual(Game.objects.filter(season__year__gt=2010).count(), 2)
     654        self.assertEqual(Game.objects.filter(season__gt__gt=222).count(), 2)
     655
     656        # Games played in 2010 and 2011
     657        self.assertEqual(Game.objects.filter(season__year__in=[2010, 2011]).count(), 5)
     658        self.assertEqual(Game.objects.filter(season__year__gt=2009).count(), 5)
     659        self.assertEqual(Game.objects.filter(season__gt__in=[222, 333]).count(), 5)
     660        self.assertEqual(Game.objects.filter(season__gt__gt=111).count(), 5)
     661
     662        # Players who played in 2009
     663        self.assertEqual(Player.objects.filter(games__season__year=2009).distinct().count(), 2)
     664        self.assertEqual(Player.objects.filter(games__season__year__exact=2009).distinct().count(), 2)
     665        self.assertEqual(Player.objects.filter(games__season__gt=111).distinct().count(), 2)
     666        self.assertEqual(Player.objects.filter(games__season__gt__exact=111).distinct().count(), 2)
     667
     668        # Players who played in 2010
     669        self.assertEqual(Player.objects.filter(games__season__year=2010).distinct().count(), 1)
     670        self.assertEqual(Player.objects.filter(games__season__year__exact=2010).distinct().count(), 1)
     671        self.assertEqual(Player.objects.filter(games__season__gt=222).distinct().count(), 1)
     672        self.assertEqual(Player.objects.filter(games__season__gt__exact=222).distinct().count(), 1)
     673
     674        # Players who played in 2011
     675        self.assertEqual(Player.objects.filter(games__season__year=2011).distinct().count(), 2)
     676        self.assertEqual(Player.objects.filter(games__season__year__exact=2011).distinct().count(), 2)
     677        self.assertEqual(Player.objects.filter(games__season__gt=333).distinct().count(), 2)
     678        self.assertEqual(Player.objects.filter(games__season__year__gt=2010).distinct().count(), 2)
     679        self.assertEqual(Player.objects.filter(games__season__gt__gt=222).distinct().count(), 2)
Back to Top