我有这个型号:
class People(models.Model):
name = models.CharField(max_length=128, db_index=True)
friends = models.ManyToManyField('self')
所以friends
关系对称。所以如果你是我的朋友,我就是你的朋友。
我还希望所有朋友的朋友自动成为我的朋友。例如:
如果A和B是朋友(AB,BA)并且我们将新朋友C添加到B,则C也将自动添加到A(AB,BA,BC,CB,AC,CA)。如果我们从B中删除C,C将自动从A中删除。
我需要在普通的管理页面中使用它。提交表单时,对于ManyToManyField,Django首先调用clean()
,删除与当前实例相关的所有关系,然后add()
,添加来自表单的所有关系。
在使用此代码添加新关系时,我能够获得良好的行为(但在删除关系时不起作用):
def add_friends(sender, instance, action, reverse, model, pk_set, **kwargs):
if action == 'post_add':
if len(pk_set) > 1:
pk = pk_set.pop()
next = People.objects.get(pk=pk)
next.friends.add(*pk_set)
m2m_changed.connect(add_friends, sender=People.friends.through)
在搜索解决方案时,我很难创建无限循环。
答案 0 :(得分:1)
我终于找到了解决方案。问题是我需要清除当前组中的所有朋友关系,但是如果不进入无限循环的clear
信号,我就找不到办法。
因此,解决方案是绕过clear()
方法并直接使用管理器的delete()
方法。我最终对3个信号使用相同的方法。这是add_friends
函数的修改代码:
def add_friends(sender, instance, action, reverse, model, pk_set, **kwargs):
# On clear, clear all indirect relations of this instance
if action == 'pre_clear':
instance.friends.through.objects.filter(from_people__in=instance.friends.all()).delete()
# Delete all relations of the objects in the removed set
# (not just the ones related to the instance)
elif action == 'post_remove':
instance.friends.through.objects.filter(
Q(from_people__in=pk_set) | Q(to_people__in=pk_set)
).delete()
# Clear all relations of People moved from one group to another
elif action == 'pre_add' and pk_set:
if instance.pk in pk_set:
raise ValueError(_(u"You can't add self as a friend."))
instance.friends.through.objects.filter(
(Q(from_people__in=pk_set) & ~Q(to_people=instance.pk)) |
(Q(to_people__in=pk_set) & ~Q(from_people=instance.pk))
).delete()
# Add all indirect relations of this instance
elif action == 'post_add' and pk_set:
manager = instance.friends.through.objects
# Get all the pk pairs
pk_set.add(instance.pk)
pk_set.update(instance.friends.all().values_list('pk', flat=True))
pairs = set(permutations(pk_set, 2))
# Get the pairs already in the DB
vals = manager.values_list('from_people', 'to_people')
vals = vals.filter(from_people__in=pk_set, to_people__in=pk_set)
# Keep only pairs that are not in DB
pairs = pairs - set(vals)
# Add them
for from_pk, to_pk in pairs:
manager.create(from_people_id=from_pk, to_people_id=to_pk)
m2m_changed.connect(add_friends, sender=People.friends.through)