ManyToMany 字段何时保存到数据库?

问题描述 投票:0回答:1

考虑到在 Django 管理应用程序中使用的(简化的)模型:

模型.py

class Appointment(models.Model):
    customer = models.ForeignKey(
        Customer, blank=False, null=True, on_delete=models.CASCADE
    )
    pianos = models.ManyToManyField(Piano, blank=True)

    def save(self, *args, **kwargs): 
        # Is it a new Appointment, not an existing one being re-saved?
        newAppointment = self.id is None
        try:
            super().save(*args, **kwargs)  # Call the "real" save() method.
            if newAppointment:
                # Returns empty queryset
                for piano in self.pianos.all():
                    print(piano)
        except:
            pass

我想访问“钢琴”。如果是新创建的约会,

self.pianos
回归

self.pianos.all()
返回一个空查询集,即使钢琴显示在提交以启动保存的模板表单中。

但是,如果是现有

Appointment
的更新,“pianos”会按预期返回数据。

显然,当调用

ManyToMany
时,
save()
字段不会立即保存到数据库中。那么如何访问如下所示的数据呢?请注意,“钢琴”在这里并未实例化,它们已经存在于数据库中,并且只需要 Appointment 指向其 m2m 字段中的一个或多个,如
horizontal_filter
中定义的
admin.py
所指示。

我还尝试了使用

post_save
信号的替代方法,结果完全相同:

@receiver(signals.post_save, sender=Appointment)
def createServiceHistory(sender, instance, created, **kwargs):
    if created:
        for piano in instance.pianos.all():  #empty queryset
            print(piano)

更新:修改为捕获

m2m_changed
而不是post_save:

@receiver(signals.m2m_changed, sender=Appointment)
def createServiceHistory(sender, instance, action, **kwargs):
    print(action)

但是没有收到这个信号。

文档

ManyToMany
字段是使用
add()
而不是
save()
保存的,但我不知道在这种情况下如何应用它。

python django django-admin django-orm django-signals
1个回答
0
投票

在保存两个实例之前,无法创建

ManyToMany
关联,这就是为什么当您处于
Appointment.save() method
时尚未创建它。

Form.save()
方法(或
Save
按钮,如果您使用的是Django管理界面)保存
Appointment
然后使用
Piano
将其与
.add()
实例关联起来。在这种情况下,您可以按照 @IainShelvington 建议使用
m2m_changed
信号

当模型实例上的

ManyToManyField
发生更改时发送。严格来说,这不是模型信号,因为它是由
ManyToManyField
发送的,但由于它在跟踪模型更改时补充了
pre_save
/
post_save
pre_delete
/
post_delete
,因此它是包括在这里。

请注意,该信号的

sender
将不再是
Appointment
。您应该使用
Appointment.pianos.through
,如 docs 中所述:

使用此信号发送的参数:

sender

描述

ManyToManyField
的中间模型类。定义多对多字段时会自动创建该类;您可以使用多对多字段上的
through
属性来访问它。

一个简化的例子是:

@receiver(signals.m2m_changed, sender=Appointment.pianos.through)
def associateAppointmentWithPiano(sender, instance, action, **kwargs):
    print(f"{sender=} {instance=} {action=})

OP 的其他评论:

为了后来的读者的利益,动作将首先是

pre_add
,然后第二个信号到达,动作是
post_add
。仅在此时,才能通过以下方式获取本示例中的数据:
Appointment.pianos.all()

© www.soinside.com 2019 - 2024. All rights reserved.