Skip to content
This repository was archived by the owner on Mar 6, 2023. It is now read-only.

Conversation

@jonathanpglick
Copy link
Contributor

@jonathanpglick jonathanpglick commented Mar 26, 2019

I was running into an issue where assigning a new foreign key reference to an object after cloning it was causing the reference to change on both the original and cloned object.

For example:

model = MyModel.objects.create()
model.author = Author.objects.create(name="Kurt Vonnegut")
model.save()
duplicate = model.clone()
duplicate.author = Author.objects.create(name="Douglas Adams")
duplicate.save()

model.author == duplicate.author
# => True, both are "Douglas Adams"

This was happening because of the ModelState object instance attached to the model._state key. When the model is copied with copy.copy(self.instance) the keys and values in __dict__ are copied over to the new instance. _state is included in the __dict__ so then both model instances end up pointing at the same instance of the model state, thus causing assignments to one foreign key field to change the value of the other foreign key field.

The ModelState object keeps a fields_cache dictionary that seems to be the culprit to this duplicate assignment.

Additionally, ModelState has an adding key that triggers validation uniqueness checks for new objects which could cause additional problems for some models if not reset to True

Since _state is a non-public attribute, setting _state.adding = True and _state.fields_cache = {} could potentially break in future versions of Django. Instead, it seemed less brittle to re-initialize _state to ModelState() so it will behave as if it was freshly loaded from the database.

I also added tests for this case.

@jonathanpglick jonathanpglick changed the title Prevents model._state ModelState instance from being shared by both… Prevents ModelState instance from being shared by model and clone Mar 27, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant