3

I have a serializer class in DRF like so:

class ProjectSerializer(serializers.HyperlinkedModelSerializer):
    created_by = UserSerializer() # created_by is an FK to User model

    class Meta:
        model = Project
        fields = ('id', 'title', 'created_by')

My views:

class ProjectList(generics.ListCreateAPIView):
    model = Project
    serializer_class = ProjectSerializer
    filter_fields = ('title',)

    def post(self, request, format=None):
        serializer = ProjectSerializer(data={
            "id": 12, 
            "title": "FooBar's Project", 
            "created_by": {
                "id": 1, 
                "username": "foobar", 
                "first_name": "foo", 
                "last_name": "bar",
            },
        })

    if serializer.is_valid():
        serializer.save()
    else:
        print serializer.errors
    return Response(serializer.data, status=status.HTTP_201_CREATED)

This works almost as expected except that DRF complains that:

{'created_by': [{'username': [u'User with this Username already exists.']}]}

What I want is that the Project is created with reference to an existing user. What am I doing wrong here?

Note: I am tracking trunk for DRF, not from the cheeseshop.

2
  • Confused why you're using pk=8 for the lookup but id=1 in the serializer... Commented Nov 23, 2013 at 6:20
  • Sorry - typo. I was testing some other behaviour. Commented Nov 23, 2013 at 20:14

2 Answers 2

6

If you want to assign an existing user, not creating one, nested serializer is not the way. As you want to just add the relation to the user, you will need to take a look at relations.

The PrimaryKeyRelatedField is way to go and since this field is going to be writable you will also need to pass a queryset attribute.

created_by = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())

See the documentation to check the additional options that can be passed to this field:

http://django-rest-framework.org/api-guide/relations#primarykeyrelatedfield

Then your example post could look like this:

def post(self, request, format=None):
    user = User.objects.get(pk=8)
    serializer = ProjectSerializer(data={
        "id": 12, 
        "title": "FooBar's Project", 
        "created_by": user.pk,
    })
Sign up to request clarification or add additional context in comments.

6 Comments

Hmmm. When I implement this I get the NestedValidationError of AttributeError: can't set attribute
Can you show us the updated source code please (with the model) ? Feel free to use some paste service .. dpaste.de for example.
Apologies - it did actually work like a charm. I had mistakenly applied it to the UserSerializer and not the ProjectSerializer. I will post my code as an Edit to the OP. Great advice, thanks very much!
One bonus question - is there any way to still return the full created by data with the Project API? Having switched to the PrimaryKeyRelatedField, I now only return the user's pk in the created_by field. Ideally I would still like to return the username, firstname and lastname as strings in this field.
Yeah, you use the depth option on the serializer. django-rest-framework.org/api-guide/…
|
0

(Posted on behalf of the question author).

This is the working solution:

# serializers.py    

class ProjectSerializer(serializers.HyperlinkedModelSerializer):
    ...
    created_by = serializers.PrimaryKeyRelatedField(
        queryset=User.objects.all()
    )

    class Meta:
        model = Project
        fields = ('id', 'title', 'created_by')

# views.py

class ProjectList(generics.ListCreateAPIView):
    model = Project
    serializer_class = ProjectSerializer
    filter_fields = ('title',)

    def post(self, request, format=None):
        serializer = ProjectSerializer(data={
            "title": request.DATA.get('title'),
            "created_by": request.user.pk,
        })
        if serializer.is_valid():
            serializer.save()
        else:
           return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        return Response(serializer.data, status=status.HTTP_201_CREATED)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.