给定在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 位于0x7f6f875209a0&gt的对象;

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

However,如果是对现有的Appointment进行更新,则‘pianos’会按预期返回数据.

显然,调用save()时,ManyToMany字段不会立即保存到数据库中.那么,我如何访问如下所示的数据呢?请注意,‘pianos’没有在这里实例化,它们已经存在于数据库中,并且只需要指定指向其M2M字段中的一个或多个,如admin.py中定义的horizontal_filter所指示的那样.

我也try 了另一种使用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)

Update:修改为捕获m2m_changed,而不是POST_SAVE:

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

但是这个信号没有被接收到.

The docs表示使用add()而不是save()保存了ManyToMany个字段,但我不明白在这种情况下如何应用这一点.

推荐答案

在保存两个实例之前,不能创建ManyToMany关联,这就是为什么您在Appointment.save() method中时还没有创建它的原因.

Form.save()方法(或者Save按钮,如果你使用的是Django管理界面)保存Appointmentthen使用.add()将其与Piano实例关联.在这种情况下,你可以使用@IainShelvington建议的m2m_changed signal:

在模型实例上更改ManyToManyField时发送.严格地说,这不是一个模型信号,因为它是由ManyToManyField发送的,但由于它在跟踪模型更改方面是对pre_save/post_savepre_delete/post_delete的补充,因此包含在这里.

请注意,此信号的sender将不再是Appointment.您应该使用docs中提到的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的其他 comments :

为了方便以后的读者,动作将首先是pre_add,然后第二个信号到达,动作是post_add.只有在这一点上,本例中的数据才可以通过:Appointment.pianos.all()获得.

Python相关问答推荐

如何将Matplotlib的fig.add_axes本地坐标与我的坐标关联起来?

如何计算列表列行之间的公共元素

优化在numpy数组中非零值周围创建缓冲区的函数的性能

Chatgpt API不断返回错误:404未能从API获取响应

即使在可见的情况下也不相互作用

如何使用symy打印方程?

将特定列信息移动到当前行下的新行

时间序列分解

如何比较numPy数组中的两个图像以获取它们不同的像素

如何使用Python将工作表从一个Excel工作簿复制粘贴到另一个工作簿?

_repr_html_实现自定义__getattr_时未显示

使可滚动框架在tkinter环境中看起来自然

基于字符串匹配条件合并两个帧

当从Docker的--env-file参数读取Python中的环境变量时,每个\n都会添加一个\'.如何没有额外的?

PyQt5,如何使每个对象的 colored颜色 不同?'

如何使用SentenceTransformers创建矢量嵌入?

导入错误:无法导入名称';操作';

无法在Spyder上的Pandas中将本地CSV转换为数据帧

有没有办法让Re.Sub报告它所做的每一次替换?

Python OPCUA,modbus通信代码运行3小时后出现RuntimeError