考虑到在 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()
保存的,但我不知道在这种情况下如何应用它。
在保存两个实例之前,无法创建
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()