web-dev-qa-db-ja.com

python bson.errors.InvalidDocument:オブジェクトをエンコードできません:datetime.date(2015、3、1)

私は次の機能を持っています:

_#  this is in a module called 'dbw_lib'
def dateTimeOuput(start_days_back, end_days_back):
    start_delta = datetime.timedelta(days=start_days_back)
    end_delta = datetime.timedelta(days=end_days_back)
    start_date = datetime.date.today() - start_delta
    end_date = datetime.date.today() - end_delta
    return start_date, end_date

def dictByDate(start_days, end_days):
    start_date, end_date = dbw_lib.dateTimeOuput(start_days, end_days)

    date_string = { "created_at": {"$gte" : start_date, "$lt": end_date }  }

    user_id_email_dict = dbw_lib.dbwIdToEmailD(user_coll_obj, date_query = date_string)  # dict of all user ids and emails  
    print user_id_email_dict
    quit()
_

key_dicts = dictByDate(90, 60)を実行すると、次のトレースバックが発生します。

_Traceback (most recent call last):
File "main.py", line 212, in <module>
program.runProgram()
File "main.py", line 61, in runProgram
report.RcreateReport()
File "filepath/report.py", line 86, in RcreateReport
key_dicts = dictByDate(90, 60)
File "filepath/report.py", line 65, in dictByDate
user_id_email_dict = dbw_lib.dbwIdToEmailD(user_coll_obj, date_query = date_string)  # dict of all user ids and emails  
File "filepath/dbw_lib.py", line 50, in dbwIdToEmailD
for pair in id_email_cursor:
File "/Library/Python/2.7/site-packages/pymongo-3.0-py2.7-macosx-10.9-intel.Egg/pymongo/cursor.py", line 968, in __next__
File "/Library/Python/2.7/site-packages/pymongo-3.0-py2.7-macosx-10.9-intel.Egg/pymongo/cursor.py", line 905, in _refresh
File "/Library/Python/2.7/site-packages/pymongo-3.0-py2.7-macosx-10.9-intel.Egg/pymongo/cursor.py", line 812, in __send_message
File "/Library/Python/2.7/site-packages/pymongo-3.0-py2.7-macosx-10.9-intel.Egg/pymongo/mongo_client.py", line 732, in _send_message_with_response
File "/Library/Python/2.7/site-packages/pymongo-3.0-py2.7-macosx-10.9-intel.Egg/pymongo/mongo_client.py", line 743, in _reset_on_error
File "/Library/Python/2.7/site-packages/pymongo-3.0-py2.7-macosx-10.9-intel.Egg/pymongo/server.py", line 85, in send_message_with_response
File "/Library/Python/2.7/site-packages/pymongo-3.0-py2.7-macosx-10.9-intel.Egg/pymongo/message.py", line 107, in get_message
bson.errors.InvalidDocument: Cannot encode object: datetime.date(2015, 3, 1)
_
13
dwstein

交換するだけ

datetime.date.today()

datetime.datetime.today()
13
W.Sun

それが判明しました

PyMongoは、日付インスタンスの保存をサポートしていません。サーバーには時刻のない日付のタイプがないため、時刻のない日付を保存するために使用される規則が必要になります。日付を保存する必要がある場合、クライアントはそれを日時インスタンスに変換する必要があり、それを保存できます。

引用のソース

この回答 は、次のようにdatetime.datetime.combineメソッドを使用してこれを実行できることを示しています。

datetime.datetime.combine(dateWithoutTime, datetime.time.min)
7
Stunner

datetime.dateはbsonエンコーダーの一部ではありません。 (たぶん、そのように日付に時間情報を追加するのが曖昧なので、作者はそれを忘れたか、意図的に省略しました。)

ただし、JSONEncoderjsonを拡張するのと同じように、pymongoでカスタム型を拡張する関数を作成できます。 SONManipulatorを使用して、pymongoで同様のことを行うことができます。

import datetime
import pymongo

class MigrationTransformer(pymongo.son_manipulator.SONManipulator):

    def _encode_date(self, value):
        return datetime.datetime.combine(
                value,
                datetime.datetime.min.time())

    def transform_incoming(self, son, collection):
        for (key, value) in son.items():
            # datetime.datetime is instance of datetime.date
            # compare type explicitly only
            if type(value) == datetime.date:
                son[key] = self._encode_date(value)
            Elif isinstance(value, dict):    # recurse into sub-docs
                son[key] = self.transform_incoming(value, collection)
        return son

次に、それをDBインスタンスに追加します。

db.add_son_manipulator(MigrationTransformer())

(質問に関係がないため、transform_outgoingメソッドを指定していませんが、ここで見つけることができます: http://api.mongodb.org/python/current/examples/custom_type。 html

編集:dictのキーの値がリスト型の場合は問題があります。何らかの理由で、pymongoはそれをSONManipulatorに渡しません。したがって、リストは変換されません。

これも処理するようにクラスを更新しました(ただし、setstuplesでは更新しませんでした)。

class MigrationTransformer(SONManipulator):

    def _encode_date(self, value):
        return datetime.datetime.combine(
                value,
                datetime.datetime.min.time())

    def _handle_list(self, value):
        for index, item in enumerate(value):
            if isinstance(item, dict):
                value[index] = self._handle_dict(item)
            Elif isinstance(item, list):
                value[index] = self._handle_list(item)
            Elif isinstance(item, datetime.date):
                value[index] = self._encode_date(item)
        return value

    def _handle_dict(self, item):
        for (key, value) in item.items():
            if type(value) == datetime.date:
                item[key] = self._encode_date(value)
            Elif isinstance(value, dict):    # recurse into sub-docs
                item[key] = self._handle_dict(value)
            Elif isinstance(value, list):    # recurse into sub-docs
                item[key] = self._handle_list(value)
        return item

    def transform_incoming(self, son, collection):
        for (key, value) in son.items():
            # datetime.datetime is instance of datetime.date
            # compare type explicitly only
            if type(value) == datetime.date:
                son[key] = self._encode_date(value)
            Elif isinstance(value, dict):    # recurse into sub-docs
                son[key] = self.transform_incoming(value, collection)
            Elif isinstance(value, list):    # recurse into sub-docs
                son[key] = self._handle_list(value)
        return son
4
kunl