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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions