default_for_new option for attributes behavior is vague and not as expected
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'})
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).
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)