Django 信号 - kwargs['update_fields'] 在通过 django admin 进行模型更新时始终为 None

Nel*_*ing 7 django django-signals django-admin

我的 django 应用程序中有一个信号,我想检查模型中的某个字段是否已更新,以便我可以继续做一些事情。

我的模型看起来像这样......

class Product(models.Model):
    name = models.CharField(max_length=100)
    price = models.PositiveIntegerField()
    tax_rate = models.PositiveIntegerField()
    display_price = models.PositiveInteger()
    inputed_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL)
    updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL)
Run Code Online (Sandbox Code Playgroud)

我的信号是这样的...

@receiver(post_save, sender=Product)
def update_model(sender, **kwargs):
    instance = kwargs['instance']
    if 'tax_rate' in kwargs['update_fields']:
        # do something
Run Code Online (Sandbox Code Playgroud)

这将返回错误None不是可迭代的。我已经阅读了关于 django 信号的文档update_fields,它说The set of fields to update as passed to Model.save(), or None if update_fields wasn’t passed to save().

我应该提到我在 django admin 中工作,所以我希望发生的是,我可以在 django admin 中创建我的 Product 模型的实例,然后如果 tax_rate 或 price 的值更新,我可以检查这些和相应地更新list_price。但是,kwargs['update_fields']始终返回 None。我怎么了?还是有其他方法可以在 django admin 中实现该结果?

更新部分

现在,假设我inputed_by在我的产品模型中引入了一个名为的字段,它指向用户模型,并且我希望在第一次保存模型时填充该字段。然后是另一个字段updated_by,用于存储上次更新模型的用户。同时,我希望检查tax_rateprice是否已更新。

在我的模型管理员中,我有以下方法...

def save_model(self, request, obj, form, change):
    update_fields = []
    if not obj.pk:
        obj.inputed_by = request.user
    elif change:
        obj.updated_by = request.user

        if form.initial['tax_rate'] != form.cleaned_data['tax_rate']:
            update_fields.append('tax_rate')
        if form.initial['price'] != form.cleaned_data['price']:
            update_fields.append('price')

    obj.save(update_fields=update_fields)
    super().save_model(request, obj, form, change)
Run Code Online (Sandbox Code Playgroud)

我的信号现在看起来像这样......

@receiver(post_save, sender=Product, dispatch_uid="update_display_price")
def update_display_price(sender, **kwargs):
    created = kwargs['created']
    instance = kwargs['instance']
    updated = kwargs['update_fields']
    checklist = ['tax_rate', 'price']

    # Prints out the frozenset containing the updated fields and then below that `The update_fields is None`

    print(f'The update_fields is {updated}')

    if created:
        instance.display_price = instance.price+instance.tax_rate
        instance.save()
    elif set(checklist).issubset(updated):
        instance.display_price = instance.price+instance.tax_rate
        instance.save() 
Run Code Online (Sandbox Code Playgroud)

我收到错误'NoneType' object is not iterable 错误似乎来自行set(checklist).issubset(updated)。我试过在 python shell 中专门运行该行,它产生了所需的结果。这次怎么了?

Dav*_*yan 12

应将字段集传递Model.save()给以使其在update_fields.

像这样

model.save(update_fields=['tax_rate'])
Run Code Online (Sandbox Code Playgroud)

如果您正在从 django admin 创建某些内容并始终获取,None则表示该update_fields内容尚未传递给模型的save方法。正因为如此,它将永远是None

如果您检查ModelAdmin类和save_model方法,您会看到调用发生时没有update_fields关键字参数。

在此处输入图片说明

如果您编写自己的save_model.

下面的代码将解决您的问题:

class ProductAdmin(admin.ModelAdmin):
    ...
    def save_model(self, request, obj, form, change):
        update_fields = []

        # True if something changed in model
        # Note that change is False at the very first time
        if change: 
            if form.initial['tax_rate'] != form.cleaned_data['tax_rate']:
                update_fields.append('tax_rate')

        obj.save(update_fields=update_fields)
Run Code Online (Sandbox Code Playgroud)

现在,您将能够测试update_model.


Ben*_*gee 5

添加到 Davit Tovmasyan 的帖子中。我制作了一个更通用的版本,它使用 for 循环涵盖了任何字段更改:

class ProductAdmin(admin.ModelAdmin): 
    ...
    def save_model(self, request, obj, form, change):
        update_fields = []
        for key, value in form.cleaned_data.items():
            # True if something changed in model
            if value != form.initial[key]:
                update_fields.append(key)

        obj.save(update_fields=update_fields)
Run Code Online (Sandbox Code Playgroud)

编辑:警告这实际上不是一个完整的解决方案。似乎不适用于对象创建,只能更改。我会尽快找出完整的解决方案。