mirror of
https://github.com/django/django.git
synced 2025-08-04 10:59:45 +00:00
Fixed #30421 -- Allowed symmetrical intermediate table for self-referential ManyToManyField.
This commit is contained in:
parent
a9179ab032
commit
87b1ad6e73
11 changed files with 167 additions and 90 deletions
|
@ -72,6 +72,7 @@ class TestNoDefaultsOrNulls(models.Model):
|
|||
class PersonSelfRefM2M(models.Model):
|
||||
name = models.CharField(max_length=5)
|
||||
friends = models.ManyToManyField('self', through="Friendship", symmetrical=False)
|
||||
sym_friends = models.ManyToManyField('self', through='SymmetricalFriendship', symmetrical=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -83,6 +84,12 @@ class Friendship(models.Model):
|
|||
date_friended = models.DateTimeField()
|
||||
|
||||
|
||||
class SymmetricalFriendship(models.Model):
|
||||
first = models.ForeignKey(PersonSelfRefM2M, models.CASCADE)
|
||||
second = models.ForeignKey(PersonSelfRefM2M, models.CASCADE, related_name='+')
|
||||
date_friended = models.DateField()
|
||||
|
||||
|
||||
# Custom through link fields
|
||||
class Event(models.Model):
|
||||
title = models.CharField(max_length=50)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from datetime import datetime
|
||||
from datetime import date, datetime
|
||||
from operator import attrgetter
|
||||
|
||||
from django.db import IntegrityError
|
||||
|
@ -7,7 +7,7 @@ from django.test import TestCase
|
|||
from .models import (
|
||||
CustomMembership, Employee, Event, Friendship, Group, Ingredient,
|
||||
Invitation, Membership, Person, PersonSelfRefM2M, Recipe, RecipeIngredient,
|
||||
Relationship,
|
||||
Relationship, SymmetricalFriendship,
|
||||
)
|
||||
|
||||
|
||||
|
@ -401,7 +401,7 @@ class M2mThroughReferentialTests(TestCase):
|
|||
attrgetter("name")
|
||||
)
|
||||
|
||||
def test_self_referential_symmetrical(self):
|
||||
def test_self_referential_non_symmetrical_both(self):
|
||||
tony = PersonSelfRefM2M.objects.create(name="Tony")
|
||||
chris = PersonSelfRefM2M.objects.create(name="Chris")
|
||||
Friendship.objects.create(
|
||||
|
@ -439,6 +439,71 @@ class M2mThroughReferentialTests(TestCase):
|
|||
attrgetter('name')
|
||||
)
|
||||
|
||||
def test_self_referential_symmetrical(self):
|
||||
tony = PersonSelfRefM2M.objects.create(name='Tony')
|
||||
chris = PersonSelfRefM2M.objects.create(name='Chris')
|
||||
SymmetricalFriendship.objects.create(
|
||||
first=tony, second=chris, date_friended=date.today(),
|
||||
)
|
||||
self.assertSequenceEqual(tony.sym_friends.all(), [chris])
|
||||
# Manually created symmetrical m2m relation doesn't add mirror entry
|
||||
# automatically.
|
||||
self.assertSequenceEqual(chris.sym_friends.all(), [])
|
||||
SymmetricalFriendship.objects.create(
|
||||
first=chris, second=tony, date_friended=date.today()
|
||||
)
|
||||
self.assertSequenceEqual(chris.sym_friends.all(), [tony])
|
||||
|
||||
def test_add_on_symmetrical_m2m_with_intermediate_model(self):
|
||||
tony = PersonSelfRefM2M.objects.create(name='Tony')
|
||||
chris = PersonSelfRefM2M.objects.create(name='Chris')
|
||||
date_friended = date(2017, 1, 3)
|
||||
tony.sym_friends.add(chris, through_defaults={'date_friended': date_friended})
|
||||
self.assertSequenceEqual(tony.sym_friends.all(), [chris])
|
||||
self.assertSequenceEqual(chris.sym_friends.all(), [tony])
|
||||
friendship = tony.symmetricalfriendship_set.get()
|
||||
self.assertEqual(friendship.date_friended, date_friended)
|
||||
|
||||
def test_set_on_symmetrical_m2m_with_intermediate_model(self):
|
||||
tony = PersonSelfRefM2M.objects.create(name='Tony')
|
||||
chris = PersonSelfRefM2M.objects.create(name='Chris')
|
||||
anne = PersonSelfRefM2M.objects.create(name='Anne')
|
||||
kate = PersonSelfRefM2M.objects.create(name='Kate')
|
||||
date_friended_add = date(2013, 1, 5)
|
||||
date_friended_set = date.today()
|
||||
tony.sym_friends.add(
|
||||
anne, chris,
|
||||
through_defaults={'date_friended': date_friended_add},
|
||||
)
|
||||
tony.sym_friends.set(
|
||||
[anne, kate],
|
||||
through_defaults={'date_friended': date_friended_set},
|
||||
)
|
||||
self.assertSequenceEqual(tony.sym_friends.all(), [anne, kate])
|
||||
self.assertSequenceEqual(anne.sym_friends.all(), [tony])
|
||||
self.assertSequenceEqual(kate.sym_friends.all(), [tony])
|
||||
self.assertEqual(
|
||||
kate.symmetricalfriendship_set.get().date_friended,
|
||||
date_friended_set,
|
||||
)
|
||||
# Date is preserved.
|
||||
self.assertEqual(
|
||||
anne.symmetricalfriendship_set.get().date_friended,
|
||||
date_friended_add,
|
||||
)
|
||||
# Recreate relationship.
|
||||
tony.sym_friends.set(
|
||||
[anne],
|
||||
clear=True,
|
||||
through_defaults={'date_friended': date_friended_set},
|
||||
)
|
||||
self.assertSequenceEqual(tony.sym_friends.all(), [anne])
|
||||
self.assertSequenceEqual(anne.sym_friends.all(), [tony])
|
||||
self.assertEqual(
|
||||
anne.symmetricalfriendship_set.get().date_friended,
|
||||
date_friended_set,
|
||||
)
|
||||
|
||||
|
||||
class M2mThroughToFieldsTests(TestCase):
|
||||
@classmethod
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue