Hello Django Community
These are my models: (The blue arrows represent m2m relations, the black arrows represent foreign keys)
And in Code:
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 currently using the admin area exclusively, as I want to have the database with its mechanisms down perfectly before working on the page for the users. My problem is the following: users will have choose one or more FunctionClass instances. These FunctionClasses hold one or more Variable instances.
Now for each Variable that is selected in this manner I automatically generate a single VariableDeviceDescription filled with default values (this model holds device-specific data of a variable. The user has to change fields if the default values are not sufficient.)
I use a receiver for automatic instance creation:
@receiver(m2m_changed, sender=Device.function_classes.through) def create_vdds(sender, instance, action, reverse, model, pk_set, **kwargs): if action == 'pre_add': fc_pre_add(instance, pk_set)
Lo and behold, when I save a Device instance, the VariableDeviceDescription instances are created, and they even show up in the inline that I prepared for them in the DeviceAdmin. The user can now go ahead and change stuff in the form if necessary.
I then applied the same logic to try and delete VariableDeviceDescription instances after Function Classes are removed from the m2m field in Device:
@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