Closed striveforbest closed 3 years ago
What type is self.prices
? Is this a relation manager? If so, then the related objects in prizes
prolly already got de-associated/deleted and .first()
will fail. You can try with an exception handler around:
try:
return self.prices.first()
except Price.DoesNotExist:
return None # field must allow NULL values
ForeignKey
fields as computed fields are somewhat tricky to handle, as they might expose partially deleted intermediate states of the ORM in the computed method.
Note that simply skipping CF updates on delete is not an option, because CFs can depend on each other. By deliberately skipping updates follow-up CFs would not get notified/updated anymore. (Example - in your case another CF might want to sum over all Work.computed_current_price
, thus has to be informed, that an item got removed to correctly remove that value.)
What type is self.prices? Is this a relation manager? If so, then the related objects in prizes prolly already got de-associated/deleted and .first() will fail. You can try with an exception handler around:
Price is a reverse fk to Work, so m2m from Work:
class Price(UpdateRelatedWorkModifiedTimeMixin, TimeStampedModel, models.Model):
work = models.ForeignKey('Work', models.CASCADE, verbose_name='Work', related_name='_prices')
Try/except doesn't work. The issue is that Work.delete triggers CASCADE delete of its related Sale objects:
class Sale(UpdateRelatedWorkModifiedTimeMixin, TimeStampedModel, models.Model):
work = models.ForeignKey(Work, models.CASCADE, related_name='sales')
_price = models.OneToOneField(Price, models.RESTRICT)
Which then triggers it's post_delete signal handler to clean up restricted _price
:
@receiver(post_delete, sender=Sale, dispatch_uid='delete_sales_price')
def delete_sale_price(sender, instance, using, **kwargs):
# Delete the underlying, protected, price after the sale has been deleted
instance._price.delete()
And the above triggers recalculation of Work.computed_current_price
.
Note that simply skipping CF updates on delete is not an option, because CFs can depend on each other. By deliberately skipping updates follow-up CFs would not get notified/updated anymore. (Example - in your case another CF might want to sum over all Work.computed_current_price, thus has to be informed, that an item got removed to correctly remove that value.)
In my case, I am deleting the whole work and CASCADE deleting its relations. so shouldn't matter?
Disconnecting the computedfields.handlers.predelete_handler
in Work.delete()
worked but feels wrong and dirty.
Sorry, cannot follow the issue with that scattered description. Can you post a downstripped model declaration, that repros the issue?
In my case, I am deleting the whole work and CASCADE deleting its relations. so shouldn't matter?
Thats true, but the CF resolver does not know, if there are follow-up deps to be updated at a particular tree node during runtime. There is no look-ahead (thats not even doable due to possible model-field recursions).
Disconnecting the computedfields.handlers.predelete_handler in Work.delete() worked but feels wrong and dirty.
Yes thats not a good idea, as it cuts whole update subtrees with possible worse side effects.
@StriveForBest Any updates on this? Plz write a short repro that I can use to test against.
Again I am not sure if we can get ForeignKey
working to full extend in CFs, as they add another level of indirection and partially degraded interim states. Will see.
I ended up refactoring the code to avoid our custom signals. But it does seem like an issue. We can close for now.
Is there a way to disconnect signal or disable recalculation on .delete()?
I have a Work model with a reverse fk to Price
When I try to delete the work, it fails, see full traceback: