Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[1.2.X] Fixed #15161 - Corrected handling of ManyToManyField with thr…

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

Backport of r15330 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.2.X@15331 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 10b4d93f50f1c2e90387edea5738b2439987ca33 1 parent 5b8c593
@carljm carljm authored
View
6 django/contrib/contenttypes/generic.py
@@ -131,6 +131,12 @@ def m2m_column_name(self):
def m2m_reverse_name(self):
return self.rel.to._meta.pk.column
+ def m2m_target_field_name(self):
+ return self.model._meta.pk.name
+
+ def m2m_reverse_target_field_name(self):
+ return self.rel.to._meta.pk.name
+
def contribute_to_class(self, cls, name):
super(GenericRelation, self).contribute_to_class(cls, name)
View
5 django/db/models/fields/related.py
@@ -1144,6 +1144,11 @@ def contribute_to_related_class(self, cls, related):
self.m2m_field_name = curry(self._get_m2m_attr, related, 'name')
self.m2m_reverse_field_name = curry(self._get_m2m_reverse_attr, related, 'name')
+ get_m2m_rel = curry(self._get_m2m_attr, related, 'rel')
+ self.m2m_target_field_name = lambda: get_m2m_rel().field_name
+ get_m2m_reverse_rel = curry(self._get_m2m_reverse_attr, related, 'rel')
+ self.m2m_reverse_target_field_name = lambda: get_m2m_reverse_rel().field_name
+
def set_attributes_from_rel(self):
pass
View
12 django/db/models/sql/query.py
@@ -1283,12 +1283,14 @@ def setup_joins(self, names, opts, alias, dupe_multis, allow_many=True,
to_col2, opts, target) = cached_data
else:
table1 = field.m2m_db_table()
- from_col1 = opts.pk.column
+ from_col1 = opts.get_field_by_name(
+ field.m2m_target_field_name())[0].column
to_col1 = field.m2m_column_name()
opts = field.rel.to._meta
table2 = opts.db_table
from_col2 = field.m2m_reverse_name()
- to_col2 = opts.pk.column
+ to_col2 = opts.get_field_by_name(
+ field.m2m_reverse_target_field_name())[0].column
target = opts.pk
orig_opts._join_cache[name] = (table1, from_col1,
to_col1, table2, from_col2, to_col2, opts,
@@ -1336,12 +1338,14 @@ def setup_joins(self, names, opts, alias, dupe_multis, allow_many=True,
to_col2, opts, target) = cached_data
else:
table1 = field.m2m_db_table()
- from_col1 = opts.pk.column
+ from_col1 = opts.get_field_by_name(
+ field.m2m_reverse_target_field_name())[0].column
to_col1 = field.m2m_reverse_name()
opts = orig_field.opts
table2 = opts.db_table
from_col2 = field.m2m_column_name()
- to_col2 = opts.pk.column
+ to_col2 = opts.get_field_by_name(
+ field.m2m_target_field_name())[0].column
target = opts.pk
orig_opts._join_cache[name] = (table1, from_col1,
to_col1, table2, from_col2, to_col2, opts,
View
22 tests/regressiontests/m2m_through_regress/models.py
@@ -53,3 +53,25 @@ class Through(ThroughBase):
class B(models.Model):
b_text = models.CharField(max_length=20)
a_list = models.ManyToManyField(A, through=Through)
+
+
+# Using to_field on the through model
+class Car(models.Model):
+ make = models.CharField(max_length=20, unique=True)
+ drivers = models.ManyToManyField('Driver', through='CarDriver')
+
+ def __unicode__(self, ):
+ return self.make
+
+class Driver(models.Model):
+ name = models.CharField(max_length=20, unique=True)
+
+ def __unicode__(self, ):
+ return self.name
+
+class CarDriver(models.Model):
+ car = models.ForeignKey('Car', to_field='make')
+ driver = models.ForeignKey('Driver', to_field='name')
+
+ def __unicode__(self, ):
+ return u"pk=%s car=%s driver=%s" % (str(self.pk), self.car, self.driver)
View
22 tests/regressiontests/m2m_through_regress/tests.py
@@ -7,7 +7,8 @@
from django.contrib.auth.models import User
from django.test import TestCase
-from models import Person, Group, Membership, UserMembership
+from models import (Person, Group, Membership, UserMembership,
+ Car, Driver, CarDriver)
class M2MThroughTestCase(TestCase):
@@ -118,6 +119,25 @@ def test_join_trimming(self):
]
)
+
+class ToFieldThroughTests(TestCase):
+ def setUp(self):
+ self.car = Car.objects.create(make="Toyota")
+ self.driver = Driver.objects.create(name="Ryan Briscoe")
+ CarDriver.objects.create(car=self.car, driver=self.driver)
+
+ def test_to_field(self):
+ self.assertQuerysetEqual(
+ self.car.drivers.all(),
+ ["<Driver: Ryan Briscoe>"]
+ )
+
+ def test_to_field_reverse(self):
+ self.assertQuerysetEqual(
+ self.driver.car_set.all(),
+ ["<Car: Toyota>"]
+ )
+
class ThroughLoadDataTestCase(TestCase):
fixtures = ["m2m_through"]
Please sign in to comment.
Something went wrong with that request. Please try again.