Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[1.2.X] Fixed #11319 - Added lookup support for ForeignKey.to_field. …

…Also reverted no-longer-needed model formsets workaround for lack of such support from r10756. Thanks Russell and Alex for review.

Backport of r15303 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.2.X@15304 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 131d83b9cdd6d287aa04345c78993c66c0300422 1 parent f01e1cd
Carl Meyer authored January 25, 2011
13  django/db/models/fields/related.py
@@ -176,9 +176,20 @@ def _pk_trace(self, value, prep_func, lookup_type, **kwargs):
7  django/db/models/sql/query.py
@@ -1365,7 +1365,12 @@ def setup_joins(self, names, opts, alias, dupe_multis, allow_many=True,
1365 1365
                         table = opts.db_table
1366 1366
                         from_col = local_field.column
1367 1367
                         to_col = field.column
1368  
-                        target = opts.pk
  1368
+                        # In case of a recursive FK, use the to_field for
  1369
+                        # reverse lookups as well
  1370
+                        if orig_field.model is local_field.model:
  1371
+                            target = opts.get_field(field.rel.field_name)
  1372
+                        else:
  1373
+                            target = opts.pk
1369 1374
                         orig_opts._join_cache[name] = (table, from_col, to_col,
1370 1375
                                 opts, target)
1371 1376
 
6  django/forms/models.py
@@ -690,13 +690,9 @@ def __init__(self, data=None, files=None, instance=None,
690 690
         self.save_as_new = save_as_new
691 691
         # is there a better way to get the object descriptor?
692 692
         self.rel_name = RelatedObject(self.fk.rel.to, self.model, self.fk).get_accessor_name()
693  
-        if self.fk.rel.field_name == self.fk.rel.to._meta.pk.name:
694  
-            backlink_value = self.instance
695  
-        else:
696  
-            backlink_value = getattr(self.instance, self.fk.rel.field_name)
697 693
         if queryset is None:
698 694
             queryset = self.model._default_manager
699  
-        qs = queryset.filter(**{self.fk.name: backlink_value})
  695
+        qs = queryset.filter(**{self.fk.name: self.instance})
700 696
         super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix,
701 697
                                                 queryset=qs)
702 698
 
8  tests/modeltests/custom_pk/tests.py
@@ -158,11 +158,9 @@ def test_custom_field_pk(self):
158 158
         new_bar = Bar.objects.create()
159 159
         new_foo = Foo.objects.create(bar=new_bar)
160 160
 
161  
-        # FIXME: This still doesn't work, but will require some changes in
162  
-        # get_db_prep_lookup to fix it.
163  
-        # f = Foo.objects.get(bar=new_bar.pk)
164  
-        # self.assertEqual(f, new_foo)
165  
-        # self.assertEqual(f.bar, new_bar)
  161
+        f = Foo.objects.get(bar=new_bar.pk)
  162
+        self.assertEqual(f, new_foo)
  163
+        self.assertEqual(f.bar, new_bar)
166 164
 
167 165
         f = Foo.objects.get(bar=new_bar)
168 166
         self.assertEqual(f, new_foo),
20  tests/regressiontests/queries/models.py
@@ -274,3 +274,23 @@ def __unicode__(self):
274 274
 class Article(models.Model):
275 275
     name = models.CharField(max_length=20)
276 276
     created = models.DateTimeField()
  277
+
  278
+class Food(models.Model):
  279
+    name = models.CharField(max_length=20, unique=True)
  280
+
  281
+    def __unicode__(self):
  282
+        return self.name
  283
+
  284
+class Eaten(models.Model):
  285
+    food = models.ForeignKey(Food, to_field="name")
  286
+    meal = models.CharField(max_length=20)
  287
+
  288
+    def __unicode__(self):
  289
+        return u"%s at %s" % (self.food, self.meal)
  290
+
  291
+class Node(models.Model):
  292
+    num = models.IntegerField(unique=True)
  293
+    parent = models.ForeignKey("self", to_field="num", null=True)
  294
+
  295
+    def __unicode__(self):
  296
+        return u"%s" % self.num
63  tests/regressiontests/queries/tests.py
@@ -14,7 +14,7 @@
14 14
 from models import (Annotation, Article, Author, Celebrity, Child, Cover, Detail,
15 15
     DumbCategory, ExtraInfo, Fan, Item, LeafA, LoopX, LoopZ, ManagedModel,
16 16
     Member, NamedCategory, Note, Number, Plaything, PointerA, Ranking, Related,
17  
-    Report, ReservedName, Tag, TvChef, Valid, X)
  17
+    Report, ReservedName, Tag, TvChef, Valid, X, Food, Eaten, Node)
18 18
 
19 19
 
20 20
 class BaseQuerysetTest(TestCase):
@@ -1506,6 +1506,67 @@ def test_ticket_7302(self):
1506 1506
         )
1507 1507
 
1508 1508
 
  1509
+class ToFieldTests(TestCase):
  1510
+    def test_in_query(self):
  1511
+        apple = Food.objects.create(name="apple")
  1512
+        pear = Food.objects.create(name="pear")
  1513
+        lunch = Eaten.objects.create(food=apple, meal="lunch")
  1514
+        dinner = Eaten.objects.create(food=pear, meal="dinner")
  1515
+
  1516
+        self.assertEqual(
  1517
+            set(Eaten.objects.filter(food__in=[apple, pear])),
  1518
+            set([lunch, dinner]),
  1519
+        )
  1520
+
  1521
+    def test_reverse_in(self):
  1522
+        apple = Food.objects.create(name="apple")
  1523
+        pear = Food.objects.create(name="pear")
  1524
+        lunch_apple = Eaten.objects.create(food=apple, meal="lunch")
  1525
+        lunch_pear = Eaten.objects.create(food=pear, meal="dinner")
  1526
+
  1527
+        self.assertEqual(
  1528
+            set(Food.objects.filter(eaten__in=[lunch_apple, lunch_pear])),
  1529
+            set([apple, pear])
  1530
+        )
  1531
+
  1532
+    def test_single_object(self):
  1533
+        apple = Food.objects.create(name="apple")
  1534
+        lunch = Eaten.objects.create(food=apple, meal="lunch")
  1535
+        dinner = Eaten.objects.create(food=apple, meal="dinner")
  1536
+
  1537
+        self.assertEqual(
  1538
+            set(Eaten.objects.filter(food=apple)),
  1539
+            set([lunch, dinner])
  1540
+        )
  1541
+
  1542
+    def test_single_object_reverse(self):
  1543
+        apple = Food.objects.create(name="apple")
  1544
+        lunch = Eaten.objects.create(food=apple, meal="lunch")
  1545
+
  1546
+        self.assertEqual(
  1547
+            set(Food.objects.filter(eaten=lunch)),
  1548
+            set([apple])
  1549
+        )
  1550
+
  1551
+    def test_recursive_fk(self):
  1552
+        node1 = Node.objects.create(num=42)
  1553
+        node2 = Node.objects.create(num=1, parent=node1)
  1554
+
  1555
+        self.assertEqual(
  1556
+            list(Node.objects.filter(parent=node1)),
  1557
+            [node2]
  1558
+        )
  1559
+
  1560
+    def test_recursive_fk_reverse(self):
  1561
+        node1 = Node.objects.create(num=42)
  1562
+        node2 = Node.objects.create(num=1, parent=node1)
  1563
+
  1564
+        self.assertEqual(
  1565
+            list(Node.objects.filter(node=node2)),
  1566
+            [node1]
  1567
+        )
  1568
+
  1569
+
1509 1570
 # In Python 2.6 beta releases, exceptions raised in __len__ are swallowed
1510 1571
 # (Python issue 1242657), so these cases return an empty list, rather than
1511 1572
 # raising an exception. Not a lot we can do about that, unfortunately, due to

0 notes on commit 131d83b

Please sign in to comment.
Something went wrong with that request. Please try again.