5

假设我有这样的模型

class Event(models.Model)
    users_count = models.IntegerField(default=0)
    users = models.ManyToManyField(User)

如果事件添加/删除一些用户,您会如何建议更新 users_count 值?

4

3 回答 3

8

如果可能的话,您可以引入Participation将加入事件和用户的模型:

class Participation(models.Model):
    user = models.ForeignKey(User)
    event = models.ForeignKey(Event)

class Event(models.Model):
    users = models.ManyToManyField(User, through='Participation')

并处理发送的 pre_save 信号Participation以更新instance.event计数。它将显着简化 m2m 的处理。在大多数情况下,后来证明某些逻辑和数据最适合中间模型。如果这不是您的情况,请尝试自定义解决方案(无论如何,您不应该有很多代码路径将用户添加到事件)。

于 2009-12-24T22:26:28.630 回答
4

我使用内置信号django.db.models.signals.m2m_changed解决了这个问题。

就我而言,每次 ManyToMany 更改时,我都必须更新另一个模型的相关实例,并且如您所知,覆盖 Model.save() 不起作用。

这是我的(法语和简化)模型:

class BaseSupport(EuidModel):
    nom = models.CharField(max_length=100, blank=True)
    periodicite = models.CharField('périodicité', max_length=16,
                                   choices=PERIODICITE_CHOICES)
    jours_de_parution_semaine = models.ManyToManyField('JourDeLaSemaine', blank=True)

    class Meta:
        abstract = True


class Support(BaseSupport):
    pass

    def save(self, *args, **kwargs):
        create_cahier_principal = False
        if not self.pk:
            create_cahier_principal = True
        super(Support, self).save(*args, **kwargs) 
        if create_cahier_principal:
            c = Cahier.objects.create(support=self,ordre=1, numero=1,
                                      nom=self.nom, nom_court=self.nom_court,
                                      euid=self.euid, periodicite=self.periodicite)



class Cahier(BaseSupport):
    """Ex : Cahier Saumon du Figaro Quotidien."""
    support = models.ForeignKey('Support', related_name='cahiers')
    ordre = models.PositiveSmallIntegerField()
    numero = models.PositiveSmallIntegerField(u'numéro', null=True, blank=True)


def sync_m2m_cahier_principal(sender, **kwargs):
    if kwargs['action'] not in ('post_add', 'post_clear', 'post_remove'):
        return
    support = kwargs['instance']
    cahier_principal = support.cahiers.get(euid=support.euid)
    cahier_principal.jours_de_parution_semaine.clear()
    if kwargs['action'] == 'post_clear':
        return 
    for jour in support.jours_de_parution_semaine.all():
        cahier_principal.jours_de_parution_semaine.add(jour)
m2m_changed.connect(sync_m2m_cahier_principal,
                    sender=Support.jours_de_parution_semaine.through)

也许这个解决方案远非理想,但我讨厌猴子修补 Django !

于 2011-05-18T16:02:47.100 回答
2

覆盖 save() 可能对您没有帮助,因为对 M2M 的更新不是原子的,而是在保存 Event 实例之后发生的(我没有研究过 delete() 语义,但它们可能相似)。这在另一个线程中讨论过。

人们正在谈论和研究这个问题。到目前为止,我看到的最好的解决方案是gregoirecachet 的这个 MonkeyPatch。我不知道这是否会进入1.2。可能不是因为发布经理 (James Bennett) 试图让人们尊重冻结日期(一个主要的日期刚刚过去)。

于 2009-12-24T15:57:10.100 回答