我做了类似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如何处理这些变量)?如果没有,为什么?我觉得这样不好,那么实现多个多对多关系的其他可能性是什么呢?
答案 0 :(得分:0)
不幸的是,它不安全,因为它不是线程安全的。当两个同时发生的Django线程试图保存模型时,行为可能无法预测。
如果您想要更可靠的锁定,请查看Redis locking。
但说实话,我会尝试使用简单的反向关系来实现它,可能会将复杂性包含在ModelManager
中。
答案 1 :(得分:0)
以下是我修改它的方法:我完全删除了save
方法并使用post_save
消息进行检查:
我这样做是因为我几乎从不需要改变人与人之间的关系,而当我创造新的关系时,不会有任何可能的竞争条件,因为我会创造新关系的背景< / 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)
如果我理解你的代码,那么:
Django会自动在两端提供关系,因此您可以通过src
从Personne
dst
转到Personne
PersonneRelation
并反转{{ 1}} - &gt;代码中的dst
。因此,src
中无需额外的opposite
字段。
如果您需要同时具有对称和不对称的实现,即PersonneRelation
- > src
,但不是dst
- &gt; dst
表示特定记录,然后我建议添加布尔字段:
src
通过这种方式,您可以在访问代码中的关系时检查class PersonneRelation(models.Model):
symmetrical = models.BooleanField(default=False)
是symmetrical
,以确定它是True
- &gt; scr
仅dst
或src
- &gt; dst
和dst
- &gt; src
。在Facebook术语中:如果symmetrical
为False
,则src
为dst
的订阅者,如果True
,则src
和dst
之间会有is_saving
pk
。您可能希望定义自定义管理器以填充此行为,尽管它是更高级的主题。
如果您需要检查模型实例是否正在保存或更新,则None
布尔字段中不需要。由于您使用的是自动主键字段,因此您只需检查模型实例上的pk
是否为None
。在Django之前,模型实例首次保存到DB('created')pk
是pk
,当实例'更新'时(它之前已经从DB读取并且现在正在保存)某些字段值已更改)DB
已设置为Save
的{{1}}值。这是Django ORM决定是否应该更新或创建新记录的方式。
一般情况下,在模型上重新定义pre_save
方法或使用post_save
/ ga_dispatchPeriod
等信号时,您在其上定义的那些函数可能不是在某些情况下由Django调用,即模型大量更新时。有关详细信息,请参阅Django文档。