Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding upsert option to save #2532

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open

Conversation

voglster
Copy link

I believe this addresses #564 without breaking backwards compatibility. Allowing you to explicitly choose not to upsert so you don't create bad documents in your database.

This may not be the correct solve long term as save as it is can literally break your database if you delete the backing object between when you loaded this one but it does maintain backwards compatibility in case someone is using that "feature".

Includes tests as well, my first pull request here so let me know if I need to do anything else.

Copy link
Collaborator

@bagerard bagerard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution, this is a good idea. We just need to make sure the behavior will be the most intuitive as once people start to rely on this, it wll be hard to introduce behavioral changes

@@ -332,6 +332,7 @@ def save(
_refs=None,
save_condition=None,
signal_kwargs=None,
upsert=None,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default is True so I believe we should make that clear on the api, thus upsert=True (similarly to force_insert=False)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that is not True. It only defaults to true IF we do not send a save condition. I'm not sure how to better fix that because there are 3 states.

@@ -505,7 +508,7 @@ def _integrate_shard_key(self, doc, select_dict):

return select_dict

def _save_update(self, doc, save_condition, write_concern):
def _save_update(self, doc, save_condition, write_concern, upsert=None):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same remark here, default should be True

@@ -524,12 +527,13 @@ def _save_update(self, doc, save_condition, write_concern):

update_doc = self._get_update_doc()
if update_doc:
upsert = save_condition is None
if upsert is None:
upsert = save_condition is None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

save_condition=something and upsert=True are contradictory, perhaps we should raise an exception whenever it enters that condition. If we don't do this, the alternative is to mention which one will prevail on the docstring (and in my opinion save_condition should prevail).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, I am going to add a guard at the top asserting that upsert = True and save_condition cannot be set

with set_write_concern(collection, write_concern) as wc_collection:
last_error = wc_collection.update_one(
select_dict, update_doc, upsert=upsert
).raw_result
if not upsert and last_error["n"] == 0:
if save_condition is not None and last_error["n"] == 0:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact that we silently ignore it if upsert=False is not entirely fixing (#564), I'm a bit hesitating to make it raise an exception because I believe people impacted by #564 will prefer to be aware of the problem

What do you think @erdenezul ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just adding to the doc string explaining the behavior of upsert=False and that it will silently fail? You do return if an object was created or not at the end of the method.

p2.save(upsert=None) # default if you dont pass it

assert Person.objects.count() == 1

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we should have 1 (perhaps more) test that ensures how providing both save_condition & upsert would behave.
Depending on how we resolved some of the comments I made in this PR, we'll probably need a few more tests as well

@voglster
Copy link
Author

Thanks for your feedback @bagerard.
I am not sure of a clean way to handle the 3 states of upsert (explicitly set True/False vs implicitly set via the save_condition argument) Any help/ideas from you would be appreciated there. But the default is not necessarily True.
I did add a new commit adding the guard against seting upsert=True and a save_condition along with the relevant test but am unsure if I am raising the correct exception. Perhaps OperationError instead?

@voglster
Copy link
Author

I made the small changes raising exceptions but got no response on the True vs None because of the three states a month ago. Any updates/thoughts?

@voglster
Copy link
Author

voglster commented Dec 9, 2021

Thought i would check in again on this... any response?

@voglster
Copy link
Author

It has been ~3 months and closing in on a year since my original pull request,

Thought I would check in again on this... any response?

@voglster
Copy link
Author

just remembered this PR, thought i would check in...
Bumping it...
bueller?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants