Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #15161 - Corrected handling of ManyToManyField with through tab…

…le using to_field on its ForeignKeys. Thanks to adehnert for the report.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15330 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 84291b7b8456b554f162b6eeaffa9b124907c1d2 1 parent 33c1556
Carl Meyer authored
6  django/contrib/contenttypes/generic.py
@@ -135,6 +135,12 @@ def m2m_column_name(self):
135 135
     def m2m_reverse_name(self):
136 136
         return self.rel.to._meta.pk.column
137 137
 
  138
+    def m2m_target_field_name(self):
  139
+        return self.model._meta.pk.name
  140
+
  141
+    def m2m_reverse_target_field_name(self):
  142
+        return self.rel.to._meta.pk.name
  143
+
138 144
     def contribute_to_class(self, cls, name):
139 145
         super(GenericRelation, self).contribute_to_class(cls, name)
140 146
 
5  django/db/models/fields/related.py
@@ -1131,6 +1131,11 @@ def contribute_to_related_class(self, cls, related):
12  django/db/models/sql/query.py
@@ -1282,12 +1282,14 @@ def setup_joins(self, names, opts, alias, dupe_multis, allow_many=True,
1282 1282
                                 to_col2, opts, target) = cached_data
1283 1283
                     else:
1284 1284
                         table1 = field.m2m_db_table()
1285  
-                        from_col1 = opts.pk.column
  1285
+                        from_col1 = opts.get_field_by_name(
  1286
+                            field.m2m_target_field_name())[0].column
1286 1287
                         to_col1 = field.m2m_column_name()
1287 1288
                         opts = field.rel.to._meta
1288 1289
                         table2 = opts.db_table
1289 1290
                         from_col2 = field.m2m_reverse_name()
1290  
-                        to_col2 = opts.pk.column
  1291
+                        to_col2 = opts.get_field_by_name(
  1292
+                            field.m2m_reverse_target_field_name())[0].column
1291 1293
                         target = opts.pk
1292 1294
                         orig_opts._join_cache[name] = (table1, from_col1,
1293 1295
                                 to_col1, table2, from_col2, to_col2, opts,
@@ -1335,12 +1337,14 @@ def setup_joins(self, names, opts, alias, dupe_multis, allow_many=True,
1335 1337
                                 to_col2, opts, target) = cached_data
1336 1338
                     else:
1337 1339
                         table1 = field.m2m_db_table()
1338  
-                        from_col1 = opts.pk.column
  1340
+                        from_col1 = opts.get_field_by_name(
  1341
+                            field.m2m_reverse_target_field_name())[0].column
1339 1342
                         to_col1 = field.m2m_reverse_name()
1340 1343
                         opts = orig_field.opts
1341 1344
                         table2 = opts.db_table
1342 1345
                         from_col2 = field.m2m_column_name()
1343  
-                        to_col2 = opts.pk.column
  1346
+                        to_col2 = opts.get_field_by_name(
  1347
+                            field.m2m_target_field_name())[0].column
1344 1348
                         target = opts.pk
1345 1349
                         orig_opts._join_cache[name] = (table1, from_col1,
1346 1350
                                 to_col1, table2, from_col2, to_col2, opts,
22  tests/regressiontests/m2m_through_regress/models.py
@@ -53,3 +53,25 @@ class Through(ThroughBase):
53 53
 class B(models.Model):
54 54
     b_text = models.CharField(max_length=20)
55 55
     a_list = models.ManyToManyField(A, through=Through)
  56
+
  57
+
  58
+# Using to_field on the through model
  59
+class Car(models.Model):
  60
+    make = models.CharField(max_length=20, unique=True)
  61
+    drivers = models.ManyToManyField('Driver', through='CarDriver')
  62
+
  63
+    def __unicode__(self, ):
  64
+        return self.make
  65
+
  66
+class Driver(models.Model):
  67
+    name = models.CharField(max_length=20, unique=True)
  68
+
  69
+    def __unicode__(self, ):
  70
+        return self.name
  71
+
  72
+class CarDriver(models.Model):
  73
+    car = models.ForeignKey('Car', to_field='make')
  74
+    driver = models.ForeignKey('Driver', to_field='name')
  75
+
  76
+    def __unicode__(self, ):
  77
+        return u"pk=%s car=%s driver=%s" % (str(self.pk), self.car, self.driver)
22  tests/regressiontests/m2m_through_regress/tests.py
@@ -7,7 +7,8 @@
7 7
 from django.contrib.auth.models import User
8 8
 from django.test import TestCase
9 9
 
10  
-from models import Person, Group, Membership, UserMembership
  10
+from models import (Person, Group, Membership, UserMembership,
  11
+                    Car, Driver, CarDriver)
11 12
 
12 13
 
13 14
 class M2MThroughTestCase(TestCase):
@@ -118,6 +119,25 @@ def test_join_trimming(self):
118 119
             ]
119 120
         )
120 121
 
  122
+
  123
+class ToFieldThroughTests(TestCase):
  124
+    def setUp(self):
  125
+        self.car = Car.objects.create(make="Toyota")
  126
+        self.driver = Driver.objects.create(name="Ryan Briscoe")
  127
+        CarDriver.objects.create(car=self.car, driver=self.driver)
  128
+
  129
+    def test_to_field(self):
  130
+        self.assertQuerysetEqual(
  131
+            self.car.drivers.all(),
  132
+            ["<Driver: Ryan Briscoe>"]
  133
+            )
  134
+
  135
+    def test_to_field_reverse(self):
  136
+        self.assertQuerysetEqual(
  137
+            self.driver.car_set.all(),
  138
+            ["<Car: Toyota>"]
  139
+            )
  140
+
121 141
 class ThroughLoadDataTestCase(TestCase):
122 142
     fixtures = ["m2m_through"]
123 143
 

0 notes on commit 84291b7

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