【问题标题】:AttributeError: _nanosecond when updating a datetime in transactionAttributeError: _nanosecond 在更新事务中的日期时间时
【发布时间】:2019-01-25 17:20:45
【问题描述】:

所以我正在尝试通过云功能更新云 Firestore 中的日期时间字段,如下所示:

transaction.update(doc_ref, {'dateTimeField1': dateTimeValue})

Google 以%Y-%m-%dT%H:%M:%SZ%Y-%m-%dT%H:%M:%S.%fZ 格式在云函数的事件参数中将日期时间对象作为字符串发送。
例如:2019-01-25T15:25:03.881Z

我将它转换为日期时间对象如下:

try:
    datetime_obj = datetime.datetime.strptime(datetime_obj, '%Y-%m-%dT%H:%M:%S.%fZ')
except:
    datetime_obj = datetime.datetime.strptime(datetime_obj, '%Y-%m-%dT%H:%M:%SZ')
datetime_obj = datetime_obj.replace(tzinfo=timezone('UTC'))

但是当我尝试执行该操作时,我遇到了以下错误: AttributeError: _nanosecond

回溯:
File "/env/local/lib/python3.7/site-packages/google/cloud/firestore_v1beta1/batch.py", line 112, in update reference._document_path, field_updates, option File "/env/local/lib/python3.7/site-packages/google/cloud/firestore_v1beta1/_helpers.py", line 822, in pbs_for_update update_pb = extractor.get_update_pb(document_path) File "/env/local/lib/python3.7/site-packages/google/cloud/firestore_v1beta1/_helpers.py", line 459, in get_update_pb name=document_path, fields=encode_dict(self.set_fields) File "/env/local/lib/python3.7/site-packages/google/cloud/firestore_v1beta1/_helpers.py", line 215, in encode_dict return {key: encode_value(value) for key, value in six.iteritems(values_dict)} File "/env/local/lib/python3.7/site-packages/google/cloud/firestore_v1beta1/_helpers.py", line 215, in <dictcomp> return {key: encode_value(value) for key, value in six.iteritems(values_dict)} File "/env/local/lib/python3.7/site-packages/google/cloud/firestore_v1beta1/_helpers.py", line 169, in encode_value return document_pb2.Value(timestamp_value=value.timestamp_pb()) File "/env/local/lib/python3.7/site-packages/google/api_core/datetime_helpers.py", line 278, in timestamp_pb nanos = self._nanosecond or self.microsecond * 1000 AttributeError: _nanosecond

是否允许通过交易设置日期时间,或者我在这里遗漏了什么?

编辑:
代码sn-p:

@firestore.transactional
def update_datetime_field(transaction, doc_ref, datetime_value):
    try:
        datetime_obj = datetime.datetime.strptime(datetime_value, '%Y-%m-%dT%H:%M:%S.%fZ')
    except:
        datetime_obj = datetime.datetime.strptime(datetime_value, '%Y-%m-%dT%H:%M:%SZ')
    datetime_obj = datetime_obj.replace(tzinfo=timezone('UTC'))
    # Example of datetime_obj -> datetime.datetime(2019, 1, 25, 15, 25, 3, 881000, tzinfo=<UTC>)
    transaction.update(doc_ref, {'datetimeField1': datetime_obj})
    return True

更多信息:

  1. 当文档更新时触发上述代码 collection1/document1/collection2/document2
  2. datetime 对象是标准库中 python 的日期时间
  3. 我正在尝试通过使用 pytz 更改时区来将日期转换为 UTC

编辑 2:

更好的全图:

from firebase_admin import credentials, firestore

# initialize firebase admin sdk
creds = credentials.ApplicationDefault()
firebase_admin.initialize_app(creds,{'projectId': 'myProjectId'})


@firestore.transactional
def update_datetime_field(transaction, doc_ref, datetime_value):
    try:
        datetime_obj = datetime.datetime.strptime(datetime_value, '%Y-%m-%dT%H:%M:%S.%fZ')
    except:
        datetime_obj = datetime.datetime.strptime(datetime_value, '%Y-%m-%dT%H:%M:%SZ')
    datetime_obj = datetime_obj.replace(tzinfo=timezone('UTC'))
    # Example of datetime_obj -> datetime.datetime(2019, 1, 25, 15, 25, 3, 881000, tzinfo=<UTC>)
    transaction.update(doc_ref, {'datetimeField1': datetime_obj})
    return True

def update_datetime_in_transaction(event, context):
    datetime_value = event['value']['fields']['datetimeField1']['timestampValue']
    # this looks something like 2019-01-25T15:25:03.881Z

    # prepare document reference to document
    doc_ref = prepare_doc_ref(event, context)

    # update_datetime_field
    client = firestore.client()
    transaction = client.transaction()
    update_datetime_field(transaction, doc_ref, datetime_value)

    return True

编辑 3:

事件参数截图:

控制台截图:

【问题讨论】:

  • 我不相信日期时间支持纳秒。
  • 请编辑问题以显示重现问题的完整、最少的代码。或者,换句话说,显示您的整个 Cloud Function 代码,以及您正在做什么来触发它。
  • 另外,请解释一下“日期时间”对象是什么意思。 Firestore 作为原生 Timestamp 类型字段,与许多系统所称的“日期时间”不同。
  • @DougStevenson 用简约代码 sn-p 更新了问题
  • 如果我没记错的话,你的代码中就有错误。 strptime() 的参数应该是 datetime_value,而不是 datetime_obj

标签: python firebase transactions google-cloud-firestore firebase-admin


【解决方案1】:

因此,firestore python sdk 需要 _nanosecond 属性,该属性目前在 python 标准库的日期时间中不可用(将来会添加。更多详细信息 here

所以在检查了他们的代码库后,我发现了一个名为 DatetimeWithNanoseconds 的类,它为传统的日期时间对象添加了纳秒支持

该类的代码(google/api_core 中的 datetime_helpers.py 文件)如下(为简洁起见,特意删除了某些部分):

class DatetimeWithNanoseconds(datetime.datetime):
"""Track nanosecond in addition to normal datetime attrs.

Nanosecond can be passed only as a keyword argument.
"""
__slots__ = ('_nanosecond',)

@classmethod
def from_rfc3339(cls, stamp):
    with_nanos = _RFC3339_NANOS.match(stamp)
    if with_nanos is None:
        raise ValueError(
            'Timestamp: {}, does not match pattern: {}'.format(
                stamp, _RFC3339_NANOS.pattern))
    bare = datetime.datetime.strptime(
        with_nanos.group('no_fraction'), _RFC3339_NO_FRACTION)
    fraction = with_nanos.group('nanos')
    if fraction is None:
        nanos = 0
    else:
        scale = 9 - len(fraction)
        nanos = int(fraction) * (10 ** scale)
    return cls(bare.year, bare.month, bare.day,
               bare.hour, bare.minute, bare.second,
               nanosecond=nanos, tzinfo=pytz.UTC)

所以现在,我可以使用这个类而不是datetime.datetime 来解析使用DatetimeWithNanoseconds.from_rfc3339(timestamp) 方法在云函数的事件参数中作为字符串发送的日期时间。

例子:

from google.api_core.datetime_helpers import DatetimeWithNanoseconds

d1 = DatetimeWithNanoseconds.from_rfc3339('2019-01-25T15:25:03.881Z')
print(d1)
# DatetimeWithNanoseconds(2019, 1, 25, 15, 25, 3, 881000, tzinfo=<UTC>)

该类还有rfc3339() 方法为您提供字符串表示。

例子:

d1.rfc3339()
# 2019-01-25T15:25:03.881Z

替代解决方案:

您也可以使用pandas.Timestamp() 代替DatetimeWithNanoseconds.from_rfc3339()

例子:

import pandas as pd

d1 = pd.Timestamp('2019-01-25T15:25:03.881Z')
print(d1)
# Timestamp('2019-01-25 15:25:03.881000+0000', tz='UTC')

我建议使用DatetimeWithNanoseconds,因为它与sdk 一起提供,您无需在requirements.txt 中添加额外的pandas 依赖项,这会增加冷启动期间的调用延迟。更多详情here.

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 2021-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多