Package: dynamizer
DynamizerA supportive set of defaults for dynamodb. Dynamizer doesn't aim to
abstract away the nuances of dynamodb, but rather to provide effective
defaults for the repetitive tasks of (de)serializing models to and from
dynamodb.It provides:Automatic deserialization from dynamo's format. This is helpful when
processing a change stream from dynamo.Overwrite detection. If a model is changed between reading it and writing
it back to dynamodb an error will be raised, preventing the unintentional
overwriting of existing data.Supportive base delete, save, and load function to make a programmers life
easier.UsageDynamizer models are built on top of immutable data classes.importdataclassesimportdynamizer@dataclasses.dataclass(frozen=True)classDemoClass(dynamizer.DynamiteModel):"""A demo class."""foobar:strfizbuzz:typing.Optional[str]=None@propertydefhash_key(self)->str:"""Get the hash key.Dynamizer can't tell you what the right hash/range key scheme iswrite for your use case and access patterns so you must definehow to translate the model's data into a hash and range key."""returnf"hash-key/{self.foobar}"@propertydefrange_key(self)->str:"""Get the range key."""return"/range-key"def_gs1(self)->str:"""Get the gs1 value.Dynamizer will also support you in instantiating any global secondaryindices. It expects these to be defined matching the pattern`^_?gs\d+$`. Dynamizer searches the class for any functions matchingthis pattern and automatically manages their lifecycle within dynamodb,creating them dynamically as the model is saved, and deleting themif they are null in some part of the models life. Note that The indexitself still needs to be created within dynamodb."""returnf"{self.foobar}/hash-key"@classmethoddefload(cls,my_identifier:typing.Any)->"DemoClass":"""Load a model from dynamodb.Dynamizer does not provide a default load function, since it doesn'tknow all of your specific access patterns and how incoming argumentsare to be translated into a hash key, range key pair, but it doesprovide helpful secondary methods to make writing a load functioneasier."""hash_key=_hash_key(my_identifier)range_key=_hash_key(my_identifier)client=boto3.client("dynamodb")result=cls._base_load(client,"my-table-name",hash_key,range_key)ifresultisNone:raiseNotFoundError(f"Could not find{my_identifier}")returnresult@classmethoddeflist_group(cls,my_group:typing.Any)->typing.List["DemoClass"]:"""List a model under the specified group.Dynamizer provides methods for easily converting a dynamodb responseinto a python model making it easier to write functions defining avariety of access patterns."""...response=client.query("Something fancy")return[cls.inflate(item)foriteminresponse.get("Items",[])]defsave(self)->"DemoClass":"""Save a model.Dynamizer keeps a count of changes to a model and checks when savingto ensure that no changes have come in since the model was initiallyread. This prevents data from being overwritten both during initialcreation and during updates."""...returnself._base_save(client,"table_to_save_to")defdelete(self):"""Delete a model.Like during saves, Dynamizer keeps a count of changes and will preventa delete from taking place if an update has taken place since the lastread."""...self._base_delete(client,"table_to_delete_from")MockingDynamizer provides a mechanism for mocking out dynamodb calls for testing. The
initial state of dynamodb can be set via yaml files.importdynamizer.mockdata=yaml.safe_load("/path/to/data.yaml")withdynamizer.mock.from_yaml(data):# Within this context dynamodb will have the state defined in data.yamlThe expected format of the yaml file is:-table_name:my-table-nameregion:us-east-1# Optionalsecondary_indexes:# Optional-name:gs1-gs2hash_key:gs1range_key:gs2objects:MyDynamizerSubCls:-foo:{S:bar}fiz:{S:buzz}AnotherDynamizerSubCls:-mock:{S:data}