diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index 3c022d6fccef48202747efd91c81ba255caec2c8..d6cc3262b19911617892f80ac683531c435f9797 100644
a
|
b
|
class OneToOneRel(ManyToOneRel):
|
1243 | 1243 | |
1244 | 1244 | class ManyToManyRel(object): |
1245 | 1245 | def __init__(self, to, related_name=None, limit_choices_to=None, |
1246 | | symmetrical=True, through=None, db_constraint=True, related_query_name=None): |
| 1246 | symmetrical=True, through=None, through_fields=None, |
| 1247 | db_constraint=True, related_query_name=None): |
1247 | 1248 | if through and not db_constraint: |
1248 | 1249 | raise ValueError("Can't supply a through model and db_constraint=False") |
| 1250 | if through_fields and not through: |
| 1251 | raise ValueError("Can't specify through_fields without a through model") |
1249 | 1252 | self.to = to |
1250 | 1253 | self.related_name = related_name |
1251 | 1254 | self.related_query_name = related_query_name |
… |
… |
class ManyToManyRel(object):
|
1255 | 1258 | self.symmetrical = symmetrical |
1256 | 1259 | self.multiple = True |
1257 | 1260 | self.through = through |
| 1261 | self.through_fields = through_fields |
1258 | 1262 | self.db_constraint = db_constraint |
1259 | 1263 | |
1260 | 1264 | def is_hidden(self): |
… |
… |
class ManyToManyField(RelatedField):
|
1844 | 1848 | limit_choices_to=kwargs.pop('limit_choices_to', None), |
1845 | 1849 | symmetrical=kwargs.pop('symmetrical', to == RECURSIVE_RELATIONSHIP_CONSTANT), |
1846 | 1850 | through=kwargs.pop('through', None), |
| 1851 | through_fields=kwargs.pop('through_fields', None), |
1847 | 1852 | db_constraint=db_constraint, |
1848 | 1853 | ) |
1849 | 1854 | |
… |
… |
class ManyToManyField(RelatedField):
|
1941 | 1946 | for field in self.rel.through._meta.fields) |
1942 | 1947 | seen_to = sum(to_model == getattr(field.rel, 'to', None) |
1943 | 1948 | for field in self.rel.through._meta.fields) |
| 1949 | through_fields = self.rel.through_fields |
1944 | 1950 | |
1945 | | if seen_from > 1: |
| 1951 | if seen_from > 1 and not (through_fields and through_fields[0]): |
1946 | 1952 | errors.append( |
1947 | 1953 | checks.Error( |
1948 | 1954 | ('The model is used as an intermediary model by ' |
… |
… |
class ManyToManyField(RelatedField):
|
1956 | 1962 | ) |
1957 | 1963 | ) |
1958 | 1964 | |
1959 | | if seen_to > 1: |
| 1965 | if seen_to > 1 and not (through_fields and through_fields[1]): |
1960 | 1966 | errors.append( |
1961 | 1967 | checks.Error( |
1962 | 1968 | ('The model is used as an intermediary model by ' |
… |
… |
class ManyToManyField(RelatedField):
|
2054 | 2060 | cache_attr = '_m2m_%s_cache' % attr |
2055 | 2061 | if hasattr(self, cache_attr): |
2056 | 2062 | return getattr(self, cache_attr) |
| 2063 | source_field = self.rel.through_fields[0] if self.rel.through_fields else None |
2057 | 2064 | for f in self.rel.through._meta.fields: |
2058 | | if hasattr(f, 'rel') and f.rel and f.rel.to == related.model: |
| 2065 | if hasattr(f, 'rel') and f.rel and f.rel.to == related.model and ( |
| 2066 | source_field is None or source_field == f.name): |
2059 | 2067 | setattr(self, cache_attr, getattr(f, attr)) |
2060 | 2068 | return getattr(self, cache_attr) |
2061 | 2069 | |
… |
… |
class ManyToManyField(RelatedField):
|
2065 | 2073 | if hasattr(self, cache_attr): |
2066 | 2074 | return getattr(self, cache_attr) |
2067 | 2075 | found = False |
| 2076 | target_field = self.rel.through_fields[1] if self.rel.through_fields else None |
2068 | 2077 | for f in self.rel.through._meta.fields: |
2069 | 2078 | if hasattr(f, 'rel') and f.rel and f.rel.to == related.parent_model: |
2070 | | if related.model == related.parent_model: |
| 2079 | if target_field is None and related.model == related.parent_model: |
2071 | 2080 | # If this is an m2m-intermediate to self, |
2072 | 2081 | # the first foreign key you find will be |
2073 | 2082 | # the source column. Keep searching for |
… |
… |
class ManyToManyField(RelatedField):
|
2077 | 2086 | break |
2078 | 2087 | else: |
2079 | 2088 | found = True |
2080 | | else: |
| 2089 | elif target_field is None or target_field == f.name: |
2081 | 2090 | setattr(self, cache_attr, getattr(f, attr)) |
2082 | 2091 | break |
2083 | 2092 | return getattr(self, cache_attr) |