Is there such a thing as a conditional "ON DELETE CASCADE" in Django?

Viewed 217

Is there a simple way, using on_delete, to delete the object containing a ForeignKey when that ForeignKey is deleted (like models.CASCADE would normally do) but just the ForeignKey relationship to None if some condition is met (like models.SET_NULL would normally do).

Here is some code:

class SomeThing(models.Model):
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL, 
        related_name="things", 
        on_delete=models.CONDITIONAL_CASCADE,  # obviously this is wrong
    )
    is_private = models.BooleanField(default=False)
>>> user = User(username="bob").save()
>>> public_thing = SomeThing(is_private=False)
>>> private_thing = SomeThing(is_private=True)
>>> user.things.add(public_thing)
>>> user.things.add(private_thing)
>>> user.delete()

When user is deleted I would like private_thing to be deleted along w/ user, but public_thing to not be deleted and for public_thing.user to be set to None.

Thanks.


edit:

Thanks to @Jarad's suggestion, I wound up writing the following generic function:


def CONDITIONAL_CASCADE(collector, field, sub_objs, using, **kwargs):
    
    condition = kwargs.get("condition", {})
    default_value = kwargs.get("default_value", None)

    sub_objs_to_cascade = sub_objs.filter(**condition)
    sub_objs_to_set = sub_objs.exclude(**condition)

    models.CASCADE(collector, field, sub_objs_to_cascade, using)
    collector.add_field_update(field, default_value, sub_objs_to_set)

and using it like this:

CASCADE_PRIVATE_THINGS_ONLY = partial(
    CONDITIONAL_CASCADE,
    condition={"is_private": True},
    default_value=None
)
0 Answers
Related