I am trying to rewrite get_by_natural_key method on django manager (models.Manager). After adding model (NexchangeModel) I can delete all() objects but single - cannot.
Can:
SmsToken.objects.all().delete()
Cannot:
SmsTokent.objects.last().delete()
Code:
from django.db import models
from core.common.models import SoftDeletableModel, TimeStampedModel, UniqueFieldMixin
class NexchangeManager(models.Manager):
def get_by_natural_key(self, param):
qs = self.get_queryset()
lookup = {qs.model.NATURAL_KEY: param}
return self.get(**lookup)
class NexchangeModel(models.Model):
class Meta:
abstract = True
objects = NexchangeManager()
class SmsToken(NexchangeModel, SoftDeletableModel, UniqueFieldMixin):
sms_token = models.CharField(
max_length=4, blank=True)
user = models.ForeignKey(User, related_name='sms_token')
send_count = models.IntegerField(default=0)
While you are calling:
SmsToken.objects.all().delete()
you are calling the queryset's delete method.
But on SmsTokent.objects.last().delete()
you are calling the instance's delete method.
After django 1.9 queryset delete
method returns no of items deleted. REF
Changed in Django 1.9: The return value describing the number of objects deleted was added.
But on instance delete
method Django already knows only one row will be deleted.
Also note that querset's delete method and instance's delete method are different.
The delete()
[on a querset]
method does a bulk delete and does not call any delete() methods on your models[instance method]
. It does, however, emit the pre_delete and post_delete signals for all deleted objects (including cascaded deletions).
So you cannot rely on the response of the method to check if the delete worked fine or not. But in terms of python's philosophy "Ask for forgiveness than for permission". That means you can rely on exceptions to see if a delete has worked properly the way it should. Django's ORM will raise proper exceptions and do proper rollbacks in case of any failure.
So you can do this:
try:
instance.delete()/querset.delete()
except Exception as e:
# some code to notify failure / raise to propagate it
# you can avoid this try..except if you want to propagate exceptions as well.
Note: I am catching generic Exception because the only code in my try block is delete
. If you wish to have some other code then you must catch specific exceptions only.
I assume that SoftDeletableModel
comes from the django-model-utils package? If so, the purpose of that model is to mark instances with an is_removed
field rather than actually deleting them. So it's to be expected that calling delete()
on a model instance—which is what you get from last()
—wouldn't actually delete anything.
SoftDeletableModel
provides an objects
attribute with a manager that limits its results to non-removed objects, and overrides delete()
to mark objects as removed instead of actually deleting them.
The problem is that you've defined your own manager as objects
, so the SoftDeletableModel
manager isn't being used. Your custom manager is actually bulk deleting objects from the database, contrary to the goal of doing a soft delete. The way to resolve this is to have your custom manager inherit from SoftDeletableManagerMixin
:
class NexchangeManager(SoftDeletableManagerMixin, models.Manager):
# your custom code
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With