5

I have 2 views - one of them uses create and another one uses RetrieveUpdateDestroyAPIView. Both use same Serializer class. I need title validation only when creating a new post, not when updating a post.
I have title validation error when I'm updating some post with "PUT" request.
How can I fix this?

class StoreApiView(mixins.CreateModelMixin, generics.ListAPIView):
    lookup_field = 'pk'
    serializer_class = ProductSerializer

    def get_queryset(self):
        qs = Product.objects.all()
        query = self.request.GET.get('q')
        if query is not None:
            qs = qs.filter(
                Q(title__icontains=query) |
                Q(description__icontains=query)
            ).distinct()
        return qs

    def perform_create(self, serializer):
        serializer.save(author=self.request.user)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class StoreRucView(generics.RetrieveUpdateDestroyAPIView):
    lookup_field = 'pk'
    serializer_class = ProductSerializer
    permission_classes = [IsOwnerOrReadOnly]


    def get_queryset(self):
        return Product.objects.all()

This is the serializer class full code:

class ProductSerializer(ModelSerializer):
    product_ratings = ProductRatingSerializer(many=True,read_only=True)
    product_badges_set = ProductBadgesSerializer(many=True,read_only=True)

    author = serializers.SerializerMethodField()
    category = serializers.SerializerMethodField()

    def get_author(self, obj):
        return obj.author.first_name+' '+obj.author.last_name

    def get_category(self, obj):
        return obj.category.title

    class Meta:
        model = Product
        fields = [
            'product_id',
            'author',
            'category',
            'title',
            'description',
            'price',
            'level',
            'video_length',
            'created_date',
            'updated_date',
            'product_ratings',
            'product_badges_set',
        ]
        read_only_fields = ['product_id', 'created_date', 'updated_date', 'author']

    def validate_title(self, value):

        qs = Product.objects.filter(title__iexact=value)
        if self.instance:
            qs.exclude(pk=self.instance.pk)
        if qs.exists():
            raise serializers.ValidationError("this title is already used")
        return value
cezar
  • 11,616
  • 6
  • 48
  • 84
devmrh
  • 1,171
  • 4
  • 19
  • 46
  • Its 9 months already. I had the same issue and the perfect and simple solution is found in this post: [How to validate a field on update in DRF?](https://stackoverflow.com/questions/39598576/how-to-validate-a-field-on-update-in-drf) Hope it helps someone. – demerit Dec 02 '18 at 17:36

1 Answers1

5

You can pass additional context in serializer. And check this context's value in validate_title. In view:

def get_serializer_context(self):
    context = super().get_serializer_context()
    context['is_create'] = True 
    return context 

def perform_create(self, serializer):
    serializer.save(author=self.request.user)

In serializer:

def validate_title(self, value):
  if self.context.get('is_create'):   
    qs = Product.objects.filter(title__iexact=value)
    if self.instance:
        qs.exclude(pk=self.instance.pk)
    if qs.exists():
        raise serializers.ValidationError("this title is already used")
    return value
neverwalkaloner
  • 46,181
  • 7
  • 92
  • 100
  • 4
    Creating a new post happens after a `POST` request, updating after a `PUT` request. Isn't your solution reinventing the wheel? Why wouldn't you check the request type instead? – cezar Feb 12 '18 at 09:40
  • I had a very similar question: https://stackoverflow.com/questions/48267017/drf-validate-nested-serializer-data-when-creating-but-not-when-updating – cezar Feb 12 '18 at 09:41
  • @cezar you are right, thank you! In this case `request` should be included into context: `serializer.save(author=self.request.user, context={'request': self.request)`. – neverwalkaloner Feb 12 '18 at 09:48
  • 1
    I don't think this works... the perform create happens after the validation. so when you try to validate the title, you get no `context` on the `validate_title` method. What I ended up doing is performing the `validate_title` queries in the `perform_create` method. The [documentation](https://www.django-rest-framework.org/api-guide/generic-views/#methods) for the `perform_create` method suggests that we could use it to raise validation errors as well. – m4rk4l Jan 09 '19 at 16:00
  • @m4rk4l you are rigth, thank you. Context should be updated inside `get_serializer_context` method. – neverwalkaloner Jan 09 '19 at 17:06