1

I have two models, one with a foreignkey relationship to the other. I am wanting to be able to create or update multiple entries at the same time. I would not be creating and updating simultaneously.

I keep getting a bit confused about what is going on with people's examples. I was hoping someone could not only show me what I need to do, but also give a bit of an explanation about what is going on at each step. I'm specifically not understanding what the validated_data.get() method is doing, or what exactly is an instance, and what is being done with them.

class ExtraFieldsSerializer(serializers.ModelSerializer):
    id = serializers.ReadOnlyField()

    class Meta:
        model = ExtraFields
        fields = [
            'id',
            'job',
            'custom_data',
        ]


class JobWriteSerializer(serializers.ModelSerializer):
    extra_fields = ExtraFieldsSerializer(many=True)

    class Meta:
        model = Job
        fields = [
            "extra_fields",
            "custom_data_fields",
        ]

    # Make JobSerializer able to create custom fields.
    def create(self, validated_data):
        extra_fields = validated_data.pop("extra_fields")
        user = User.objects.get(id=self.context['request'].user.id)
        client = Client.objects.get(user=self.context['request'].user)
        new_job = Job.objects.create(user=user, client=client, **validated_data)
        new_job.save()
        for field in extra_fields:
            new_field = ExtraFields.objects.create(job=new_job, custom_data=field['custom_data'])
            new_field.save()
        return new_job

    # Make JobSerializer able to update custom fields
    def update(self, instance, validated_data):
        pass

1 Answer 1

4

First of all, you could probably use drf-writeable-nested for that, as it pretty much does exactly what you want.

But it never hurts to understand what's going on:

def create(self, validated_data):
        extra_fields = validated_data.pop("extra_fields")
        user = User.objects.get(id=self.context['request'].user.id)
        client = Client.objects.get(user=self.context['request'].user)
        new_job = Job.objects.create(user=user, client=client, **validated_data)
        new_job.save()
        for field in extra_fields:
            new_field = ExtraFields.objects.create(job=new_job, custom_data=field['custom_data'])
            new_field.save()
        return new_job

Since JobWriteSerializer is the parent serializer, we will start with that. the validated_data argument that is passed to your create function contains, well, as the name suggests, the validated data. This means, you can assume that all the constraints you defined in your serializer (required fields, max_length, min_length etc) hold, and you don't need to check them.

The code looks fine, it seems you are popping all the extra_fields from your serializer and create ExtraField objects from them. You specifically ask what's going on when using validated_data.get but you are using validated_data.pop. the difference is, that when using get, the retrieved data stays in the dictionary, while pop removes it.

This is especially handy for cases where you also have to create nested objects, consider this (some things omitted as they are not relevant):

class MyModel(models.Model):
  text = models.CharField(max_length=10, related_name='children')

class MyChildModel(models.Model):
  someVal = models.BooleanField()
  model = models.ForeignKey(MyModel)

class MyChildSerializer(serializers.ModelSerializer):
  someVal = serializers.BooleanField()  
   
class MyModelSerializer(serializers.ModelSerializer):
  text = serializers.TextField(...)
  childen = ChildrenSerializer(many=True)
  
  def create(self, validated_data):
     children = validated_data.pop('children', []) #POP!
     instance = super().create(validated_data)
     
     for c in children:
       MyChildSerializer.objects.create(model=instance, **c)
     return instance

You can test this yourself, if you use get instead of pop here, your serializer will rightfully complain that there are children objects inside the validated_data object, and drf cannot create nested relations out of the box. When you pop them, the serializer does not have those fields anymore and it works.

Note that this approach would not be efficient for your case, as you manually pass data to your Job object (like the user and the client) which you do not get via the passed data, but from your request. You can, if you want, get around that by using get_serializer_context, but lets just say that this is out of the questions scope.

On to your update method, I suggest something like that (not tested, but you get the idea):

def update(self, instance, validated_data):
  extra_fields = validated_data.pop('extra_fields', []) # POP the extra fields
  instance = super().update(instance, validated_data) # !!!!!

  for extra in extra_fields:
    #retrieve your extra fields and update them
    myExtra = ExtraFields.objects.get(id=extra['id])
    ....

The passed argument instance is actually the already existing instance of your Job model. This is the object you want to update. Note that again, I popped the extra_fields, precisely for doing what I described above: I used drf itself to modify/update the object, since I only have to implement the update for the child elements.

Sign up to request clarification or add additional context in comments.

3 Comments

I was asking the questions about the update method, as I already understand the create method. Every example I saw of the update method being overridden has made copies of parts of the instance, but they also use validate_data. I was wondering about the get method because I assumed you could access it just like a dict.
and I showed you a possible way of writing your update method. You can access this like a normal dict, get/pop simply provide the convenience of providing a default argument if the key is not present.
Okay, I now see what is going on with the examples. Thanks

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.