CORD-762 add support for update_fields
Change-Id: Ifabe918fd02be2314112543d408acb68d23cd62f
diff --git a/xos/grpc/apihelper.py b/xos/grpc/apihelper.py
index 6857439..e5ee9c6 100644
--- a/xos/grpc/apihelper.py
+++ b/xos/grpc/apihelper.py
@@ -147,7 +147,7 @@
new_obj.save()
return self.objToProto(new_obj)
- def update(self, djangoClass, user, id, message):
+ def update(self, djangoClass, user, id, message, context):
obj = djangoClass.objects.get(id=id)
obj.caller = user
if (not user) or (not obj.can_update(user)):
@@ -155,7 +155,13 @@
args = self.protoToArgs(djangoClass, message)
for (k,v) in args.iteritems():
setattr(obj, k, v)
- obj.save()
+
+ save_kwargs={}
+ for (k, v) in context.invocation_metadata():
+ if k=="update_fields":
+ save_kwargs["update_fields"] = v.split(",")
+
+ obj.save(**save_kwargs)
return self.objToProto(obj)
def delete(self, djangoClass, user, id):
diff --git a/xos/tools/apigen/grpc_api.template.py b/xos/tools/apigen/grpc_api.template.py
index 84693e1..b8dcc59 100644
--- a/xos/tools/apigen/grpc_api.template.py
+++ b/xos/tools/apigen/grpc_api.template.py
@@ -44,7 +44,7 @@
def Update{{ object.camel() }}(self, request, context):
user=self.authenticate(context)
model=self.get_model("{{ object.camel() }}")
- return self.update(model, user, request.id, request)
+ return self.update(model, user, request.id, request, context)
{% endfor %}
diff --git a/xos/xos_client/tests/orm_user_crud.py b/xos/xos_client/tests/orm_user_crud.py
index 45bda8f..875c08e 100644
--- a/xos/xos_client/tests/orm_user_crud.py
+++ b/xos/xos_client/tests/orm_user_crud.py
@@ -49,8 +49,20 @@
u3 = c.xos_orm.User.objects.get(id=orig_id)
assert(u3.password=="foobar")
+ # try a partial update
+ u3.password = "should_not_change"
+ u3.firstname = "new_first_name"
+ u3.lastname = "new_last_name"
+ u3.save(update_fields = ["firstname", "lastname"])
+
+ # get and make sure the password was not updated, but first and last name were
+ u4 = c.xos_orm.User.objects.get(id=orig_id)
+ assert(u4.password=="foobar")
+ assert(u4.firstname == "new_first_name")
+ assert(u4.lastname == "new_last_name")
+
# delete the user
- u3.delete()
+ u4.delete()
# make sure it is deleted
u_all = c.xos_orm.User.objects.all()
diff --git a/xos/xos_client/xosapi/orm.py b/xos/xos_client/xosapi/orm.py
index d23c1b1..1808e08 100644
--- a/xos/xos_client/xosapi/orm.py
+++ b/xos/xos_client/xosapi/orm.py
@@ -140,13 +140,16 @@
self.reverse_cache.clear()
self.poisoned.clear()
- def save(self):
+ def save(self, update_fields=None):
if self.is_new:
new_class = self.stub.invoke("Create%s" % self._wrapped_class.__class__.__name__, self._wrapped_class)
self._wrapped_class = new_class
self.is_new = False
else:
- self.stub.invoke("Update%s" % self._wrapped_class.__class__.__name__, self._wrapped_class)
+ metadata = []
+ if update_fields:
+ metadata.append( ("update_fields", ",".join(update_fields)) )
+ self.stub.invoke("Update%s" % self._wrapped_class.__class__.__name__, self._wrapped_class, metadata=metadata)
def delete(self):
id = self.stub.make_ID(id=self._wrapped_class.id)
@@ -263,7 +266,7 @@
def listObjects(self):
return self.all_model_names
- def invoke(self, name, request):
+ def invoke(self, name, request, metadata=[]):
if self.invoker:
# Hook in place to call Chameleon's invoke method, as soon as we
# have rewritten the synchronizer to use reactor.
@@ -275,7 +278,7 @@
backoff = [0.5, 1, 2, 4, 8]
try:
method = getattr(self.grpc_stub, name)
- return method(request)
+ return method(request, metadata=metadata)
except grpc._channel._Rendezvous, e:
code = e.code()
if code == grpc.StatusCode.UNAVAILABLE:
@@ -294,14 +297,4 @@
return _sym_db._classes["xos.Query"]()
-#def wrap_get(*args, **kwargs):
-# stub=kwargs.pop("stub")
-# getmethod=kwargs.pop("getmethod")
-# result = getmethod(*args, **kwargs)
-# return ORMWrapper(result)
-#
-#def wrap_stub(stub):
-# for name in dir(stub):
-# if name.startswith("Get"):
-# setattr(stub, name, functools.partial(wrap_get, stub=stub, getmethod=getattr(stub,name)))