Migrations in mongoengine: InvalidId

只谈情不闲聊 提交于 2021-02-06 09:12:57

问题


I am working with mongoengine and trying to do a simple migration. I have a field which I would like to migrate from being a StringField to a ReferenceField to another Object. I planned on doing the migration by hand, first constructing the new object based on the string that comes from the old StringField and then setting it explicitly.

The problem is I cannot even access one of the top level documents anymore once I changed the field type. Is it required to create a "dummy" field in the my document's class code as a placeholder for the new field? This seems kludgy to me so I assume there is a better way?

Here is the error, which is because the field coming out of the DB (StringField) is not consistent with a reference field.

/usr/lib/python2.7/site-packages/mongoengine/queryset/base.pyc in __getitem__(self, key)
    149                 return queryset._get_as_pymongo(queryset._cursor.next())
    150             return queryset._document._from_son(queryset._cursor[key],
--> 151                                                 _auto_dereference=self._auto_dereference)
    152         raise AttributeError
    153 

/usr/lib/python2.7/site-packages/mongoengine/base/document.pyc in _from_son(cls, son, _auto_dereference)
    568                 try:
    569                     data[field_name] = (value if value is None
--> 570                                         else field.to_python(value))
    571                     if field_name != field.db_field:
    572                         del data[field.db_field]

/usr/lib/python2.7/site-packages/mongoengine/fields.pyc in to_python(self, value)
    935            not isinstance(value, (DBRef, Document, EmbeddedDocument))):
    936             collection = self.document_type._get_collection_name()
--> 937             value = DBRef(collection, self.document_type.id.to_python(value))
    938         return value
    939 

/usr/lib/python2.7/site-packages/mongoengine/base/fields.pyc in to_python(self, value)
    390     def to_python(self, value):
    391         if not isinstance(value, ObjectId):
--> 392             value = ObjectId(value)
    393         return value
    394 

/usr/lib/python2.7/site-packages/bson/objectid.pyc in __init__(self, oid)
     88             self.__generate()
     89         else:
---> 90             self.__validate(oid)
     91 
     92     @classmethod

/usr/lib/python2.7/site-packages/bson/objectid.pyc in __validate(self, oid)
    192                     raise InvalidId("%s is not a valid ObjectId" % oid)
    193             else:
--> 194                 raise InvalidId("%s is not a valid ObjectId" % oid)
    195         else:
    196             raise TypeError("id must be an instance of (%s, %s, ObjectId), "

InvalidId: Was Dirty: a2111fe89383bb562738b81c2b63fe78e877ed32 is not a valid ObjectId

回答1:


I've always migrated things by hand using an intermediate collection or an intermediate field in the same collection, but this example makes it look like you don't have to. Stack overflow hates external links so I'm including the example verbatim below. BTW be careful with that "drop_collection" part!

import unittest
from mongoengine import *


class Test(unittest.TestCase):

    def setUp(self):
        conn = connect(db='mongoenginetest')

    def create_old_data(self):
        class Person(Document):
            name = StringField()
            age = FloatField()  # Save as string

            meta = {'allow_inheritance': True}

        Person.drop_collection()

        Person(name="Rozza", age=35.00).save()

        rozza = Person._get_collection().find_one()
        self.assertEqual(35.00, rozza['age'])

    def test_migration(self):

        self.create_old_data()

        class Person(Document):
            name = StringField()
            age = StringField()

            meta = {'allow_inheritance': True}

        for p in Person.objects:
            p.age = "%s" % p.age
            p.save()

        rozza = Person.objects.first()
        self.assertEqual("35.0", rozza.age)

if __name__ == '__main__':
    unittest.main()



回答2:


The 2 options I would suggest for your migration script:

  • using DynamicField on the field you must migrate should allow you to read ObjectIds and store back DBRefs
  • doing the migration in pymongo directly (pymongo's collection is accessible through Person._get_collection()) and looping over the items, updating & saving


来源:https://stackoverflow.com/questions/20618863/migrations-in-mongoengine-invalidid

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!