Conditional Operations¶
Some DynamoDB operations support the inclusion of conditions. The user can supply a condition to be evaluated by DynamoDB before an item is modified (with save, update and delete) or before an item is included in the result (with query and scan). See the official documentation for more details.
Suppose that you have defined a Thread Model for the examples below.
from pynamodb.models import Model
from pynamodb.attributes import UnicodeAttribute, NumberAttribute
class Thread(Model):
class Meta:
table_name = 'Thread'
forum_name = UnicodeAttribute(hash_key=True)
subject = UnicodeAttribute(range_key=True)
views = NumberAttribute(default=0)
authors = ListAttribute()
properties = MapAttribute()
Condition Expressions¶
PynamoDB supports creating condition expressions from attributes using a mix of built-in operators and method calls. Any value provided will be serialized using the serializer defined for that attribute. See the comparison operator and function reference for more details.
DynamoDB Condition |
PynamoDB Syntax |
Attribute Types |
Example |
---|---|---|---|
= |
== |
Any |
|
<> |
!= |
Any |
|
< |
< |
Binary, Number, String |
|
<= |
<= |
Binary, Number, String |
|
> |
> |
Binary, Number, String |
|
>= |
>= |
Binary, Number, String |
|
BETWEEN |
between( lower , upper ) |
Binary, Number, String |
|
IN |
is_in( *values ) |
Binary, Number, String |
|
attribute_exists ( path ) |
exists() |
Any |
|
attribute_not_exists ( path ) |
does_not_exist() |
Any |
|
attribute_type ( path , type ) |
is_type() |
Any |
|
begins_with ( path , substr ) |
startswith( prefix ) |
String |
|
contains ( path , operand ) |
contains( item ) |
Set, String |
|
size ( path ) |
size( attribute ) |
Binary, List, Map, Set, String |
|
AND |
& |
Any |
|
OR |
| |
Any |
|
NOT |
~ |
Any |
|
Conditions expressions using nested list and map attributes can be created with Python’s item operator []
.
# Query for threads where 'properties' map contains key 'emoji'
Thread.query(..., filter_condition=Thread.properties['emoji'].exists())
# Query for threads where the first author's name contains "John"
Thread.authors[0].contains("John")
Conditions can be composited using &
(AND) and |
(OR) operators. For the &
(AND) operator, the left-hand side
operand can be None
to allow easier chaining of filter conditions:
condition = None
if request.subject:
condition &= Thread.subject.contains(request.subject)
if request.min_views:
condition &= Thread.views >= min_views
results = Thread.query(..., filter_condition=condition)
Conditioning on keys¶
When writing to a table (save, update, delete), an exists()
condition on a key attribute
ensures that the item already exists (under the given key) in the table before the operation.
For example, a save or update would update an existing item, but fail if the item
does not exist.
Correspondingly, a does_not_exist()
condition on a key ensures that the item
does not exist. For example, a save with such a condition ensures that it’s not
overwriting an existing item.
For models with a range key, conditioning exists()
on either the hash key
or the range key has the same effect. There is no way to condition on _some_ item
existing with the given hash key. For example:
thread = Thread('DynamoDB', 'Using conditions')
# This will fail if the item ('DynamoDB', 'Using conditions') does not exist,
# even if the item ('DynamoDB', 'Using update expressions') does.
thread.save(condition=Thread.forum_name.exists())
# This will fail if the item ('DynamoDB', 'Using conditions') does not exist,
# even if the item ('S3', 'Using conditions') does.
thread.save(condition=Thread.subject.exists())
Conditional Model.save¶
This example saves a Thread item, only if the item exists.
thread_item = Thread('Existing Forum', 'Example Subject')
# DynamoDB will only save the item if forum_name exists
print(thread_item.save(Thread.forum_name.exists())
# You can specify multiple conditions
print(thread_item.save(Thread.forum_name.exists() & Thread.subject.contains('foobar')))
Conditional Model.update¶
This example will update a Thread item, if the views attribute is less than 5 OR greater than 10:
thread_item.update(condition=(Thread.views < 5) | (Thread.views > 10))
Conditional Model.delete¶
This example will delete the item, only if its views attribute is equal to 0.
print(thread_item.delete(Thread.views == 0))
Conditional Operation Failures¶
You can check for conditional operation failures by inspecting the cause of the raised exception:
try:
thread_item.save(Thread.forum_name.exists())
except PutError as e:
if e.cause_response_code = "ConditionalCheckFailedException":
raise ThreadDidNotExistError()