Сигналы Django - kwargs ['update_fields'] всегда отсутствует при обновлении модели через администратора django - PullRequest
0 голосов
/ 07 февраля 2019

У меня есть сигнал в моем приложении 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)

Мой сигнал выглядит следующим образом ...

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

Это возвращает ошибку 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, поэтому я надеялся, что я смогу создать экземпляр своегоМодель продукта в django admin, а затем, если значение tax_rate или цена были обновлены, я могу проверить их и соответственно обновить list_price.Однако kwargs['update_fields'] всегда возвращает None.Что я делаю не так?Или есть какой-то другой способ добиться этого результата внутри администратора django?

Обновлен раздел

Теперь, скажем, я ввел поле под названием inputed_by в моей модели продукта., это указывает на пользовательскую модель, и я хочу, чтобы это поле заполнялось при первом сохранении модели.Затем другое поле updated_by, в котором хранится пользователь, который последний раз обновлял модель.В то же время я хочу проверить, был ли обновлен tax_rate или price.

Внутри администратора моей модели у меня есть следующий метод ...

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)

Мой сигнал теперь выглядит следующим образом ...

@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() 

Я получаю ошибку 'NoneType' object is not iterable Ошибка, кажется, исходит от линии set(checklist).issubset(updated).Я попытался запустить эту строку специально внутри оболочки Python, и она дает желаемые результаты.Что не так в этот раз?

Ответы [ 2 ]

0 голосов
/ 25 мая 2019

Добавить к сообщению Давида Товмасяна.Я сделал более универсальную версию, которая охватывает любое изменение поля, используя цикл 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)

РЕДАКТИРОВАТЬ: ПРЕДУПРЕЖДЕНИЕ Это на самом деле не полное решение.Кажется, не работает для создания объекта, только изменения.Я постараюсь выяснить полное решение в ближайшее время.

0 голосов
/ 07 февраля 2019

Набор полей должен быть передан в Model.save(), чтобы сделать их доступными в update_fields.

Как это

model.save(update_fields=['tax_rate'])

Если вы создаете что-то из администратора django и получаетевсегда None это означает, что update_fields не передан методу save модели.И поэтому всегда будет None.

Если вы проверите класс ModelAdmin и метод save_model, вы увидите, что вызов происходит без аргумента ключевого слова update_fields.

enter image description here

Это будет работать, если вы напишите свой собственный 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)

Теперь вы сможете проверить членство в update_model.

...