AttributeError: _nanosecond при обновлении даты и времени в транзакции - PullRequest
0 голосов
/ 25 января 2019

Поэтому я пытаюсь обновить поле даты и времени в облачном хранилище с помощью облачной функции следующим образом:

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

Я конвертирую его в объект datetime следующим образом:

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

Traceback:
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

Разрешено ли устанавливать время с помощью транзакций или я что-то здесь упускаю?

EDIT:
фрагмент кода:

@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. Я пытаюсь преобразовать дату в UTC, изменив часовой пояс с помощью pytz

РЕДАКТИРОВАТЬ 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:

Скриншот параметра события: enter image description here

Снимок экрана консоли:
enter image description here

1 Ответ

0 голосов
/ 27 января 2019

Таким образом, firestore python sdk ожидает атрибут _nanosecond, который в данный момент недоступен в datetime стандартной библиотеки python (будет добавлен в будущем. Подробнее здесь )

Итак, изучив их кодовую базу, я нашел класс с именем DatetimeWithNanoseconds, который добавляет поддержку наносекунд к традиционному объекту datetime .

Код для класса (файл datetime_helpers.py в google / api_core) выглядит следующим образом (некоторые части намеренно удалены для краткости):

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().

* +1034 * Пример: * * одна тысяча тридцать пять
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, которая может увеличить задержку вызова при холодном запуске. Подробнее здесь .

Надеюсь, это поможет.

...