-
Notifications
You must be signed in to change notification settings - Fork 4
/
model_update.py
54 lines (45 loc) · 1.96 KB
/
model_update.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import operator
from django.db.models.expressions import F, ExpressionNode
EXPRESSION_NODE_CALLBACKS = {
ExpressionNode.ADD: operator.add,
ExpressionNode.SUB: operator.sub,
ExpressionNode.MUL: operator.mul,
ExpressionNode.DIV: operator.div,
ExpressionNode.MOD: operator.mod,
ExpressionNode.AND: operator.and_,
ExpressionNode.OR: operator.or_,
}
class CannotResolve(Exception):
pass
def _resolve(instance, node):
if isinstance(node, F):
return getattr(instance, node.name)
elif isinstance(node, ExpressionNode):
return _resolve(instance, node)
return node
def resolve_expression_node(instance, node):
op = EXPRESSION_NODE_CALLBACKS.get(node.connector, None)
if not op:
raise CannotResolve
runner = _resolve(instance, node.children[0])
for n in node.children[1:]:
runner = op(runner, _resolve(instance, n))
return runner
def update(instance, **kwargs):
"Atomically update instance, setting field/value pairs from kwargs"
# fields that use auto_now=True should be updated corrected, too!
for field in instance._meta.fields:
if hasattr(field, 'auto_now') and field.auto_now and field.name not in kwargs:
kwargs[field.name] = field.pre_save(instance, False)
rows_affected = instance.__class__._default_manager.filter(pk=instance.pk).update(**kwargs)
# apply the updated args to the instance to mimic the change
# note that these might slightly differ from the true database values
# as the DB could have been updated by another thread. callers should
# retrieve a new copy of the object if up-to-date values are required
for k,v in kwargs.iteritems():
if isinstance(v, ExpressionNode):
v = resolve_expression_node(instance, v)
setattr(instance, k, v)
# If you use an ORM cache, make sure to invalidate the instance!
#cache.set(djangocache.get_cache_key(instance=instance), None, 5)
return rows_affected