Fixed #23862 -- Made ManyToManyRel.get_related_field() respect to_field.

This commit is contained in:
Simon Charette 2014-11-16 17:21:33 +01:00 committed by Tim Graham
parent a7c3d0f288
commit c7087bc777
3 changed files with 62 additions and 5 deletions

View File

@ -1348,11 +1348,18 @@ class ManyToManyRel(object):
def get_related_field(self): def get_related_field(self):
""" """
Returns the field in the to' object to which this relationship is tied Returns the field in the 'to' object to which this relationship is tied.
(this is always the primary key on the target model). Provided for Provided for symmetry with ManyToOneRel.
symmetry with ManyToOneRel.
""" """
return self.to._meta.pk opts = self.through._meta
if self.through_fields:
field = opts.get_field(self.through_fields[0])
else:
for field in opts.fields:
rel = getattr(field, 'rel', None)
if rel and rel.to == self.to:
break
return field.foreign_related_fields[0]
class ForeignObject(RelatedField): class ForeignObject(RelatedField):

View File

@ -113,3 +113,25 @@ class Relationship(models.Model):
another = models.ForeignKey(Employee, related_name="rel_another_set", null=True) another = models.ForeignKey(Employee, related_name="rel_another_set", null=True)
target = models.ForeignKey(Employee, related_name="rel_target_set") target = models.ForeignKey(Employee, related_name="rel_target_set")
source = models.ForeignKey(Employee, related_name="rel_source_set") source = models.ForeignKey(Employee, related_name="rel_source_set")
class Ingredient(models.Model):
iname = models.CharField(max_length=20, unique=True)
class Meta:
ordering = ('iname',)
class Recipe(models.Model):
rname = models.CharField(max_length=20, unique=True)
ingredients = models.ManyToManyField(
Ingredient, through='RecipeIngredient', related_name='recipes',
)
class Meta:
ordering = ('rname',)
class RecipeIngredient(models.Model):
ingredient = models.ForeignKey(Ingredient, to_field='iname')
recipe = models.ForeignKey(Recipe, to_field='rname')

View File

@ -6,7 +6,8 @@ from operator import attrgetter
from django.test import TestCase from django.test import TestCase
from .models import (Person, Group, Membership, CustomMembership, from .models import (Person, Group, Membership, CustomMembership,
PersonSelfRefM2M, Friendship, Event, Invitation, Employee, Relationship) PersonSelfRefM2M, Friendship, Event, Invitation, Employee, Relationship,
Ingredient, Recipe, RecipeIngredient)
class M2mThroughTests(TestCase): class M2mThroughTests(TestCase):
@ -426,3 +427,30 @@ class M2mThroughReferentialTests(TestCase):
['peter', 'mary', 'harry'], ['peter', 'mary', 'harry'],
attrgetter('name') attrgetter('name')
) )
class M2mThroughToFieldsTests(TestCase):
def setUp(self):
self.pea = Ingredient.objects.create(iname='pea')
self.potato = Ingredient.objects.create(iname='potato')
self.tomato = Ingredient.objects.create(iname='tomato')
self.curry = Recipe.objects.create(rname='curry')
RecipeIngredient.objects.create(recipe=self.curry, ingredient=self.potato)
RecipeIngredient.objects.create(recipe=self.curry, ingredient=self.pea)
RecipeIngredient.objects.create(recipe=self.curry, ingredient=self.tomato)
def test_retrieval(self):
# Forward retrieval
self.assertQuerysetEqual(
self.curry.ingredients.all(),
[self.pea, self.potato, self.tomato], lambda x: x
)
# Backward retrieval
self.assertEqual(self.tomato.recipes.get(), self.curry)
def test_choices(self):
field = Recipe._meta.get_field('ingredients')
self.assertEqual(
[choice[0] for choice in field.get_choices(include_blank=False)],
['pea', 'potato', 'tomato']
)