mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #8279 -- Multiple many-to-many relations to "self" are now possible.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@8721 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
		| @@ -716,7 +716,7 @@ class OneToOneField(ForeignKey): | |||||||
|                 SingleRelatedObjectDescriptor(related)) |                 SingleRelatedObjectDescriptor(related)) | ||||||
|         if not cls._meta.one_to_one_field: |         if not cls._meta.one_to_one_field: | ||||||
|             cls._meta.one_to_one_field = self |             cls._meta.one_to_one_field = self | ||||||
|      |  | ||||||
|     def formfield(self, **kwargs): |     def formfield(self, **kwargs): | ||||||
|         if self.rel.parent_link: |         if self.rel.parent_link: | ||||||
|             return None |             return None | ||||||
| @@ -844,6 +844,15 @@ class ManyToManyField(RelatedField, Field): | |||||||
|         return smart_unicode(data) |         return smart_unicode(data) | ||||||
|  |  | ||||||
|     def contribute_to_class(self, cls, name): |     def contribute_to_class(self, cls, name): | ||||||
|  |         # To support multiple relations to self, it's useful to have a non-None | ||||||
|  |         # related name on symmetrical relations for internal reasons. The | ||||||
|  |         # concept doesn't make a lot of sense externally ("you want me to | ||||||
|  |         # specify *what* on my non-reversible relation?!"), so we set it up | ||||||
|  |         # automatically. The funky name reduces the chance of an accidental | ||||||
|  |         # clash. | ||||||
|  |         if self.rel.symmetrical and self.rel.related_name is None: | ||||||
|  |             self.rel.related_name = "%s_rel_+" % name | ||||||
|  |  | ||||||
|         super(ManyToManyField, self).contribute_to_class(cls, name) |         super(ManyToManyField, self).contribute_to_class(cls, name) | ||||||
|         # Add the descriptor for the m2m relation |         # Add the descriptor for the m2m relation | ||||||
|         setattr(cls, self.name, ReverseManyRelatedObjectsDescriptor(self)) |         setattr(cls, self.name, ReverseManyRelatedObjectsDescriptor(self)) | ||||||
|   | |||||||
							
								
								
									
										0
									
								
								tests/regressiontests/m2m_regress/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/regressiontests/m2m_regress/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										56
									
								
								tests/regressiontests/m2m_regress/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								tests/regressiontests/m2m_regress/models.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | |||||||
|  | from django.db import models | ||||||
|  |  | ||||||
|  | # No related name is needed here, since symmetrical relations are not | ||||||
|  | # explicitly reversible. | ||||||
|  | class SelfRefer(models.Model): | ||||||
|  |     name = models.CharField(max_length=10) | ||||||
|  |     references = models.ManyToManyField('self') | ||||||
|  |     related = models.ManyToManyField('self') | ||||||
|  |  | ||||||
|  |     def __unicode__(self): | ||||||
|  |         return self.name | ||||||
|  |  | ||||||
|  | class Tag(models.Model): | ||||||
|  |     name = models.CharField(max_length=10) | ||||||
|  |  | ||||||
|  |     def __unicode__(self): | ||||||
|  |         return self.name | ||||||
|  |  | ||||||
|  | # A related_name is required on one of the ManyToManyField entries here because | ||||||
|  | # they are both addressable as reverse relations from Tag. | ||||||
|  | class Entry(models.Model): | ||||||
|  |     name = models.CharField(max_length=10) | ||||||
|  |     topics = models.ManyToManyField(Tag) | ||||||
|  |     related = models.ManyToManyField(Tag, related_name="similar") | ||||||
|  |  | ||||||
|  |     def __unicode__(self): | ||||||
|  |         return self.name | ||||||
|  |  | ||||||
|  | __test__ = {"regressions": """ | ||||||
|  | # Multiple m2m references to the same model or a different model must be | ||||||
|  | # distinguished when accessing the relations through an instance attribute. | ||||||
|  |  | ||||||
|  | >>> s1 = SelfRefer.objects.create(name='s1') | ||||||
|  | >>> s2 = SelfRefer.objects.create(name='s2') | ||||||
|  | >>> s3 = SelfRefer.objects.create(name='s3') | ||||||
|  | >>> s1.references.add(s2) | ||||||
|  | >>> s1.related.add(s3) | ||||||
|  |  | ||||||
|  | >>> e1 = Entry.objects.create(name='e1') | ||||||
|  | >>> t1 = Tag.objects.create(name='t1') | ||||||
|  | >>> t2 = Tag.objects.create(name='t2') | ||||||
|  | >>> e1.topics.add(t1) | ||||||
|  | >>> e1.related.add(t2) | ||||||
|  |  | ||||||
|  | >>> s1.references.all() | ||||||
|  | [<SelfRefer: s2>] | ||||||
|  | >>> s1.related.all() | ||||||
|  | [<SelfRefer: s3>] | ||||||
|  |  | ||||||
|  | >>> e1.topics.all() | ||||||
|  | [<Tag: t1>] | ||||||
|  | >>> e1.related.all() | ||||||
|  | [<Tag: t2>] | ||||||
|  |  | ||||||
|  | """ | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user