Disable creating nested objects in django rest framework

All we need is an easy explanation of the problem, so here it is.

Hi,

Can you help me how to disable creating nested objects ?

I have serializers like this:

(Employee has ForeignKey to Team)

class TeamSerializer(serializers.ModelSerializer):
    class Meta:
        model = Team
        fields = ('id', 'name')
class EmployeeSerializer(serializers.ModelSerializer):
    class Meta:
        model = Employee
        fields = ('id', 'name', 'surname', 'team')
        depth = 1

or instead could be:

class EmployeeSerializer(serializers.ModelSerializer):
    team = TeamSerializer()
    class Meta:
        model = Employee
        fields = ('id', 'name', 'surname', 'team')

when i post json (create employee)

{
    name: "name",
    surname: "surname",
    team: {
           id: 1,
           name: "Web Team"
          }
}

object employee creates but also object team…
is there any way to disable creating team object together with employee ?
i just want to create employee and set selected team (curently in database) to employee

And on GET (list) i would like to be able to retrieve
data like:

{
 name: "name",
 surname: "surname",
 team: {
    id: 1,
    name: "Web Team"
 }

not like that

{
 name: "name",
 surname: "surname",
 team: 1
}

Is there any way to to that in django rest framework (also iam using angular)

Regards

UPDATE

Currently serializers:

class TeamSerializer(serializers.ModelSerializer):
    class Meta:
        model = Team

class EmployeeSerializer(serializers.ModelSerializer):
    team = TeamSerializer()

    class Meta:
        model = Employee

I cant use serializers.RelatedField() because it’s returned only unicode I need ‘id’ and ‘name’ (i suppose)

this is my POST json: ( Restangular.all(’employee’).post(data) )

data: {
        name: "emp1",
        photo: "",
        skype: "",
        surname: "qweqwe",
        team: {
              id: 1,
              name: "Web",
        }
}

and DRF returned json:

employee: {
    id: 2,
    name: "emp1",
    photo: "",
    skype: "",
    surname: "qweqwe",
    team: {
        id:3, <-- NEW ID!
        name: "Web"
    }   
}

so yea, i am sure that new team objects created. So what now ?:)

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

I found that ModelSerializer.to_native() and ModelSerializer.from_native() don’t exist in the latest version of DRF. I came up with the following derived from the accepted solution:

class PlayerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Player
        fields = ('id', 'name', 'team')

    def to_internal_value(self, data):
        # If team is not a dict, such as when submitting via the Browseable UI, this would fail.
        try:
            data['team'] = data['team']['id']
        except TypeError:
            pass
        return super(PlayerSerializer, self).to_internal_value(data)

    def to_representation(self, instance):
        return ReadPlayerSerializer(instance).data


class ReadPlayerSerializer(serializers.ModelSerializer):
    team = TeamSerializer()

    class Meta(PlayerSerializer.Meta):
        pass

Effectively, it seems that from_native is now to_representation, and to_native is no to_internal_value

Method 2

Okay, first of all, are you sure that your nested object is created? Because DRF was not designed to create nested objects, so this is a very strange behavior (more precisely, this is a work in progress, as stated by its creator, Tom Christie).

Then, in order to have the serializer representation that you want, you must follow some rules:

  1. Create a simple serializer for each model (just like in your first code snippet)

  2. Add the FK relationship on the EmployeeSerializer: (be careful, for this to work, you must have your FK named ‘team’)
    team = serializers.RelatedField()

  3. Add the ManyToOne relationship on your TeamSerializer: (this is only needed if you also want to have all your employees when you get a Team object; if not, you can skip this part)
    employees = EmployeeSerializer(required=False, many=True)

Also, you should remove the depth attribute from your serializer, he is the one that flattens your serialization (or you can just set it to 2). Hope this helps.

UPDATE

View for multiple serializers:

def get_serializer_class(self):
    if self.request.method == 'GET':
        return ReadEmployeeSerializer
    elif self.request.method == 'POST':
        return WriteEmployeeSerializer
    else:
        return DefaultSerializer

class WriteEmployeeSerializer(serializers.ModelSerializer):
    class Meta:
        model = Employee
        fields = ('id', 'name', 'surname', 'team')

class ReadEmployeeSerializer(serializers.ModelSerializer):
    team = TeamSerializer()
    class Meta:
        model = Employee
        fields = ('id', 'name', 'surname', 'team')

A bit redundant, but should do the job.

Method 3

Solved problem:

class ReadEmployeeSerializer(serializers.ModelSerializer):
    team = TeamSerializer()

    class Meta:
        model = Employee
        fields = ('id', 'name', 'surname', 'team',)


class WriteEmployeeSerializer(serializers.ModelSerializer):

    def from_native(self, data, files):
        data['team'] = data['team']['id']
        return serializers.ModelSerializer.from_native(self, data, files)

    def to_native(self, obj):
        return ReadEmployeeSerializer(obj).data

    class Meta:
        model = Employee
        fields = ('id', 'name', 'surname', 'team',)

post/put method use primary key (WriteEmployeeSerializer) – before replace dict to primary key

get method use full object (ReadEmployeeSerializer)

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply