We are using Django 3.1 for our websites. We have a model, User
, which is related to two SiteProfile
models for two websites, and is also related to other models such as Friend
and UserEmailAddress
. All the relations are with on_delete=models.CASCADE
. We also have models.signals.post_delete
signals for models such as Friend
, to update the number of friends a user has, which is saved in one of the SiteProfile
models. But the problem is, when deleting a user, the signal is called and then the SiteProfile
object is saved, and then I get an exception for django.db.utils.IntegrityError
- violations of foreign keys. We used to have the same problem with the UserEmailAddress
model (a user's email address), which I fixed with the following code:
def delete(self, *args, **kwargs):
if ((self.is_staff) or (self.is_superuser)):
warnings.warn('Can’t delete staff user.')
return False
else:
with transaction.atomic():
for user_email_address in self.email_addresses.all():
user_email_address.delete()
return_value = super().delete(*args, **kwargs)
return return_value
But I would like to know, is there a better way to delete a user with all its related objects which have on_delete=models.CASCADE
, but without saving the counters in models.signals.post_delete
to the database? Can I check something in models.signals.post_delete
to know if the user is being deleted and then don't save the counters? Since I don't want to specifically delete any related object in the def delete
method - there are at least 5 such models not including the two SiteProfile
models.
Django 4.1 introduced a feature where post_delete
signals now dispatch origin
of deletion.
@receiver(signal=models.signals.post_delete, sender=Friend)
def update_all_friends_count_on_unfriend(sender, instance: Friend, **kwargs):
user = instance.to_user
# Check origin for not cascade delete User -> Friend
if (user != kwargs.get('origin')):
user.speedy_net_profile.all_friends_count = user.friends.count()
user.speedy_net_profile.save()
@receiver(signal=models.signals.post_delete, sender=Friend)
def update_all_friends_count_on_unfriend(sender, instance: Friend, **kwargs):
from speedy.net.accounts.models import SiteProfile as SpeedyNetSiteProfile
user = instance.to_user
# Do .filter(...).update(...) because for cascade delete User -> Friend, accessing user.speedy_net_profile will re-create deleted SpeedyNetSiteProfile
SpeedyNetSiteProfile.objects.filter(user=user).update(all_friends_count=user.friends.count())