Ticket #14549: #14549-intermediary_m2m_disambiguation_fixed_tests.diff
File #14549-intermediary_m2m_disambiguation_fixed_tests.diff, 9.7 KB (added by , 13 years ago) |
---|
-
django/db/models/fields/related.py
998 998 if kwargs['rel'].through is not None: 999 999 assert self.db_table is None, "Cannot specify a db_table if an intermediary model is used." 1000 1000 1001 self.source_fk_name = kwargs.pop('source_fk_name', None) 1002 self.target_fk_name = kwargs.pop('target_fk_name', None) 1003 1001 1004 Field.__init__(self, **kwargs) 1002 1005 1003 1006 msg = _('Hold down "Control", or "Command" on a Mac, to select more than one.') … … 1023 1026 return getattr(self, cache_attr) 1024 1027 for f in self.rel.through._meta.fields: 1025 1028 if hasattr(f,'rel') and f.rel and f.rel.to == related.model: 1026 setattr(self, cache_attr, getattr(f, attr)) 1027 return getattr(self, cache_attr) 1029 if not self.source_fk_name or self.source_fk_name == f.name: 1030 setattr(self, cache_attr, getattr(f, attr)) 1031 return getattr(self, cache_attr) 1028 1032 1029 1033 def _get_m2m_reverse_attr(self, related, attr): 1030 1034 "Function that can be curried to provide the related accessor or DB column name for the m2m table" … … 1034 1038 found = False 1035 1039 for f in self.rel.through._meta.fields: 1036 1040 if hasattr(f,'rel') and f.rel and f.rel.to == related.parent_model: 1037 if related.model == related.parent_model:1041 if not self.target_fk_name and related.model == related.parent_model: 1038 1042 # If this is an m2m-intermediate to self, 1039 1043 # the first foreign key you find will be 1040 1044 # the source column. Keep searching for … … 1044 1048 break 1045 1049 else: 1046 1050 found = True 1047 el se:1051 elif not self.target_fk_name or self.target_fk_name == f.name: 1048 1052 setattr(self, cache_attr, getattr(f, attr)) 1049 1053 break 1050 1054 return getattr(self, cache_attr) -
django/core/management/validation.py
def get_validation_errors(outfile, app=None): 173 173 rel_to = getattr(inter_field.rel, 'to', None) 174 174 if from_model == to_model: # relation to self 175 175 if rel_to == from_model: 176 seen_self += 1 176 if (not getattr(f, 'source_fk_name', None) or f.source_fk_name == inter_field.name or 177 not getattr(f, 'target_fk_name', None) or f.target_fk_name == inter_field.name): 178 seen_self += 1 177 179 if seen_self > 2: 178 180 e.add(opts, "Intermediary model %s has more than " 179 "two foreign keys to %s , which is ambiguous"180 "and is not permitted." % (181 "two foreign keys to %s. Add a source_fk_name " 182 "and a target_fk_name arguments to the definition for '%s'." % ( 181 183 f.rel.through._meta.object_name, 182 from_model._meta.object_name 184 from_model._meta.object_name, 185 f.name 183 186 ) 184 187 ) 185 188 else: 186 if rel_to == from_model :189 if rel_to == from_model and (not getattr(f, 'source_fk_name', None) or f.source_fk_name == inter_field.name): 187 190 if seen_from: 188 191 e.add(opts, "Intermediary model %s has more " 189 "than one foreign key to %s , which is"190 "a mbiguous and is not permitted." % (192 "than one foreign key to %s. Add a source_fk_name " 193 "argument to the definition for '%s'." % ( 191 194 f.rel.through._meta.object_name, 192 from_model._meta.object_name 195 from_model._meta.object_name, 196 f.name 193 197 ) 194 198 ) 195 199 else: 196 200 seen_from = True 197 elif rel_to == to_model :201 elif rel_to == to_model and (not getattr(f, 'target_fk_name', None) or f.target_fk_name == inter_field.name): 198 202 if seen_to: 199 203 e.add(opts, "Intermediary model %s has more " 200 "than one foreign key to %s , which is"201 "a mbiguous and is not permitted." % (204 "than one foreign key to %s. Add a target_fk_name " 205 "argument to the definition for '%s'." % ( 202 206 f.rel.through._meta.object_name, 203 rel_to._meta.object_name 207 rel_to._meta.object_name, 208 f.name 204 209 ) 205 210 ) 206 211 else: -
docs/topics/db/models.txt
435 435 436 436 There are a few restrictions on the intermediate model: 437 437 438 * Your intermediate model must contain one - and *only* one - foreign key 439 to the target model (this would be ``Person`` in our example). If you 440 have more than one foreign key, a validation error will be raised. 441 442 * Your intermediate model must contain one - and *only* one - foreign key 443 to the source model (this would be ``Group`` in our example). If you 444 have more than one foreign key, a validation error will be raised. 438 * To automatically detect the target foreign key, your intermediate model 439 must contain one - and *only* one - foreign key to the target model (this 440 would be ``Person`` in our example). If you have more than one foreign 441 key, you will need to specify the field name by adding a 442 :attr:`~django.db.models.ManyToManyField.target_fk_name`. 443 444 * To automatically detect the source foreign key, your intermediate model 445 must contain one - and *only* one - foreign key to the source model (this 446 would be ``Group`` in our example). If you have more than one foreign key, 447 you will need to specify the field name by adding a 448 :attr:`~django.db.models.ManyToManyField.source_fk_name`. 445 449 446 450 * The only exception to this is a model which has a many-to-many 447 451 relationship to itself, through an intermediary model. In this 448 452 case, two foreign keys to the same model are permitted, but they 449 453 will be treated as the two (different) sides of the many-to-many 450 relation. 454 relation. If more than two foreign keys exist, or if you want to 455 explicitly specify which field is the target and which is the source, 456 you will need to specify the field names by adding a 457 :attr:`~django.db.models.ManyToManyField.target_fk_name` and a 458 :attr:`~django.db.models.ManyToManyField.source_fk_name`. 451 459 452 460 * When defining a many-to-many relationship from a model to 453 461 itself, using an intermediary model, you *must* use -
tests/modeltests/invalid_models/invalid_models/models.py
326 326 invalid_models.grouptwo: 'secondary' is a manually-defined m2m relation through model MembershipMissingFK, which does not have foreign keys to Group and GroupTwo 327 327 invalid_models.missingmanualm2mmodel: 'missing_m2m' specifies an m2m relation through model MissingM2MModel, which has not been installed 328 328 invalid_models.group: The model Group has two manually-defined m2m relations through the model Membership, which is not permitted. Please consider using an extra field on your intermediary model instead. 329 invalid_models.group: Intermediary model RelationshipDoubleFK has more than one foreign key to Person , which is ambiguous and is not permitted.329 invalid_models.group: Intermediary model RelationshipDoubleFK has more than one foreign key to Person. Add a target_fk_name argument to the definition for 'tertiary'. 330 330 invalid_models.personselfrefm2m: Many-to-many fields with intermediate tables cannot be symmetrical. 331 invalid_models.personselfrefm2m: Intermediary model RelationshipTripleFK has more than two foreign keys to PersonSelfRefM2M , which is ambiguous and is not permitted.331 invalid_models.personselfrefm2m: Intermediary model RelationshipTripleFK has more than two foreign keys to PersonSelfRefM2M. Add a source_fk_name and a target_fk_name arguments to the definition for 'too_many_friends'. 332 332 invalid_models.personselfrefm2mexplicit: Many-to-many fields with intermediate tables cannot be symmetrical. 333 333 invalid_models.abstractrelationmodel: 'fk1' has a relation with model AbstractModel, which has either not been installed or is abstract. 334 334 invalid_models.abstractrelationmodel: 'fk2' has an m2m relation with model AbstractModel, which has either not been installed or is abstract.