theatlantic / django-nested-admin

Django admin classes that allow for nested inlines
http://django-nested-admin.readthedocs.org/
Other
715 stars 99 forks source link

Deleting inlined objects in a m2m receiver #240

Closed DeHess closed 9 months ago

DeHess commented 1 year ago

My db schema:

image

And this is my models.py:


class Device(models.Model):
    function_classes = models.ManyToManyField(
        FunctionClass,
        blank=True,
    )

class FunctionClass(models.Model):
    variable_collection = models.ManyToManyField(
        Variable,
        related_name='functionclass_set',
        blank=True,
    )

class VariableDeviceDescription(models.Model):
    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['variable', 'device'], name="unique_variable_device")
        ]

    variable = models.ForeignKey(
        Variable,
        on_delete=models.CASCADE,
    )

    device = models.ForeignKey(
        Device,
        on_delete=models.CASCADE,
    )

class Variable(models.Model):
    pass

I am trying to delete VariableDeviceDescription instances through a m2m receiver like so: (This works when I use normal tabular inlines, but obviously that functionality is not good enough which is why I have chosen this otherwise great package.)

@receiver(m2m_changed, sender=Device.function_classes.through)
def device_m2m_changed(sender, instance, action, reverse, model, pk_set, **kwargs):

    if action == 'post_remove':
        removed_vars = get_vars_of_function_classes(pk_set)
        #gives me a queryset of all variables of the functionclasses that were removed
        for var in removed_vars:
            vdd_to_remove = VariableDeviceDescription.objects.filter(device=instance, variable=var).first()
            #gives me a queryset of the vdd which corresponds to the current removed variable
            vdd_to_remove.delete()

And well it just does not work. Once I press save, I can follow the debugger through this code, it seems to delete the element of the queryset without issue / exception, but then the page reloads and the VariableDeviceDescriptions are unchanged.

I have tried using a custom signal, which is sent out when the clean() method of the Device is called.

I have tried using a pre/post_save receiver on device, where I get the same effect.

I have a theory that the objects get deleted correctly when using these approaches, but get resurrected because their data is held in the inline form when I press save. However, I am terrible with forms / formsets and have no idea where to begin.

I have also tried using a pre/post_init receiver, despite the documentation warning me against making queries in receivers of this type. I had some success there, as the objects actually got deleted. For some reason, “Device” didn’t show up in the app list to the left anymore. Plus I had an error message telling me to “correct the error below” in the formset of the Device, which I could do nothing about. So I abandoned this idea as well.

I appreciate any pointers or ideas as to the solution to this problem.

DeHess commented 9 months ago

While I'm still of the opinion that some bug is causing the behavior above, I have managed to solve the problem by utilizing a custom form instead of a receiver.

Little did I know that instances can be created / deleted in the form as well, and so I overrode the save() and clean() method of the form to achieve my goal.