How to change the behaviour of unique true in django model?
Question:
Here I am not deleting the model objects from database. I am just changing the is_deleted
status to True while delete. But while doing this unique=True
gives error for the deleted objects so how can I handle this ?
I want to exclude is_deleted=True
objects from unique True.
class MyModel(models.Model):
name = models.CharField(max_length=20, unique=True)
is_deleted = models.BooleanField(default=False)
#views
class MyViewSet(ModelViewSet):
serializer_class = MySerializer
queryset = MyModel.objects.all()
def destroy(self, request, *args, **kwargs):
object = self.get_object()
object.is_deleted = True
object.save()
return Response(status=status.HTTP_204_NO_CONTENT)
Answers:
You can use a UniqueConstraint
[Django docs] with a condition instead of the unique
kwarg on the field. Although there is a caveat that validation (by forms etc.) will not be done automatically for a unique constraint with a condition and you will need to do that yourself.
from django.db.models import Q
class MyModel(models.Model):
name = models.CharField(max_length=20)
is_deleted = models.BooleanField(default=False)
class Meta:
constraints = [
models.UniqueConstraint(fields=['name'], condition=Q(is_deleted=False), name='unique_undeleted_name')
]
Note: Since Django 4.1 validation is automatically performed for all constraints using the Model.validate_constraints
method and hence the above mentioned caveat doesn’t apply.
Since django-2.2, you can work with Django’s constraint API, you can then specify a UniqueConstraint
[Django-doc] that has a condition:
class MyModel(models.Model):
# no unique=True ↓
name = models.CharField(max_length=20)
is_deleted = models.BooleanField(default=False)
class Meta:
constraints = [
models.UniqueConstraint(
fields=['name'],
name='unique_name_not_deleted',
condition=Q(is_deleted=False)
)
]
It is of course the database that enforces this, and thus some databases might not have implemented that feature.
You may use following also:
class Meta:
unique_together = ("name", "is_deleted")
Here I am not deleting the model objects from database. I am just changing the is_deleted
status to True while delete. But while doing this unique=True
gives error for the deleted objects so how can I handle this ?
I want to exclude is_deleted=True
objects from unique True.
class MyModel(models.Model):
name = models.CharField(max_length=20, unique=True)
is_deleted = models.BooleanField(default=False)
#views
class MyViewSet(ModelViewSet):
serializer_class = MySerializer
queryset = MyModel.objects.all()
def destroy(self, request, *args, **kwargs):
object = self.get_object()
object.is_deleted = True
object.save()
return Response(status=status.HTTP_204_NO_CONTENT)
You can use a UniqueConstraint
[Django docs] with a condition instead of the unique
kwarg on the field. Although there is a caveat that validation (by forms etc.) will not be done automatically for a unique constraint with a condition and you will need to do that yourself.
from django.db.models import Q
class MyModel(models.Model):
name = models.CharField(max_length=20)
is_deleted = models.BooleanField(default=False)
class Meta:
constraints = [
models.UniqueConstraint(fields=['name'], condition=Q(is_deleted=False), name='unique_undeleted_name')
]
Note: Since Django 4.1 validation is automatically performed for all constraints using the Model.validate_constraints
method and hence the above mentioned caveat doesn’t apply.
Since django-2.2, you can work with Django’s constraint API, you can then specify a UniqueConstraint
[Django-doc] that has a condition:
class MyModel(models.Model):
# no unique=True ↓
name = models.CharField(max_length=20)
is_deleted = models.BooleanField(default=False)
class Meta:
constraints = [
models.UniqueConstraint(
fields=['name'],
name='unique_name_not_deleted',
condition=Q(is_deleted=False)
)
]
It is of course the database that enforces this, and thus some databases might not have implemented that feature.
You may use following also:
class Meta:
unique_together = ("name", "is_deleted")