virusdefender
virusdefender

Reputation: 543

How to deserialize foreignkey with django rest framework?

For example

class People(models.Model):
    name = models.CharField(max_length=20)

class Blog(models.Model):
    author = models.ForeignKeyField(People)
    content = models.TextField()

and then,

class CreateBlogSerializer(serializers.Serializer):
    #author id
    author = serializers.IntegerField()
    content = serializers.TextField()

In views, I need to get author_id , check if the id exists and get the author instance, it's fussy to do that.

serializer = CreateBlogSerializer(data=request.DATA)
if serializer.is_valid():
    try:
        author = Author.objects.get(pk=serializer.data["author"])
    except Author.DoesNotExist:
        return Response(data={"author does not exist"})
    blog = Blog.objects.create(author=author, content=serializer.data["content"])

Is there a ForeignKeyField to deserialize and validate primarykey data and then return a instance.

class CreateBlogSerializer(serializers.Serializer):
    author = serializers.ForeignKeyField(Author)
    content = serializers.TextField()

serializer = CreateBlogSerializer(data=request.DATA)
if serializer.is_valid():
    #after deserialization , the author id becomes author model instance
    blog = Blog.objects.create(author=serializer.data["author"], content=serializer.data["content"])
else:
    #the author id does not exist will cause serializer.is_valid=Flase

PS

I knew PrimaryKeyRelatedField in ModelSerializer,but I can't use ModelSerializer here, the model structures are complex, the above are just examples。

My first thought is to write a customer field.

Upvotes: 0

Views: 2040

Answers (2)

virusdefender
virusdefender

Reputation: 543

class ForeignKeyField(WritableField):

    def __init__(self, model_name, *args, **kwargs):
        super(ForeignKeyField, self).__init__(*args, **kwargs)
        self.model_name = model_name
        self.model_instance = None

    def from_native(self, pk):
        if not isinstance(pk, int):
            raise ValidationError("pk must be int")
        try:
            self.model_instance = self.model_name.objects.get(pk=pk)
            return self.model_instance
        except self.model_name.DoesNotExist:
            raise ValidationError('object does not exist')

    def to_native(self, obj):
        return self.model_instance

I hacked it, but I don't konw why it works.

Usage: there is a little difference

class t_serializer(serializers.Serializer):
    author = ForeignKeyField(Author)

@api_view(["POST"])
def func(request):
    serializer = t_serializer(data=request.DATA)
    if serializer.is_valid():
        print isinstance(serializer.data["author"], Author)
        #print True
    else:
        print serializer.errors

Upvotes: 1

isomarcte
isomarcte

Reputation: 1981

It appears that what you want is custom validation code.

For this particular instance, you could write the following.

class CreateBlogSerializer(serializers.Serializer):
    author = serializers.ForeignKeyField(Author)
    content = serializers.TextField()

    def validate_author(self, attrs, source):
        """
        Verify that the author exists.
        """
        author = attrs[source]
        if Author.objects.filter(pk=author).exists():
            return attrs
        raise serializers.ValidationError("Specified Author does not exist!")

Now when you call serializer.is_valid() this check will occur.

So you can then do this elsewhere in your code,

if serializer.is_valid():
     blog = Blog.objects.create(author=serializer.data["author"], content=serializer.data["content"])

And be sure that if the given blog is created, there is a corresponding Author already in the DB.

Detailed Explanation

So Django Rest Framework provides a method of adding custom validation to serializers. This can be done with by providing methods of the following format in the serializer class validate_<field name>. These methods will set the source value to name of the given field, in our example author, which can then be used with the attrs variable to get the current value of the field (attrs contains all the values passed into the serializer).

These validation methods are each called with then serializer.is_valid() method is called. They are supposed to just return attrs if the given test passes, and raise a ValidationError otherwise.

They can get somewhat complex, so it is probably best if you read the full documentation here, http://www.django-rest-framework.org/api-guide/serializers#validation

Upvotes: 0

Related Questions