PynamoDB icon indicating copy to clipboard operation
PynamoDB copied to clipboard

default_for_new option for attributes behavior is vague and not as expected

Open red-crown opened this issue 6 years ago • 2 comments

I would have expected the default_for_new option on attributes to results in a conditional put_item request. However, it's unclear what the option does.

I thought it might result in an update action being added like this:

some_field = if_not_exists (some_field, {'S': 'some value'})

red-crown avatar Nov 20 '19 22:11 red-crown

I agree. The goal of this option was to compensate for another bad design choice, which is that default applies to both "new' and "existing" models.

Admittedly, "new" and "existing" is also poor terminology. What "New" really means is "initialized by user" (e.g. model = MyModel()) and "existing" means "deserialized from network data" (e.g. model = MyModel.get(123)).

Because the default option applies to "existing" models, it's unfortunately easy to load a model where a certain attribute was unset, but (a) have the in-memory representation look as if it was set, (b) set it with a default value on save (i.e. an unintentional backfill).

The feature you expected would be a little complicated to implement within the current API, since models have to support save which results in a PutItem (which can only be conditional in its entirety).

ikonst avatar Nov 20 '19 23:11 ikonst

Good point. That's why I've resorted to having a custom method on my model where I do something like the following:

attributes = [k for k in self.get_attributes().keys() if k not in self._create_only_fields]
actions = [getattr(SomeModel, attr).set(getattr(self, attr)) for attr in attributes]
actions.extend(
    [
        getattr(SomeModel, attr).set(getattr(self, attr) | getattr(self, attr))
        for attr in self._create_only_fields
    ]
)
self.update(actions=actions)

red-crown avatar Nov 21 '19 01:11 red-crown