Skip to content

Commit f8d3712

Browse files
committed
tmp work on migration doc
1 parent 2f4464e commit f8d3712

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

docs/guide/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ User Guide
1616
text-indexes
1717
logging-monitoring
1818
mongomock
19+
migration

docs/guide/migration.rst

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
==============================
2+
Handling migration
3+
==============================
4+
5+
The structure of your documents and their associated mongoengine schemas are likely
6+
to change over the lifetime of an application. This section provides guidance and
7+
recommendations on how to deal with migrations.
8+
9+
Due to the very flexible nature of mongodb, migrations of models aren't trivial and
10+
for people that know about `alembic` for `sqlalchemy`, there is unfortunately no equivalent
11+
library that will manage the migration in an automatic fashion for mongoengine.
12+
13+
First of all, let's take a simple example of model change and review the different option you
14+
have to deal with the migration.
15+
16+
Let's assume we start with the following schema and save an instance:
17+
18+
.. code-block:: python
19+
20+
class User(Document):
21+
name = StringField()
22+
23+
User(name=username).save()
24+
25+
# print the objects as they exist in mongodb
26+
print(User.objects().as_pymongo()) # [{u'_id': ObjectId('5d06b9c3d7c1f18db3e7c874'), u'name': u'John'}]
27+
28+
On the next version of your application, let's now assume that a new field `enabled` gets added to the
29+
existing User model with a `default=True`. Thus you simply update the `User` class to the following:
30+
31+
.. code-block:: python
32+
33+
class User(Document):
34+
name = StringField(required=True)
35+
enabled = BooleaField(default=True)
36+
37+
Without migration, we now reload an object from the database into the `User` class and checks its `enabled`
38+
attribute:
39+
40+
.. code-block:: python
41+
42+
assert User.objects.count() == 1
43+
user = User.objects().first()
44+
assert user.enabled is True
45+
print(User.objects(enabled=True).count()) # 0 ! uh?
46+
print(User.objects(enabled=False).count()) # 0 ! uh?
47+
48+
# but this is consistent with what we have in database
49+
print(User.objects().as_pymongo().first()) # {u'_id': ObjectId('5d06b9c3d7c1f18db3e7c874'), u'name': u'John'}
50+
assert User.objects(enabled=None).count() == 1
51+
52+
As you can see, even if the document wasn't updated, mongoengine applies the default value seemlessly when it
53+
loads the pymongo dict into a `User` instance. At first sight it looks like you don't need to migrate the
54+
existing documents when adding new fields but this actually leads to inconsistencies when it comes to querying.
55+
56+
In fact, when querying, mongoengine isn't trying to account for the default value of the new field and so
57+
if you don't actually migrate the existing documents, you are taking a risk that querying/updating
58+
will be missing relevant record.
59+
60+
When adding fields/modifying default values, you can use any of the following to do the migration
61+
as a standalone script:
62+
63+
.. code-block:: python
64+
65+
User.objects().update(enabled=True)
66+
# or
67+
user_coll = User._get_collection()
68+
user_coll.update_many({}, {'$set': {'enabled': True}})
69+
70+

0 commit comments

Comments
 (0)