tmp work on migration doc

This commit is contained in:
Bastien Gérard 2019-06-16 23:05:06 +02:00
parent 2f4464ead5
commit f8d371229e
2 changed files with 71 additions and 0 deletions

View File

@ -16,3 +16,4 @@ User Guide
text-indexes
logging-monitoring
mongomock
migration

70
docs/guide/migration.rst Normal file
View File

@ -0,0 +1,70 @@
==============================
Handling migration
==============================
The structure of your documents and their associated mongoengine schemas are likely
to change over the lifetime of an application. This section provides guidance and
recommendations on how to deal with migrations.
Due to the very flexible nature of mongodb, migrations of models aren't trivial and
for people that know about `alembic` for `sqlalchemy`, there is unfortunately no equivalent
library that will manage the migration in an automatic fashion for mongoengine.
First of all, let's take a simple example of model change and review the different option you
have to deal with the migration.
Let's assume we start with the following schema and save an instance:
.. code-block:: python
class User(Document):
name = StringField()
User(name=username).save()
# print the objects as they exist in mongodb
print(User.objects().as_pymongo()) # [{u'_id': ObjectId('5d06b9c3d7c1f18db3e7c874'), u'name': u'John'}]
On the next version of your application, let's now assume that a new field `enabled` gets added to the
existing User model with a `default=True`. Thus you simply update the `User` class to the following:
.. code-block:: python
class User(Document):
name = StringField(required=True)
enabled = BooleaField(default=True)
Without migration, we now reload an object from the database into the `User` class and checks its `enabled`
attribute:
.. code-block:: python
assert User.objects.count() == 1
user = User.objects().first()
assert user.enabled is True
print(User.objects(enabled=True).count()) # 0 ! uh?
print(User.objects(enabled=False).count()) # 0 ! uh?
# but this is consistent with what we have in database
print(User.objects().as_pymongo().first()) # {u'_id': ObjectId('5d06b9c3d7c1f18db3e7c874'), u'name': u'John'}
assert User.objects(enabled=None).count() == 1
As you can see, even if the document wasn't updated, mongoengine applies the default value seemlessly when it
loads the pymongo dict into a `User` instance. At first sight it looks like you don't need to migrate the
existing documents when adding new fields but this actually leads to inconsistencies when it comes to querying.
In fact, when querying, mongoengine isn't trying to account for the default value of the new field and so
if you don't actually migrate the existing documents, you are taking a risk that querying/updating
will be missing relevant record.
When adding fields/modifying default values, you can use any of the following to do the migration
as a standalone script:
.. code-block:: python
User.objects().update(enabled=True)
# or
user_coll = User._get_collection()
user_coll.update_many({}, {'$set': {'enabled': True}})