Django IntegrityError: Unique Constraint Violation

I’m working on a Django project using Django Rest Framework (DRF) and PostgreSQL, with a Scan model that tracks different phases of a label scan. I’ve set a unique constraint on the combination of label_id and phase fields to prevent duplicate scans for the same label in the same phase.

Here’s my Scan model:

class Scan(models.Model):
    user = models.ForeignKey("accounts.User", on_delete=models.CASCADE)
    label = models.ForeignKey("labels.Label", on_delete=models.CASCADE)
    phase = models.IntegerField(
        choices=ScanPhaseChoice.choices,
        default=ScanPhaseChoice.unknown.value,
    )
    distribution = models.ForeignKey("factories.Distribution", null=True, blank=True, on_delete=models.CASCADE)
    store = models.ForeignKey("factories.Store", null=True, blank=True, on_delete=models.CASCADE)
    created_date = models.DateTimeField(auto_now_add=True)
    updated_date = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = ["label", "phase"]

When I try to create a new scan, I get this error:

django.db.utils.IntegrityError: duplicate key value violates unique constraint "labels_scan_label_id_phase_81ec6788_uniq"
DETAIL: Key (label_id, phase)=(413, 1) already exists.

I’ve verified that the combination of label_id and phase already exists in the database, but I’m not sure why this error is raised when I’ve used full_clean() to validate data in the save() method.

What I’ve Tried

  1. Ensuring the constraint is defined in both the database and the model’s Meta class.
  2. Adding validate_unique in the clean and save methods, but it didn’t prevent the error.
  3. Using full_clean() in the save method, expecting it to check for unique constraints.

Questions

  1. Why doesn’t full_clean() catch the unique constraint violation in the save() method? I thought full_clean() would validate uniqueness, but it seems this isn’t happening here.
  2. Is there a best practice for handling unique constraint violations in Django when using DRF and PostgreSQL for cases where a combination may exist already?
  3. Should I check for duplicates manually in the save() method, or is there a more efficient way to prevent this error?

Any insights on why this is happening and suggestions for how to handle it would be greatly appreciated. Thank you!

Please post the view (and any other directly-referenced code) that is saving the model and throwing this error.

View :

class ScanModelViewSet(
    mixins.CreateModelMixin,
    mixins.RetrieveModelMixin,
    mixins.ListModelMixin,
    viewsets.GenericViewSet,
):

    serializer_class = ScanSerializer
    permission_classes = [IsFactoryDriver]

    def get_queryset(self):
        return Scan.objects.filter(user=self.request.user)

Serializer :

class ScanSerializer(serializers.ModelSerializer):
    user = UserSerializer(read_only=True)

    class Meta:
        model = Scan
        fields = [
            "id",
            "user",
            "label",
            "phase",
            "distribution",
            "store",
            "label_image",
            "label_coordinate",
            "label_quality",
            "created_date",
            "updated_date",
        ]
        read_only_fields = [
            "id",
            "user",
            "label_quality",
            "created_date",
            "updated_date",
        ]

    def create(self, validated_data):
        request = self.context.get("request")
        validated_data["user"] = request.user
        instance = super().create(validated_data)
        return instance

im override my save method on my model :

    def save(self, *args, **kwargs):
        self.full_clean()
        super().save(*args, **kwargs)
        

I noticed that when I try adding a duplicate entry through a regular request, Django correctly raises a 400 Bad Request with the message: “The fields label, phase must make a unique set.” However, at random, some requests still result in an IntegrityError instead of a 400 response.
I think it might be a race condition because
When I submit through the admin panel or manually (with postman) , there is no issue. However, when the frontend sends labels consecutively, sometimes one of them causes this issue. But under normal circumstances, it works fine. The question is why when the front sends the same label several times, sometimes it works correctly and sometimes it gives an error
@KenWhitesell