Commits

Max Noel committed 24c9f82

* Added allow_overwrite optional parameter (default=True) to DynamoDBModel.save(). When set to False, OverwriteError will be raised if the save() call would overwrite something in the database (primary key(s) already taken).

Comments (0)

Files changed (1)

dynamodb_mapper/model.py

     pass
 
 
+class OverwriteError(Exception):
+    """Raised when saving a DynamoDBModel instance would overwrite something
+    in the database and we've forbidden that because we believe we're creating
+    a new one (see :meth:`DynamoDBModel.save`).
+    """
+    pass
+
+
 class autoincrement_int(int):
     """Dummy int subclass for use in your schemas.
 
             max_hash_key = max_hash_item["__max_hash_key__"]
             max_hash_item["__max_hash_key__"] += 1
             try:
-                # Conditional write: we're overwriting iff the value hasn't changed
+                # Conditional write: we're overwriting iff the value
+                # hasn't changed.
                 max_hash_item.put({"__max_hash_key__": max_hash_key})
                 break
             except DynamoDBResponseError as e:
         item[item.hash_key_name] = max_hash_key + 1
         item.put()
 
-    def save(self):
+    def save(self, allow_overwrite=True):
         """Save the object to the database.
 
         This method may be used both to insert a new object in the DB, or to
-        update an existing one.
+        update an existing one (iff allow_overwrite == True -- otherwise,
+        the operation fails with OverwriteError).
         """
         table = ConnectionBorg().get_table(self.__table__)
         # Yes, that part is horrible. DynamoDB can't store empty sets,
             # Update the primary key so that it reflects what it was saved as.
             setattr(self, self.__hash_key__, item[self.__hash_key__])
         else:
-            item.put()
+            if allow_overwrite:
+                expected_values = None
+            else:
+                # Forbid overwrites: do a conditional write on
+                # "this hash_key doesn't exist"
+                expected_values = {self.__hash_key__: False}
+                if self.__range_key__:
+                    expected_values[self.__range_key__] = False
+            try:
+                item.put(expected_values)
+            except DynamoDBResponseError as e:
+                if e.error_code == "ConditionalCheckFailedException":
+                    raise OverwriteError(item)
+                # Unhandled exception
+                raise
 
     def delete(self):
         """Delete the current object from the database."""
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.