0
votes

I have a little problem with django rest framework about a generic relation which is also used in a unique_together constraint.

I have this model :

class VPC(models.Model):
    name = models.SlugField(null=False, max_length=100)
    deletable = models.BooleanField(null=False, default=True)
    date_created = models.DateTimeField(auto_now_add=True)
    date_modified = models.DateTimeField(auto_now=True)
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    class Meta:
        unique_together = (('name', 'content_type', 'object_id'))

It has a generic relation and a unique constraint : the name of the vpc + the generic relation(which is the owner).

Here is the serializer:

class VPCSerializer(serializers.ModelSerializer):

 class Meta:
    model = VPC
    read_only_fields = ('deletable', 'date_created', 'date_modified')
    fields = ('name', 'deletable')
    lookup_field = 'name'
    validators = [
        UniqueTogetherValidator(
            queryset=VPC.objects.all(),
            fields=('name', 'content_type', 'object_id')
        )
    ]

In the fields I don't put the content_type and object_id as I don't want them to be displayed/set by the users.

But I have to put them in the UniqueTogetherValidator in order to avoid a django.db.utils.integrityerror raised error when creating a VPC with the same account/name.

But now when I try to create a VPC I got this error :

{"object_id":["This field is required."],"content_type":["This field is required."]}

So in my viewsets.ModelViewSet I tried to override perform_create() to set the object_id and content_type but it looks like that the error raised before calling this method.

I also try to override get_serializer_context() to return a dictionary containing the object_id and content_type, but it doesn't work neither.

I spent a lot of time on this and I don't find out. So what method should I override to be able to set the object_id and content_type in the serializer ?

Thanks.

1

1 Answers

0
votes

The serializer validation happens in the

def create(self, request, *args, **kwargs):

method not in "perform_create"

you need to override the "create" method and fill 'content_type', 'object_id' to the request data(which will be used to initialize the serializer and validation).

you can do like, for example,

def create(self, request, *args, **kwargs):
    if hasattr(request.data, '_mutable'):
        request.data._mutable = True
    request.data['content_type'] = "your_content_type"
    request.data['content_type'] = "your_object_id"

    serializer = VPCSerializer(data=request.data,  request=request)
    serializer.is_valid(raise_exception=True)
    self.perform_create(serializer)

    return Response here..

Just fill the required data before you initialize the serializer and check for validation then save using the serializer object and return the serilizer.data or any custom response you want.