Skip to content

Commit f621cac

Browse files
committed
fix: set object updatedAt on creation
1 parent 9b19ef1 commit f621cac

3 files changed

Lines changed: 56 additions & 19 deletions

File tree

leancloud/object_.py

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -334,25 +334,18 @@ def _to_pointer(self):
334334
}
335335

336336
def _merge_metadata(self, server_data):
337-
for key in ("objectId", "createdAt", "updatedAt"):
338-
if server_data.get(key) is None:
339-
continue
340-
if key == "objectId":
341-
self.id = server_data[key]
342-
else:
343-
if isinstance(server_data[key], six.string_types):
344-
dt = utils.decode(key, {"__type": "Date", "iso": server_data[key]})
345-
elif server_data[key]["__type"] == "Date":
346-
dt = utils.decode(key, server_data[key])
347-
else:
348-
raise TypeError("Invalid date type")
349-
server_data[key] = dt
350-
if key == "createdAt":
351-
self.created_at = dt
352-
elif key == "updatedAt":
353-
self.updated_at = dt
354-
else:
355-
raise TypeError
337+
object_id = server_data.get("objectId")
338+
_created_at = utils.decode_date_string(server_data.get("createdAt"))
339+
_updated_at = utils.decode_updated_at(server_data.get("updatedAt"), _created_at)
340+
341+
if object_id is not None:
342+
self.id = object_id
343+
if _created_at is not None:
344+
self.created_at = _created_at
345+
if _updated_at is not None:
346+
self.updated_at = _updated_at
347+
348+
356349

357350
def validate(self, attrs):
358351
if "ACL" in attrs and not isinstance(attrs["ACL"], leancloud.ACL):
@@ -370,6 +363,23 @@ def get(self, attr, default=None, deafult=None):
370363
# for backward compatibility
371364
if (deafult is not None) and (default is None):
372365
default = deafult
366+
367+
# createdAt is stored as string in the cloud but used as datetime object on the client side.
368+
# We need to make sure that `.created_at` and `.get("createdAt")` return the same value.
369+
# Otherwise users will get confused.
370+
if attr == "createdAt":
371+
if self.created_at is None:
372+
return None
373+
else:
374+
return self.created_at
375+
376+
# Similar to createdAt.
377+
if attr == "updatedAt":
378+
if self.updated_at is None:
379+
return None
380+
else:
381+
return self.updated_at
382+
373383
return self._attributes.get(attr, default)
374384

375385
def relation(self, attr):

leancloud/utils.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,32 @@ def decode(key, value):
146146
return f
147147

148148

149+
def decode_date_string(date_or_string):
150+
if date_or_string is None:
151+
return None
152+
elif isinstance(date_or_string, six.string_types):
153+
return decode_date_string({"__type": "Date", "iso": date_or_string})
154+
elif date_or_string["__type"] == "Date":
155+
return arrow.get(iso8601.parse_date(date_or_string["iso"])).to("local").datetime
156+
else:
157+
raise TypeError("Invalid date type")
158+
159+
160+
def decode_updated_at(updated_at_date_string, created_at_datetime):
161+
updated_at = decode_date_string(updated_at_date_string)
162+
if updated_at is None:
163+
if created_at_datetime is None:
164+
return None
165+
else:
166+
# When a new object is created, updatedAt will be set as the same value as createdAt on the cloud.
167+
# However, the cloud will only return objectId and createdAt in REST API response, without updatedAt.
168+
# Thus we need to set updatedAt as the same value as createdAt, consistent with the value on the cloud.
169+
# This behaviour is consistent with other SDKs such as JavaScript and Go.
170+
return created_at_datetime
171+
else:
172+
return updated_at
173+
174+
149175
def traverse_object(obj, callback, seen=None):
150176
seen = seen or set()
151177

tests/test_object.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ def test_fetch_when_save(): # type: () -> None
314314
foo = Foo()
315315
foo.set("counter", 1)
316316
foo.save()
317+
assert foo.created_at == foo.updated_at
317318
assert foo.get("counter") == 1
318319

319320
foo_from_other_thread = leancloud.Query(Foo).get(foo.id)

0 commit comments

Comments
 (0)