Skip to content

Update nested related OneToOneField without providing its pk #157

@JocelynDelalande

Description

@JocelynDelalande

Here is minimal fictive example to explain:

class ChildModel(models.Model):
    name = models.CharField()

class ParentModel(models.Model):
    child = models.OneToOneField(ChildModel, on_delete=models.CASCADE)

class ParentSerializer(WritableNestedModelSerializer):
    class Meta:
        fields = ['child']
        model = ParentModel

class ChildSerializer(serializers.ModelSerializer):
    class Meta:
        fields = ['name']
        model = ChildModel

child = ChildModel.objects.create(name='fu')
parent = ParentModel.objects.create(child=child)

old_child_pk = child.pk
parent_serializer = ParentSerializer(data={'pk': parent.pk, 'child': {'name': 'bar'}})
parent_serializer.is_valid(raise_exception=True)
parent = parent_serializer.save()

# What I'd like:
# Child serializer went through update()

assert parent.child.pk == old_child_pk
assert ChildModel.objects.count() == 1

# What I get:
# Child serializer went through create()

assert parent.child.pk != old_child_pk
assert ChildModel.objects.count() == 2  # I got one orphaned

What is the recommended way to achieve what I want ?

I believe this behavior is expectable as long as you have a composition relation, which can also be the case with ForeignKey. In other words, my behavior may be expected when ChildModel has no independent existence from ParentModel.

For esthetic/practical reason I do not want to provide the pk of child model (can be inferred by code).

Current workaround I found is overriding ParentModel ; but this is unclean.

    # Override of private method, uh-oh
    def _get_related_pk(self, data, model_class):
        if model_class == ChildModel and self.instance and self.instance.child:
            # Reuse existing related instance instead of recreating one on each update
            return self.instance.child.pk
        else:
            return super()._get_related_pk(data, model_class)

Any thoughts ? I'm wondering if the library could not offer a way to do that.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions