ReferenceField now store ObjectId's by default rather than DBRef (#290)
This commit is contained in:
parent
c59ea26845
commit
c60ea40828
@ -4,6 +4,7 @@ Changelog
|
|||||||
|
|
||||||
Changes in 0.8.X
|
Changes in 0.8.X
|
||||||
================
|
================
|
||||||
|
- ReferenceField now store ObjectId's by default rather than DBRef (#290)
|
||||||
- Added ImageField support for inline replacements (#86)
|
- Added ImageField support for inline replacements (#86)
|
||||||
- Added SequenceField.set_next_value(value) helper (#159)
|
- Added SequenceField.set_next_value(value) helper (#159)
|
||||||
- Updated .only() behaviour - now like exclude it is chainable (#202)
|
- Updated .only() behaviour - now like exclude it is chainable (#202)
|
||||||
|
@ -76,6 +76,35 @@ the case and the data is set only in the ``document._data`` dictionary: ::
|
|||||||
File "<stdin>", line 1, in <module>
|
File "<stdin>", line 1, in <module>
|
||||||
AttributeError: 'Animal' object has no attribute 'size'
|
AttributeError: 'Animal' object has no attribute 'size'
|
||||||
|
|
||||||
|
ReferenceField
|
||||||
|
--------------
|
||||||
|
|
||||||
|
ReferenceFields now store ObjectId's by default - this is more efficient than
|
||||||
|
DBRefs as we already know what Document types they reference.
|
||||||
|
|
||||||
|
# Old code
|
||||||
|
class Animal(Document):
|
||||||
|
name = ReferenceField('self')
|
||||||
|
|
||||||
|
# New code to keep dbrefs
|
||||||
|
class Animal(Document):
|
||||||
|
name = ReferenceField('self', dbref=True)
|
||||||
|
|
||||||
|
To migrate all the references you need to touch each object and mark it as dirty
|
||||||
|
eg::
|
||||||
|
|
||||||
|
# Doc definition
|
||||||
|
class Person(Document):
|
||||||
|
name = StringField()
|
||||||
|
parent = ReferenceField('self')
|
||||||
|
friends = ListField(ReferenceField('self'))
|
||||||
|
|
||||||
|
# Mark all ReferenceFields as dirty and save
|
||||||
|
for p in Person.objects:
|
||||||
|
p._mark_as_dirty('parent')
|
||||||
|
p._mark_as_dirty('friends')
|
||||||
|
p.save()
|
||||||
|
|
||||||
Querysets
|
Querysets
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
@ -772,7 +772,7 @@ class ReferenceField(BaseField):
|
|||||||
.. versionchanged:: 0.5 added `reverse_delete_rule`
|
.. versionchanged:: 0.5 added `reverse_delete_rule`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, document_type, dbref=None,
|
def __init__(self, document_type, dbref=False,
|
||||||
reverse_delete_rule=DO_NOTHING, **kwargs):
|
reverse_delete_rule=DO_NOTHING, **kwargs):
|
||||||
"""Initialises the Reference Field.
|
"""Initialises the Reference Field.
|
||||||
|
|
||||||
@ -786,12 +786,7 @@ class ReferenceField(BaseField):
|
|||||||
self.error('Argument to ReferenceField constructor must be a '
|
self.error('Argument to ReferenceField constructor must be a '
|
||||||
'document class or a string')
|
'document class or a string')
|
||||||
|
|
||||||
if dbref is None:
|
self.dbref = dbref
|
||||||
msg = ("ReferenceFields will default to using ObjectId "
|
|
||||||
"in 0.8, set DBRef=True if this isn't desired")
|
|
||||||
warnings.warn(msg, FutureWarning)
|
|
||||||
|
|
||||||
self.dbref = dbref if dbref is not None else True # To change in 0.8
|
|
||||||
self.document_type_obj = document_type
|
self.document_type_obj = document_type
|
||||||
self.reverse_delete_rule = reverse_delete_rule
|
self.reverse_delete_rule = reverse_delete_rule
|
||||||
super(ReferenceField, self).__init__(**kwargs)
|
super(ReferenceField, self).__init__(**kwargs)
|
||||||
|
@ -30,28 +30,6 @@ class AllWarnings(unittest.TestCase):
|
|||||||
# restore default handling of warnings
|
# restore default handling of warnings
|
||||||
warnings.showwarning = self.showwarning_default
|
warnings.showwarning = self.showwarning_default
|
||||||
|
|
||||||
def test_dbref_reference_field_future_warning(self):
|
|
||||||
|
|
||||||
class Person(Document):
|
|
||||||
name = StringField()
|
|
||||||
parent = ReferenceField('self')
|
|
||||||
|
|
||||||
Person.drop_collection()
|
|
||||||
|
|
||||||
p1 = Person()
|
|
||||||
p1.parent = None
|
|
||||||
p1.save()
|
|
||||||
|
|
||||||
p2 = Person(name="Wilson Jr")
|
|
||||||
p2.parent = p1
|
|
||||||
p2.save(cascade=False)
|
|
||||||
|
|
||||||
self.assertTrue(len(self.warning_list) > 0)
|
|
||||||
warning = self.warning_list[0]
|
|
||||||
self.assertEqual(FutureWarning, warning["category"])
|
|
||||||
self.assertTrue("ReferenceFields will default to using ObjectId"
|
|
||||||
in str(warning["message"]))
|
|
||||||
|
|
||||||
def test_document_save_cascade_future_warning(self):
|
def test_document_save_cascade_future_warning(self):
|
||||||
|
|
||||||
class Person(Document):
|
class Person(Document):
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
from convert_to_new_inheritance_model import *
|
||||||
|
from refrencefield_dbref_to_object_id import *
|
||||||
from turn_off_inheritance import *
|
from turn_off_inheritance import *
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -38,7 +38,7 @@ class ConvertToNewInheritanceModel(unittest.TestCase):
|
|||||||
|
|
||||||
# 3. Confirm extra data is removed
|
# 3. Confirm extra data is removed
|
||||||
count = collection.find({'_types': {"$exists": True}}).count()
|
count = collection.find({'_types': {"$exists": True}}).count()
|
||||||
assert count == 0
|
self.assertEqual(0, count)
|
||||||
|
|
||||||
# 4. Remove indexes
|
# 4. Remove indexes
|
||||||
info = collection.index_information()
|
info = collection.index_information()
|
52
tests/migration/refrencefield_dbref_to_object_id.py
Normal file
52
tests/migration/refrencefield_dbref_to_object_id.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from mongoengine import Document, connect
|
||||||
|
from mongoengine.connection import get_db
|
||||||
|
from mongoengine.fields import StringField, ReferenceField, ListField
|
||||||
|
|
||||||
|
__all__ = ('ConvertToObjectIdsModel', )
|
||||||
|
|
||||||
|
|
||||||
|
class ConvertToObjectIdsModel(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
connect(db='mongoenginetest')
|
||||||
|
self.db = get_db()
|
||||||
|
|
||||||
|
def test_how_to_convert_to_object_id_reference_fields(self):
|
||||||
|
"""Demonstrates migrating from 0.7 to 0.8
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 1. Old definition - using dbrefs
|
||||||
|
class Person(Document):
|
||||||
|
name = StringField()
|
||||||
|
parent = ReferenceField('self', dbref=True)
|
||||||
|
friends = ListField(ReferenceField('self', dbref=True))
|
||||||
|
|
||||||
|
Person.drop_collection()
|
||||||
|
|
||||||
|
p1 = Person(name="Wilson", parent=None).save()
|
||||||
|
f1 = Person(name="John", parent=None).save()
|
||||||
|
f2 = Person(name="Paul", parent=None).save()
|
||||||
|
f3 = Person(name="George", parent=None).save()
|
||||||
|
f4 = Person(name="Ringo", parent=None).save()
|
||||||
|
Person(name="Wilson Jr", parent=p1, friends=[f1, f2, f3, f4]).save()
|
||||||
|
|
||||||
|
# 2. Start the migration by changing the schema
|
||||||
|
# Change ReferenceField as now dbref defaults to False
|
||||||
|
class Person(Document):
|
||||||
|
name = StringField()
|
||||||
|
parent = ReferenceField('self')
|
||||||
|
friends = ListField(ReferenceField('self'))
|
||||||
|
|
||||||
|
# 3. Loop all the objects and mark parent as changed
|
||||||
|
for p in Person.objects:
|
||||||
|
p._mark_as_changed('parent')
|
||||||
|
p._mark_as_changed('friends')
|
||||||
|
p.save()
|
||||||
|
|
||||||
|
# 4. Confirmation of the fix!
|
||||||
|
wilson = Person.objects(name="Wilson Jr").as_pymongo()[0]
|
||||||
|
self.assertEqual(p1.id, wilson['parent'])
|
||||||
|
self.assertEqual([f1.id, f2.id, f3.id, f4.id], wilson['friends'])
|
@ -72,6 +72,7 @@ class SignalTests(unittest.TestCase):
|
|||||||
else:
|
else:
|
||||||
signal_output.append('Not loaded')
|
signal_output.append('Not loaded')
|
||||||
self.Author = Author
|
self.Author = Author
|
||||||
|
Author.drop_collection()
|
||||||
|
|
||||||
class Another(Document):
|
class Another(Document):
|
||||||
name = StringField()
|
name = StringField()
|
||||||
@ -110,6 +111,7 @@ class SignalTests(unittest.TestCase):
|
|||||||
signal_output.append('post_delete Another signal, %s' % document)
|
signal_output.append('post_delete Another signal, %s' % document)
|
||||||
|
|
||||||
self.Another = Another
|
self.Another = Another
|
||||||
|
Another.drop_collection()
|
||||||
|
|
||||||
class ExplicitId(Document):
|
class ExplicitId(Document):
|
||||||
id = IntField(primary_key=True)
|
id = IntField(primary_key=True)
|
||||||
@ -123,7 +125,8 @@ class SignalTests(unittest.TestCase):
|
|||||||
signal_output.append('Is updated')
|
signal_output.append('Is updated')
|
||||||
|
|
||||||
self.ExplicitId = ExplicitId
|
self.ExplicitId = ExplicitId
|
||||||
self.ExplicitId.objects.delete()
|
ExplicitId.drop_collection()
|
||||||
|
|
||||||
# Save up the number of connected signals so that we can check at the
|
# Save up the number of connected signals so that we can check at the
|
||||||
# end that all the signals we register get properly unregistered
|
# end that all the signals we register get properly unregistered
|
||||||
self.pre_signals = (
|
self.pre_signals = (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user