Как определить, какие элементы были изменены в поле django manytomany - PullRequest
0 голосов
/ 27 сентября 2019

У меня есть модель с ManyToManyField ( step ).Я хотел бы удалить объекты ( ImagePlus ), связанные с этим полем, если больше нет элемента step , ссылающегося на них.

Я пытался использовать pre_deleteсигнал, и это нормально работает для половины проблемы (я могу удалить указанный объект, если последний шаг ссылающийся на них удален).Тем не менее, я изо всех сил пытаюсь удалить объект ImagePlus , на который есть ссылка, если он отключен обновлением поля manytomany на шаге .

Я попытался решить его, перегрузивметод save или сигнал pre_save / post_save объекта step , но все изменения во множестве полей происходят либо до, либо после любого из них.

Я также попытался решить эту проблему, перехвативm2m_changed сигнал сквозной таблицы.

Еще раз я могу частично решить мою проблему.Если я добавляю / удаляю объекты через .add или через .remove в оболочке, я получаю правильный сигнал post_add / post_remove, который я могу обработать, как удалить.

Проблема здесь в том, что когда я пытаюсь изменитьсодержимое поля manytomany в админке Django или через ajax / DRF) Я всегда получаю 4 сигнала в следующем порядке: «pre_clear», «post_clear», «pre_add» и «post_add».Теперь я не могу использовать сигналы «pre_clear» или «post_clear», так как я не могу знать, был ли элемент повторно присоединен в данный момент через сигнал добавления.OTOH Я действительно не могу использовать сигналы pre_add или post_add, так как я не уверен, что они произойдут.(на самом деле я немного запутался, почему вместо этих 4 я не получаю ни одного сигнала 'post_remove' - это сильно облегчит жизнь).

Я попробовал этот взлом pre_clean / pre_add, как показано ниже, и этоработает разумно.Однако я не уверен в возможных побочных эффектах.По крайней мере, это кажется опасным, и он потерпит неудачу, если будет вызвана только очистка без добавления.

Я не совсем уверен, какие у меня варианты на данный момент.Сделать таймер, который удаляет объекты, на которые есть ссылки, если они не были присоединены через 3 секунды?Это было бы довольно неуклюже и могло бы вызвать несоответствия базы данных.

class Step(models.Model):
    instruments = ManyToManyField(ImagePlus, verbose_name="Instruments and materials", blank=True, related_name='instruments_step_set')
    previous_instruments = None

class ImagePlus(models.Model):
    image = ImageField(verbose_name="Single image", upload_to=CreateImageFilename('gallery/images'), null=True, blank=True)

@receiver(pre_delete, sender=Step)
def pre_delete_step(sender, instance, **kwargs):
    for instrument in instance.instruments.all():
        if instrument.instruments_step_set.count()==1:
            instrument.delete()

@receiver(m2m_changed, sender=Step.instruments.through)
def m2m_changed_step(sender, instance, action, **kwargs):
    def cleanup (set):
        for element in set:
            if Step.instruments.through.objects.filter(imageplus__id=element).count()==0:
                ImagePlus.objects.get(id=element).delete()  

    if action == 'pre_clear':
        instance.previous_instruments = set(instance.instruments.all().values_list('pk',flat=True))
        instance.previous_instruments_timer = Timer (3, cleanup, (instance.previous_instruments,)) 
        instance.previous_instruments_timer.start()

    if action == 'pre_add':
        pk_set = kwargs.pop('pk_set', set())
        deleted = instance.previous_instruments-pk_set
        instance.previous_instruments_timer.cancel()
        instance.previous_instruments = None
        cleanup (deleted)

    if action == 'post_remove':
        deleted = kwargs.pop('pk_set', None)
        cleanup (deleted)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...