tmp work on migration doc
This commit is contained in:
		
							
								
								
									
										70
									
								
								docs/guide/migration.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								docs/guide/migration.rst
									
									
									
									
									
										Normal 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}}) | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user