实施多人关系

时间:2016-01-11 19:43:07

标签: django django-models

我做了类似Facebook的模型,但我希望Personne多个链接与另一个Personne

我有一个带有自定义PersonneRelation方法的中间表save。这个想法是:当我向一个人添加一个关系时,我想以另一种方式创建另一个关系。问题是,如果我尝试保存在save方法中,那么它就是一个递归调用。所以我的想法是创建一个类的变量,并在我想避免递归时将其设置为True。

以下是我的表现:

class Personne(models.Model):
    user = models.OneToOneField(User)
    relations = models.ManyToManyField('self', through='PersonneRelation',
                                       symmetrical=False)


class PersonneRelation(models.Model):
    is_saving = False
    # TAB_TYPES omitted for brevity
    type_relation = models.CharField(max_length=1,
                                     choices=[(a, b) for a, b in
                                              list(TAB_TYPES.items())],
                                     default=TYPE_FRIEND)
    src = models.ForeignKey('Personne', related_name='src')
    dst = models.ForeignKey('Personne', related_name='dst')
    opposite = models.ForeignKey('PersonneRelation',
                                 null=True, blank=True, default=None)
    def save(self, *args, **kwargs):
        if self.is_saving:
            return super(PersonneRelation, self).save(args, kwargs)
        old = None
        if self.pk and self.opposite:
            old = self.type_relation
        retour = super(PersonneRelation, self).save(args, kwargs)
        if old:
            PersonneRelation.objects.filter(
                src=self.dst, dst=self.src, opposite=self, type_relation=old
            ).update(type_relation=self.type_relation)
        if self.opposite is None:
            self.opposite = PersonneRelation(
                src=self.dst, dst=self.src, opposite=self,
                type_relation=self.type_relation, is_reverse=True)
            self.opposite.save()
            self.is_saving = True
            self.save()
            self.is_saving = False
        return retour

我的问题是:这样做是否安全(使用类变量is_saving)(我不知道Python如何处理这些变量)?如果没有,为什么?我觉得这样不好,那么实现多个多对多关系的其他可能性是什么呢?

3 个答案:

答案 0 :(得分:0)

不幸的是,它不安全,因为它不是线程安全的。当两个同时发生的Django线程试图保存模型时,行为可能无法预测。

如果您想要更可靠的锁定,请查看Redis locking

但说实话,我会尝试使用简单的反向关系来实现它,可能会将复杂性包含在ModelManager中。

答案 1 :(得分:0)

以下是我修改它的方法:我完全删除了save方法并使用post_save消息进行检查:

  • 如果它是创建没有相反的一面,我在这里创建了与创建的相反的一面(我可以在这里没有任何问题!)然后我更新用#创建的那个34;相反"
  • 如果没有创建,这是一个更新,所以只需确保相反的一面也被更改。

我这样做是因为我几乎从不需要改变人与人之间的关系,而当我创造新的关系时,不会有任何可能的竞争条件,因为我会创造新关系的背景< / p>

@receiver(post_save, sender=PersonneRelation)
def signal_receiver(sender, **kwargs):
    created = kwargs['created']
    obj = kwargs['instance']
    if created and not obj.opposite:
        opposite = PersonneRelation(
            src=obj.dst, dst=obj.src, opposite=obj,
            type_relation=obj.type_relation, is_reverse=True)
        opposite.save()
        obj.opposite = opposite
        obj.save()
    elif not created and obj.type_relation != obj.opposite.type_relation:
        obj.opposite.type_relation = obj.type_relation
        obj.opposite.save()

答案 2 :(得分:0)

如果我理解你的代码,那么:

  1. Django会自动在两端提供关系,因此您可以通过srcPersonne dst转到Personne PersonneRelation并反转{{ 1}} - &gt;代码中的dst。因此,src中无需额外的opposite字段。

  2. 如果您需要同时具有对称和不对称的实现,即PersonneRelation - > src,但不是dst - &gt; dst表示特定记录,然后我建议添加布尔字段:

    src

  3. 通过这种方式,您可以在访问代码中的关系时检查class PersonneRelation(models.Model): symmetrical = models.BooleanField(default=False)symmetrical,以确定它是True - &gt; scrdstsrc - &gt; dstdst - &gt; src。在Facebook术语中:如果symmetricalFalse,则srcdst的订阅者,如果True,则srcdst之间会有is_saving pk。您可能希望定义自定义管理器以填充此行为,尽管它是更高级的主题。

    1. 如果您需要检查模型实例是否正在保存或更新,则None布尔字段中不需要。由于您使用的是自动主键字段,因此您只需检查模型实例上的pk是否为None。在Django之前,模型实例首次保存到DB('created')pkpk,当实例'更新'时(它之前已经从DB读取并且现在正在保存)某些字段值已更改)DB已设置为Save的{​​{1}}值。这是Django ORM决定是否应该更新或创建新记录的方式。

    2. 一般情况下,在模型上重新定义pre_save方法或使用post_save / ga_dispatchPeriod等信号时,您在其上定义的那些函数可能不是在某些情况下由Django调用,即模型大量更新时。有关详细信息,请参阅Django文档。