Миграции в mongoengine: InvalidId

Работя с mongoengine и се опитвам да направя проста миграция. Имам поле, което бих искал да мигрирам от StringField към ReferenceField към друг обект. Планирах да направя миграцията на ръка, като първо конструирам новия обект въз основа на низа, който идва от стария StringField и след това го задам изрично.

Проблемът е, че вече дори не мога да получа достъп до един от документите от най-високо ниво, след като промених типа на полето. Необходимо ли е да се създаде "фиктивно" поле в кода на класа на моя документ като контейнер за новото поле? Това ми се струва тъпо, така че предполагам, че има по-добър начин?

Тук е грешката, която се дължи на това, че полето, излизащо от DB (StringField), не е в съответствие с референтно поле.

/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

person dave    schedule 16.12.2013    source източник


Отговори (2)


Винаги съм мигрирал неща на ръка, използвайки междинна колекция или междинно поле в същата колекция, но този пример изглежда така, сякаш не е нужно. Stack overflow мрази външни връзки, така че включвам примера дословно по-долу. Между другото внимавайте с тази част „drop_collection“!

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()
person mvr    schedule 06.06.2015

Двете опции, които бих предложил за вашия скрипт за миграция:

  • използването на DynamicField в полето, което трябва да мигрирате, трябва да ви позволи да четете ObjectIds и да съхранявате обратно DBRefs
  • извършване на миграцията в pymongo директно (колекцията на pymongo е достъпна чрез Person._get_collection()) и преминаване през елементите, актуализиране и запазване
person bagerard    schedule 01.06.2016