0

I am trying to accomplish the following with Django REST framework. I have model Records, which has field. It has foreign key to User. Each user can create multiple records with different numbers, but cannot create more than one record with the same number for themselves. However, each other user can create a record for itself with same number. E.g. Users Joe and Jill. Joe can create a record with number 123 only once, if he tries to make another record with 123, he should not be allowed. However Jill can create 123 once for herself, but not allowed more again.

My guess is to create a validator inside the serializer, which is

class RecordSerializer(serializers.HyperlinkedModelSerializer):
    '''Serializer for Record'''

    class Meta:
        model = Record
        fields = [
            'user', 
            'number', 
            'otherfield', 
        ]

        validators = [
            UniqueValidator(
                queryset= Record.objects.filter(user=<current user>),
            )
        ]

However, I cannot get the current user inside validator property, or how can I accomplish this otherwise?

Gevorg Hakobyan
  • 328
  • 3
  • 14

2 Answers2

1

On your model Meta, set unique_together https://docs.djangoproject.com/en/3.0/ref/models/options/#unique-together

Andee
  • 753
  • 3
  • 12
1

You should add the constraint both in the serializer level and model level just in case a record is created outside the serializer. So let's say the record has id and owner fields, add the unique_together property in the model's Meta class:

class Meta:
    unique_together = (('id', 'owner'))

Then in the serializer, you can use the CurrentUserDefault advanced field to capture the logged-in user.

from rest_framework.validators import UniqueTogetherValidator


class RecordSerializer(serializer.ModelSerializer):
    owner = serializers.HiddenField(
        default=serializers.CurrentUserDefault()
    )

    class Meta:
        validators = [
            UniqueTogetherValidator(
                queryset=Record.objects.all(),
                fields=['id', 'owner']
            )
        ]

Of course, the serializer should be used in a view which requires authentication

Ken4scholars
  • 6,076
  • 2
  • 21
  • 38
  • Guys! @Andee and @Ken4Sholars! thanks so much! I have spent a whole day finding a solution! – Gevorg Hakobyan May 25 '20 at 20:35
  • This answer at https://stackoverflow.com/a/55786819/7027427 points that UniqueConstraints should be preferred over unique_together, and that can be deprecated. Also I am getting error pointing to those fields, when migrating. – Gevorg Hakobyan May 25 '20 at 20:52
  • 1
    Yes, that is just the preferred way of specifying the unique constraints in the new versions of Django, so if you have such a version, please go ahead and use that instead of `unique_together`. As for the `IntegrityError`, it seems you are violating the unique constraint. That's exactly how it notifies the developer that it is being violated. I suspect that you are creating the record outside the serializer. Means that user already has a record with that number. Also, you may want to rename the `id` field in my answer to the number field you described in the question – Ken4scholars May 25 '20 at 21:00
  • I had records violating the constraint in the db. removed them and fixed :) thanks a lot once again. – Gevorg Hakobyan May 25 '20 at 21:01