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

xriantvc  于 5个月前  发布在  Python
关注(0)|答案(1)|浏览(44)

给定(简化)模型,在Django Admin应用程序中使用:

models.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
<django.db.models.fields.related_descriptors.create_forward_many_to_many_manager..ManyRelatedManager object at 0x7f6f875209a0>
self.pianos.all()返回一个空的queryset,即使钢琴显示在提交以启动保存的模板表单中。

然而,如果它是对现有Appointment的更新,'pianos'将按预期返回数据。

显然,当save()被调用时,ManyToMany字段不会立即保存到数据库中。那么我如何才能访问如下所示的数据呢?请注意,“钢琴”在这里没有示例化,它们已经存在于数据库中,并且只需要Appointment指向其m2m字段中的一个或多个钢琴,就像从admin.py中定义的horizontal_filter定向的那样。
我还尝试了使用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_保存:

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


但是这个信号没有被接收到。
文档说ManyToMany字段是使用add()而不是save()保存的,但我不知道在这种情况下如何应用。

4bbkushb

4bbkushb1#

在两个示例都保存之前不能创建ManyToMany关联,这就是为什么当您在Appointment.save() method中时还没有创建它的原因。
Form.save()方法(或Save按钮,如果您使用Django管理界面)保存Appointment然后使用.add()将其与Piano示例关联。在这种情况下,您可以使用@IainShelvington建议的m2m_changed信号:
当模型示例上的ManyToManyField发生更改时发送。严格来说,这不是模型信号,因为它是由ManyToManyField发送的,但由于它在跟踪模型更改时补充了pre_save/post_savepre_delete/post_delete,因此包含在这里。
请注意,此信号的sender将不再是Appointment。您应该使用文档中提到的Appointment.pianos.through
与此信号一起发送的参数:
sender
描述ManyToManyField的中间模型类。这个类在定义多对多字段时自动创建;您可以使用多对多字段上的through属性访问它。
一个简单的例子是:

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

字符串
OP的其他评论:
为了方便以后的读者,我们先将action设置为pre_add,然后第二个信号到达,action设置为post_add。只有在这个时候,本例中的数据才可以通过Appointment.pianos.all()获得。

相关问题