[1.2.X] Fixed #15161 - Corrected handling of ManyToManyField with through 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
This commit is contained in:
parent
5b8c593053
commit
10b4d93f50
@ -131,6 +131,12 @@ class GenericRelation(RelatedField, Field):
|
|||||||
def m2m_reverse_name(self):
|
def m2m_reverse_name(self):
|
||||||
return self.rel.to._meta.pk.column
|
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):
|
def contribute_to_class(self, cls, name):
|
||||||
super(GenericRelation, self).contribute_to_class(cls, name)
|
super(GenericRelation, self).contribute_to_class(cls, name)
|
||||||
|
|
||||||
|
@ -1144,6 +1144,11 @@ class ManyToManyField(RelatedField, Field):
|
|||||||
self.m2m_field_name = curry(self._get_m2m_attr, related, 'name')
|
self.m2m_field_name = curry(self._get_m2m_attr, related, 'name')
|
||||||
self.m2m_reverse_field_name = curry(self._get_m2m_reverse_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):
|
def set_attributes_from_rel(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -1283,12 +1283,14 @@ class Query(object):
|
|||||||
to_col2, opts, target) = cached_data
|
to_col2, opts, target) = cached_data
|
||||||
else:
|
else:
|
||||||
table1 = field.m2m_db_table()
|
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()
|
to_col1 = field.m2m_column_name()
|
||||||
opts = field.rel.to._meta
|
opts = field.rel.to._meta
|
||||||
table2 = opts.db_table
|
table2 = opts.db_table
|
||||||
from_col2 = field.m2m_reverse_name()
|
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
|
target = opts.pk
|
||||||
orig_opts._join_cache[name] = (table1, from_col1,
|
orig_opts._join_cache[name] = (table1, from_col1,
|
||||||
to_col1, table2, from_col2, to_col2, opts,
|
to_col1, table2, from_col2, to_col2, opts,
|
||||||
@ -1336,12 +1338,14 @@ class Query(object):
|
|||||||
to_col2, opts, target) = cached_data
|
to_col2, opts, target) = cached_data
|
||||||
else:
|
else:
|
||||||
table1 = field.m2m_db_table()
|
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()
|
to_col1 = field.m2m_reverse_name()
|
||||||
opts = orig_field.opts
|
opts = orig_field.opts
|
||||||
table2 = opts.db_table
|
table2 = opts.db_table
|
||||||
from_col2 = field.m2m_column_name()
|
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
|
target = opts.pk
|
||||||
orig_opts._join_cache[name] = (table1, from_col1,
|
orig_opts._join_cache[name] = (table1, from_col1,
|
||||||
to_col1, table2, from_col2, to_col2, opts,
|
to_col1, table2, from_col2, to_col2, opts,
|
||||||
|
@ -53,3 +53,25 @@ class Through(ThroughBase):
|
|||||||
class B(models.Model):
|
class B(models.Model):
|
||||||
b_text = models.CharField(max_length=20)
|
b_text = models.CharField(max_length=20)
|
||||||
a_list = models.ManyToManyField(A, through=Through)
|
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)
|
||||||
|
@ -7,7 +7,8 @@ from django.core import management
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.test import TestCase
|
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):
|
class M2MThroughTestCase(TestCase):
|
||||||
@ -118,6 +119,25 @@ class M2MThroughTestCase(TestCase):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
class ThroughLoadDataTestCase(TestCase):
|
||||||
fixtures = ["m2m_through"]
|
fixtures = ["m2m_through"]
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user