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

File 11670.field-lookup-collisions.2.diff, 8.0 KB (added by Julien Phalip, 13 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 61fd2be..58f53b3 100644
    a b class Query(object):  
    10451045        if not parts:
    10461046            raise FieldError("Cannot parse keyword query %r" % arg)
    10471047
    1048         # Work out the lookup type and remove it from 'parts', if necessary.
    1049         if len(parts) == 1 or parts[-1] not in self.query_terms:
    1050             lookup_type = 'exact'
    1051         else:
    1052             lookup_type = parts.pop()
     1048        # Work out the lookup type and remove it from the end of 'parts',
     1049        # if necessary.
     1050        lookup_type = 'exact' # Default lookup type
     1051        num_parts = len(parts)
     1052        if num_parts > 1 and arg not in self.aggregates:
     1053            # Traverse the lookup query to distinguish related fields from
     1054            # lookup types.
     1055            try:
     1056                lookup_model = self.model
     1057                for counter, field_name in enumerate(parts):
     1058                    lookup_field = lookup_model._meta.get_field(field_name)
     1059                    if (counter + 1) < num_parts:
     1060                        # Unless we're at the end of the list of lookups, let's
     1061                        # attempt to continue traversing relations.
     1062                        lookup_model = lookup_field.rel.to
     1063            except (FieldDoesNotExist, AttributeError):
     1064                # The traversing didn't reach the end because at least one of
     1065                # the lookups wasn't a field.
     1066                if parts[-1] in self.query_terms:
     1067                    lookup_type = parts.pop()
    10531068
    10541069        # By default, this is a WHERE clause. If an aggregate is referenced
    10551070        # 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 82434bb..dbd52e3 100644
    a b class Tag(models.Model):  
    2626    name = models.CharField(max_length=100)
    2727    class Meta:
    2828        ordering = ('name', )
     29
     30class Season(models.Model):
     31    year = models.PositiveSmallIntegerField()
     32    gt = models.IntegerField(null=True, blank=True)
     33
     34    def __unicode__(self):
     35        return unicode(self.year)
     36
     37class Game(models.Model):
     38    season = models.ForeignKey(Season, related_name='games')
     39    home = models.CharField(max_length=100)
     40    away = models.CharField(max_length=100)
     41
     42    def __unicode__(self):
     43        return u"%s at %s" % (self.away, self.home)
     44
     45class Player(models.Model):
     46    name = models.CharField(max_length=100)
     47    games = models.ManyToManyField(Game, related_name='players')
     48
     49    def __unicode__(self):
     50        return self.name
     51 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 33eeae7..6e4d98e 100644
    a b  
    11from datetime import datetime
    22from operator import attrgetter
     3
    34from django.core.exceptions import FieldError
    45from django.test import TestCase, skipUnlessDBFeature
    5 from models import Author, Article, Tag
     6
     7from models import Author, Article, Tag, Game, Season, Player
    68
    79
    810class LookupTests(TestCase):
    class LookupTests(TestCase):  
    243245        self.assertQuerysetEqual(Article.objects.filter(id=self.a5.id).values(),
    244246            [{
    245247                'id': self.a5.id,
    246                 'author_id': self.au2.id, 
     248                'author_id': self.au2.id,
    247249                'headline': 'Article 5',
    248250                'pub_date': datetime(2005, 8, 1, 9, 0)
    249251            }], transform=identity)
    class LookupTests(TestCase):  
    606608        a16.save()
    607609        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'b(.).*b\1'),
    608610            ['<Article: barfoobaz>', '<Article: bazbaRFOO>', '<Article: foobarbaz>'])
     611
     612class LookupCollisionTests(TestCase):
     613
     614    def setUp(self):
     615        # Here we're using 'gt' as a code number for the year, e.g. 111=>2009.
     616        season_2009 = Season.objects.create(year=2009, gt=111)
     617        season_2009.games.create(home="Houston Astros", away="St. Louis Cardinals")
     618        season_2010 = Season.objects.create(year=2010, gt=222)
     619        season_2010.games.create(home="Houston Astros", away="Chicago Cubs")
     620        season_2010.games.create(home="Houston Astros", away="Milwaukee Brewers")
     621        season_2010.games.create(home="Houston Astros", away="St. Louis Cardinals")
     622        season_2011 = Season.objects.create(year=2011, gt=333)
     623        season_2011.games.create(home="Houston Astros", away="St. Louis Cardinals")
     624        season_2011.games.create(home="Houston Astros", away="Milwaukee Brewers")
     625        hunter_pence = Player.objects.create(name="Hunter Pence")
     626        hunter_pence.games = Game.objects.filter(season__year__in=[2009, 2010])
     627        pudge = Player.objects.create(name="Ivan Rodriquez")
     628        pudge.games = Game.objects.filter(season__year=2009)
     629        pedro_feliz = Player.objects.create(name="Pedro Feliz")
     630        pedro_feliz.games = Game.objects.filter(season__year__in=[2011])
     631        johnson = Player.objects.create(name="Johnson")
     632        johnson.games = Game.objects.filter(season__year__in=[2011])
     633
     634    def test_lookup_collision(self):
     635        """
     636        Ensure that genuine field names don't collide with built-in lookup
     637        types ('year', 'gt', 'range', 'in' etc.).
     638        Refs #11670.
     639        """
     640        # Games in 2010
     641        self.assertEqual(Game.objects.filter(season__year=2010).count(), 3)
     642        self.assertEqual(Game.objects.filter(season__year__exact=2010).count(), 3)
     643        self.assertEqual(Game.objects.filter(season__gt=222).count(), 3)
     644        self.assertEqual(Game.objects.filter(season__gt__exact=222).count(), 3)
     645
     646        # Games in 2011
     647        self.assertEqual(Game.objects.filter(season__year=2011).count(), 2)
     648        self.assertEqual(Game.objects.filter(season__year__exact=2011).count(), 2)
     649        self.assertEqual(Game.objects.filter(season__gt=333).count(), 2)
     650        self.assertEqual(Game.objects.filter(season__gt__exact=333).count(), 2)
     651        self.assertEqual(Game.objects.filter(season__year__gt=2010).count(), 2)
     652        self.assertEqual(Game.objects.filter(season__gt__gt=222).count(), 2)
     653
     654        # Games played in 2010 and 2011
     655        self.assertEqual(Game.objects.filter(season__year__in=[2010, 2011]).count(), 5)
     656        self.assertEqual(Game.objects.filter(season__year__gt=2009).count(), 5)
     657        self.assertEqual(Game.objects.filter(season__gt__in=[222, 333]).count(), 5)
     658        self.assertEqual(Game.objects.filter(season__gt__gt=111).count(), 5)
     659
     660        # Players who played in 2009
     661        self.assertEqual(Player.objects.filter(games__season__year=2009).distinct().count(), 2)
     662        self.assertEqual(Player.objects.filter(games__season__year__exact=2009).distinct().count(), 2)
     663        self.assertEqual(Player.objects.filter(games__season__gt=111).distinct().count(), 2)
     664        self.assertEqual(Player.objects.filter(games__season__gt__exact=111).distinct().count(), 2)
     665
     666        # Players who played in 2010
     667        self.assertEqual(Player.objects.filter(games__season__year=2010).distinct().count(), 1)
     668        self.assertEqual(Player.objects.filter(games__season__year__exact=2010).distinct().count(), 1)
     669        self.assertEqual(Player.objects.filter(games__season__gt=222).distinct().count(), 1)
     670        self.assertEqual(Player.objects.filter(games__season__gt__exact=222).distinct().count(), 1)
     671
     672        # Players who played in 2011
     673        self.assertEqual(Player.objects.filter(games__season__year=2011).distinct().count(), 2)
     674        self.assertEqual(Player.objects.filter(games__season__year__exact=2011).distinct().count(), 2)
     675        self.assertEqual(Player.objects.filter(games__season__gt=333).distinct().count(), 2)
     676        self.assertEqual(Player.objects.filter(games__season__year__gt=2010).distinct().count(), 2)
     677        self.assertEqual(Player.objects.filter(games__season__gt__gt=222).distinct().count(), 2)
Back to Top