Ticket #16187: 16187.lookup-refactor.diff

File 16187.lookup-refactor.diff, 6.5 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..2863a50 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        lookup_type = "exact"
     1049        model = self.model
     1050        # walk the relations and figure out the lookup type
     1051        if len(parts) > 1 and arg not in self.aggregates:
     1052            for idx, part in enumerate(parts):
     1053                try:
     1054                    if part == "pk":
     1055                        field = model._meta.pk
     1056                    else:
     1057                        # Try to retrieve one of the model's fields from the
     1058                        # given part.
     1059                        try:
     1060                            field, _, _, _ = model._meta.get_field_by_name(part)
     1061                        except FieldDoesNotExist:
     1062                            # The first attempt failed. Let's check if it's
     1063                            # a foreignkey.
     1064                            if len(part) > 3 and part[-3:] == '_id':
     1065                                field, _, _, _ = model._meta.get_field_by_name(part[:-3])
     1066                            else:
     1067                                # OK, so the field really doesn't exist
     1068                                raise
     1069                        if hasattr(field, "model") and field.model != model:
     1070                            model = field.model
     1071                            continue
     1072                        if hasattr(field, "field"):
     1073                            # RelatedObjects
     1074                            field = field.field
     1075                    if field.rel is not None:
     1076                        model = field.rel.to
     1077                        continue
     1078                    # If we reach here we are no longer traversing relations
     1079                    if idx < len(parts) - 1 and parts[-1] in self.query_terms:
     1080                        lookup_type = parts.pop()
     1081                    break
     1082                except FieldDoesNotExist:
     1083                    if part in self.aggregates and parts[-1] in self.query_terms:
     1084                        lookup_type = parts.pop()
     1085                        break
     1086                    # if we're not on the last part something is wrong
     1087                    if idx != len(parts) - 1:
     1088                        # We can't handle this - fall through
     1089                        if parts[-1] in self.query_terms:
     1090                            lookup_type = parts.pop()
     1091                        break
     1092                    lookup_type = parts.pop()
     1093                    break
    10531094
    10541095        # By default, this is a WHERE clause. If an aggregate is referenced
    10551096        # in the value, the filter will be promoted to a HAVING
  • new file tests/modeltests/lookup_collision/models.py

    diff --git a/tests/modeltests/lookup_collision/__init__.py b/tests/modeltests/lookup_collision/__init__.py
    new file mode 100644
    index 0000000..e69de29
    diff --git a/tests/modeltests/lookup_collision/models.py b/tests/modeltests/lookup_collision/models.py
    new file mode 100644
    index 0000000..e8ed05a
    - +  
     1from django.db import models, DEFAULT_DB_ALIAS, connection
     2from django.conf import settings
     3
     4class RepeatingEvent(models.Model):
     5    name = models.CharField(max_length=100)
     6    week_day = models.IntegerField()
     7    class Meta:
     8        ordering = ('week_day', 'name', )
     9
     10class Appointment(models.Model):
     11    name = models.CharField(max_length=100)
     12    day = models.DateField()
     13    repeating = models.ForeignKey(RepeatingEvent)
     14    class Meta:
     15        ordering = ('day', 'name', )
     16
     17class Calendar(models.Model):
     18    name = models.CharField(max_length=100)
     19    appointments = models.ManyToManyField(Appointment)
     20    class Meta:
     21        ordering = ('name',)
     22
     23    def __unicode__(self):
     24        return self.name
  • new file tests/modeltests/lookup_collision/tests.py

    diff --git a/tests/modeltests/lookup_collision/tests.py b/tests/modeltests/lookup_collision/tests.py
    new file mode 100644
    index 0000000..0a87730
    - +  
     1from datetime import datetime
     2from django.test import TestCase
     3from models import RepeatingEvent, Appointment, Calendar
     4
     5
     6class LookupCollisionTests(TestCase):
     7    """Test for collisions between lookup type names and field names"""
     8
     9    def setUp(self):
     10        # Create a RepeatingEvent
     11        self.rep1 = RepeatingEvent(name="Every Thursday", week_day=5)
     12        self.rep1.save()
     13        # Create an Appointment
     14        self.ap1 = Appointment(name="Today and every thursday", day=datetime(2011, 6, 9), repeating=self.rep1)
     15        self.ap1.save()
     16        # Create a Calendar
     17        self.cal = Calendar(name="My Calendar")
     18        self.cal.save()
     19        self.cal.appointments = [self.ap1, ]
     20        self.cal.save()
     21
     22    def test_query_simple(self):
     23        """Test simple queries with colliding names without traversing relations"""
     24        self.assertTrue(RepeatingEvent.objects.filter(week_day=5).exists())
     25        self.assertTrue(Appointment.objects.filter(day=datetime(2011, 6, 9)).exists())
     26
     27    def test_query_related(self):
     28        """Test queries through related fields with colliding field names"""
     29        self.assertTrue(Appointment.objects.filter(repeating__week_day=5).exists())
     30
     31        self.assertTrue(Calendar.objects.filter(appointments__day=datetime(2011, 6, 9)).exists())
     32        self.assertTrue(Calendar.objects.filter(appointments__repeating__week_day=5).exists())
     33
     34    def test_query_related_with_lookup(self):
     35        """Test queries through related fields with colliding names with explicit lookup types"""
     36        self.assertTrue(RepeatingEvent.objects.filter(week_day__exact=5).exists())
     37
     38        self.assertTrue(Appointment.objects.filter(day__exact=datetime(2011, 6, 9)).exists())
     39        self.assertTrue(Appointment.objects.filter(day__week_day=5).exists())
     40        self.assertTrue(Appointment.objects.filter(repeating__week_day__exact=5).exists())
     41
     42        self.assertTrue(Calendar.objects.filter(appointments__day__exact=datetime(2011, 6, 9)).exists())
     43        self.assertTrue(Calendar.objects.filter(appointments__day__week_day=5).exists())
     44        self.assertTrue(Calendar.objects.filter(appointments__repeating__week_day__exact=5).exists())
Back to Top