5

I have a big misunderstanding with DRF nested serializers. I read docs about this and found out that I need to provide my own update method. So, here it is:

class SkillsSerializer(serializers.ModelSerializer):

class Meta:
    model = Skills

class ProfileSerializer(serializers.ModelSerializer):
    skills = SkillsSerializer(many=True)

    class Meta:
        model = Profile
        fields = ('user', 'f_name', 'l_name', 'bd_day', 'bd_month', 'bd_year', 'spec', 'company', 'rate', 'skills', 'bill_rate', 'website', 'about', 'city', 'avatar', 'filled')

    def update(self, instance, validated_data):
        instance.user_id = validated_data.get('user', instance.user_id)
        instance.f_name = validated_data.get('f_name', instance.f_name)
        instance.l_name = validated_data.get('l_name', instance.l_name)
        instance.bd_day = validated_data.get('bd_day', instance.bd_day)
        instance.bd_month = validated_data.get('bd_month', instance.bd_month)
        instance.bd_year = validated_data.get('bd_year', instance.bd_year)
        instance.spec = validated_data.get('spec', instance.spec)
        instance.company = validated_data.get('company', instance.company)
        instance.rate = validated_data.get('rate', instance.rate)
        instance.website = validated_data.get('website', instance.website)
        instance.avatar = validated_data.get('avatar', instance.avatar)
        instance.about = validated_data.get('about', instance.about)
        instance.city = validated_data.get('city', instance.city)
        instance.filled = validated_data.get('filled', instance.filled)
        instance.skills = validated_data.get('skills', instance.skills)
        instance.save()
        return instance

I compared it with docs and didn't found any difference. But in this case, when I try to update skills, it doesn't work. And there is a real magic: when I put this

instance.skills = validated_data.get('bd_day', instance.skills)

It works PERFECTLY WELL! For ex., if I put bd_day = 12, update method saves instance with skills with ID's 1 and 2. So, it seems like serializer ignores skills from AJAX data and still thinking, that skills serializer is read_only. So, what is a point of this logic and how I can finally update my skills?

UPDATE

My models:

class Skills(models.Model):
    tags = models.CharField(max_length='255', blank=True, null=True)

    def __unicode__(self):
        return self.tags


class Profile(models.Model):
     user = models.OneToOneField(User, primary_key=True)
     ...
     skills = models.ManyToManyField(Skills, related_name='skills')
     ...

UPDATE2

Still doesn't have any solution for this case! I tried this and this - the same result. It seems that serializer ignored JSON data at all.

Community
  • 1
  • 1
NONAMA
  • 503
  • 1
  • 9
  • 21

2 Answers2

3

I had to update the answer, you did it inefficient way, so see the solution, it's far better

class ProfileSerializer(serializers.ModelSerializer):

    class Meta:
        model = Profile
        fields = ('user', 'f_name', ... 'skills', ... 'filled')
        depth = 1

class ProfileUpdateSerializer(serializers.ModelSerializer):
    skills = serializers.PrimaryKeyRelatedField(many=True, queryset=Skills.objects.all(), required=False)

    class Meta:
        model = Profile
        fields = ('user', 'f_name', ... 'skills', ... 'filled')
    
    def update(self, instance, validated_data):
        user = validated_data.pop('user', {})
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()

        if user:
            User.objects.filter(id=self.context['request'].user.id).update(**user)


        return instance

But after that I had another issue. I can received only one element from array of skills. And I found solution here:

 $.ajax({
    url: myurl,
    type: 'PUT',
    dataType: 'json',
    traditional: true,<-----THIS!
    data: data,

And that's it! It works like a charm!

I hope, my solution will be useful!

Community
  • 1
  • 1
NONAMA
  • 503
  • 1
  • 9
  • 21
  • I am stuck with the same problem, but I don't think creating two serializers is ideal according to DRY. Any new ideas? – vabada Jan 04 '16 at 13:19
1

You have an issue here as you're providing non model data.

this:

    instance.skills = validated_data.get('skills', instance.skills)

Will not provide Skill model instances but a dictionary. You need to get the skills instance first and then inject them back to the instance.skills.

Linovia
  • 19,812
  • 4
  • 47
  • 48
  • Could you please give an example how it must to be looks like? – NONAMA Nov 08 '15 at 18:08
  • The documentation already provides one: http://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers – Linovia Nov 08 '15 at 20:26
  • 1
    It is, but I didn't found an example for update method for many-to-many without through table. – NONAMA Nov 09 '15 at 13:23
  • That's because there are too many use case. Would you rather update or create a new related instance when some data change ? What about the removal policy ? and so on – Linovia Nov 09 '15 at 13:45
  • I still doesn't have any solution for this case. I tried to use this advice [link](http://stackoverflow.com/a/27585066/4149471), but it didn't help. I can't understood how I can get Skills() instance if I can't receive it from JSON? – NONAMA Nov 11 '15 at 19:22
  • Then you need to loop on validated_data.get('skills') and fetch skill instance from the database according to the key you'd get and update or create it. – Linovia Nov 11 '15 at 19:47
  • validated_data.get('skills') is None when I try to receive it. – NONAMA Nov 11 '15 at 19:51
  • This is another question which is likely already asked on SO. – Linovia Nov 12 '15 at 06:09