2
# -*- coding: utf-8 -*-
3
# Copyright (c) 2009-2011, Nicolas Clairon
5
# Redistribution and use in source and binary forms, with or without
6
# modification, are permitted provided that the following conditions are met:
8
# * Redistributions of source code must retain the above copyright
9
# notice, this list of conditions and the following disclaimer.
10
# * Redistributions in binary form must reproduce the above copyright
11
# notice, this list of conditions and the following disclaimer in the
12
# documentation and/or other materials provided with the distribution.
13
# * Neither the name of the University of California, Berkeley nor the
14
# names of its contributors may be used to endorse or promote products
15
# derived from this software without specific prior written permission.
17
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
18
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
21
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
logging.basicConfig(level=logging.DEBUG)
33
from mongokit import *
34
from pymongo.objectid import ObjectId
36
class AutoRefTestCase(unittest.TestCase):
37
"""Tests AutoRef case"""
39
self.connection = Connection()
40
self.col = self.connection['test']['mongokit']
43
self.connection.drop_database('test')
44
self.connection.drop_database('test2')
46
def test_simple_autoref(self):
51
self.connection.register([DocA])
53
doca = self.col.DocA()
63
self.connection.register([DocB])
65
docb = self.col.DocB()
66
# the structure is automaticly filled by the corresponding structure
67
assert docb == {'b': {'doc_a':None}}, docb
70
docb['b']['doc_a'] = 4
71
self.assertRaises(SchemaTypeError, docb.validate)
72
docb['b']['doc_a'] = doca
73
assert docb == {'b': {'doc_a': {'a': {'foo': 3}, '_id': 'doca'}}, '_id': 'docb'}
75
saved_docb = self.col.find_one({'_id':'docb'})
76
_docb = self.col.DocB.get_from_id('docb')
77
assert saved_docb['b']['doc_a'] == DBRef(database='test', collection='mongokit', id='doca'), saved_docb['b']['doc_a']
79
docb_list = list(self.col.DocB.fetch())
80
assert len(docb_list) == 1
81
new_docb = docb_list[0]
82
assert isinstance(new_docb['b']['doc_a'], DocA), new_docb['b']['doc_a'].__class__
83
assert docb == {'b': {'doc_a': {'a': {'foo': 3}, '_id': 'doca'}}, '_id': 'docb'}, docb
84
assert docb['b']['doc_a']['a']['foo'] == 3
85
docb['b']['doc_a']['a']['foo'] = 4
87
assert docb['b']['doc_a']['a']['foo'] == 4, docb
88
assert self.col.DocA.fetch().next()['a']['foo'] == 4
89
assert doca['a']['foo'] == 4, doca['a']['foo']
90
saved_docb = self.col.DocB.collection.find_one({'_id':'docb'})
91
assert saved_docb['b']['doc_a'] == DBRef(database='test', collection='mongokit', id='doca'), saved_docb['b']['doc_a']
92
assert self.col.DocB.fetch_one() == docb
93
assert self.col.DocB.find_one({'_id':'docb'}) == docb
95
def test_simple_autoref2(self):
96
class Embed(Document):
108
self.connection.register([Embed, Doc])
110
embed = self.col.Embed()
111
embed['foo'] = {'hello':u'monde'}
120
assert doc == {'embed': {u'_id': embed['_id'], u'bar': 3, u'foo': {u'hello': u'monde'}}, '_id': doc['_id'], 'eggs': u'arf'}, doc
122
doc = self.col.Doc.fetch_one()
123
doc['embed']['foo']['hello'] = u'World'
126
assert doc == {'embed': {u'_id': embed['_id'], u'bar': 3, u'foo': {u'hello': u'World'}}, '_id': doc['_id'], 'eggs': u'arf'}, doc
127
assert self.col.Embed.fetch_one() == {u'_id': embed['_id'], u'bar': 3, u'foo': {u'hello': u'World'}}
129
def test_autoref_with_default_values(self):
130
class DocA(Document):
135
self.connection.register([DocA])
136
doca = self.col.DocA()
141
class DocB(Document):
146
default_values = {'b.doc_a':doca}
147
self.connection.register([DocB])
149
docb = self.col.DocB()
150
assert docb == {'b': {'doc_a': {'a': {'foo': 2}, 'abis': {'bar': None}, '_id': 'doca'}}}, docb
153
def test_autoref_with_required_fields(self):
154
class DocA(Document):
159
required_fields = ['a.foo']
160
self.connection.register([DocA])
162
doca = self.col.DocA()
167
class DocB(Document):
169
collection_name = "mongokit"
174
self.connection.register([DocB])
176
docb = self.col.DocB()
177
docb['b']['doc_a'] = doca
178
assert docb == {'b': {'doc_a': {'a': {'foo': 2}, 'abis': {'bar': None}, '_id': 'doca'}}}, docb
180
docb['b']['doc_a']['a']['foo'] = None
181
self.assertRaises(RequireFieldError, docb.validate)
182
docb['b']['doc_a']['a']['foo'] = 4
185
docb['b']['doc_a'] = None
188
def test_badautoref(self):
189
"""Test autoref enabled, but embed the wrong kind of document.
190
Assert that it tells us it's a bad embed.
192
class EmbedDoc(Document):
196
self.connection.register([EmbedDoc])
197
embed = self.col.EmbedDoc()
198
embed["spam"] = u"eggs"
202
class EmbedOtherDoc(Document):
206
self.connection.register([EmbedOtherDoc])
207
embedOther = self.connection.test.embed_other.EmbedOtherDoc()
208
embedOther["ham"] = u"eggs"
212
class MyDoc(Document):
222
self.connection.register([MyDoc])
223
mydoc = self.connection.test.autoref.MyDoc()
224
mydoc["bla"]["foo"] = u"bar"
225
mydoc["bla"]["bar"] = 42
226
mydoc["spam"] = embedOther
228
self.assertRaises(SchemaTypeError, mydoc.save)
230
def test_badautoref_not_enabled(self):
231
# Test that, when autoref is disabled
232
# we refuse to allow a MongoDocument
233
# to be valid schema.
235
class EmbedDoc(Document):
239
self.connection.register([EmbedDoc])
240
embed = self.connection.test['autoref.embed'].EmbedDoc()
241
embed["spam"] = u"eggs"
245
class MyDoc(Document):
253
self.assertRaises(StructureError, self.connection.register, [MyDoc])
255
def test_subclass(self):
256
# Test autoref enabled, but embed a subclass.
257
# e.g. if we say EmbedDoc, a subclass of EmbedDoc
260
class EmbedDoc(Document):
264
self.connection.register([EmbedDoc])
265
embed = self.connection.test['autoref.embed'].EmbedDoc()
266
embed["spam"] = u"eggs"
269
class EmbedOtherDoc(EmbedDoc):
273
self.connection.register([EmbedOtherDoc])
274
embedOther = self.connection.test['autoref.embed_other'].EmbedOtherDoc()
275
embedOther["ham"] = u"eggs"
279
class MyDoc(Document):
288
self.connection.register([MyDoc])
289
mydoc = self.connection.test.autoref.MyDoc()
290
mydoc["bla"]["foo"] = u"bar"
291
mydoc["bla"]["bar"] = 42
292
mydoc["spam"] = embedOther
295
assert mydoc['spam'].collection.name == "autoref.embed_other"
296
assert mydoc['spam'] == embedOther
298
def test_autoref_in_list(self):
299
class DocA(Document):
303
self.connection.register([DocA])
305
doca = self.col.DocA()
310
doca2 = self.col.DocA()
311
doca2['_id'] = 'doca2'
312
doca2['a']['foo'] = 5
315
class DocB(Document):
317
"b":{"doc_a":[DocA]},
320
self.connection.register([DocB])
322
docb = self.col.DocB()
323
# the structure is automaticly filled by the corresponding structure
324
assert docb == {'b': {'doc_a':[]}}, docb
327
docb['b']['doc_a'].append(u'bla')
328
self.assertRaises(SchemaTypeError, docb.validate)
329
docb['b']['doc_a'] = []
330
docb['b']['doc_a'].append(doca)
331
assert docb == {'b': {'doc_a': [{'a': {'foo': 3}, '_id': 'doca'}]}, '_id': 'docb'}
333
assert isinstance(docb.collection.find_one({'_id':'docb'})['b']['doc_a'][0], DBRef), type(docb.collection.find_one({'_id':'docb'})['b']['doc_a'][0])
335
assert docb == {'b': {'doc_a': [{'a': {'foo': 3}, '_id': 'doca'}]}, '_id': 'docb'}
336
assert docb['b']['doc_a'][0]['a']['foo'] == 3
337
docb['b']['doc_a'][0]['a']['foo'] = 4
339
assert docb['b']['doc_a'][0]['a']['foo'] == 4, docb['b']['doc_a'][0]['a']['foo']
340
assert doca['a']['foo'] == 4, doca['a']['foo']
342
docb['b']['doc_a'].append(doca2)
343
assert docb == {'b': {'doc_a': [{'a': {'foo': 4}, '_id': 'doca'}, {'a': {'foo': 5}, '_id': 'doca2'}]}, '_id': 'docb'}
346
def test_autoref_retrieval(self):
347
class DocA(Document):
351
self.connection.register([DocA])
353
doca = self.col.DocA()
358
class DocB(Document):
362
"deep": {"doc_a_deep":DocA},
363
"deeper": {"doc_a_deeper":DocA,
364
"inner":{"doc_a_deepest":DocA}}
369
self.connection.register([DocB])
371
docb = self.col.DocB()
372
# the structure is automaticly filled by the corresponding structure
374
docb['b']['doc_a'] = doca
376
# create a few deeper docas
377
deep = self.col.DocA()
378
#deep['_id'] = 'deep'
381
docb['b']['deep']['doc_a_deep'] = deep
382
deeper = self.col.DocA()
383
deeper['_id'] = 'deeper'
384
deeper['a']['foo'] = 8
386
docb['b']['deeper']['doc_a_deeper'] = deeper
387
deepest = self.col.DocA()
388
deepest['_id'] = 'deepest'
389
#deepest['_id'] = 'deeper'
390
deepest['a']['foo'] = 18
392
docb['b']['deeper']['inner']['doc_a_deepest'] = deepest
396
# now, does retrieval function as expected?
397
test_doc = self.col.DocB.get_from_id(docb['_id'])
398
assert isinstance(test_doc['b']['doc_a'], DocA), type(test_doc['b']['doc_a'])
399
assert test_doc['b']['doc_a']['a']['foo'] == 3
400
assert isinstance(test_doc['b']['deep']['doc_a_deep'], DocA)
401
assert test_doc['b']['deep']['doc_a_deep']['a']['foo'] == 5
402
assert isinstance(test_doc['b']['deeper']['doc_a_deeper'], DocA)
403
assert test_doc['b']['deeper']['doc_a_deeper']['a']['foo'] == 8, test_doc
404
assert isinstance(test_doc['b']['deeper']['inner']['doc_a_deepest'], DocA)
405
assert test_doc['b']['deeper']['inner']['doc_a_deepest']['a']['foo'] == 18
407
def test_autoref_with_same_embed_id(self):
408
class DocA(Document):
412
self.connection.register([DocA])
414
doca = self.col.DocA()
419
class DocB(Document):
423
"deep": {"doc_a_deep":DocA},
428
self.connection.register([DocB])
430
docb = self.col.DocB()
432
docb['b']['doc_a'] = doca
433
# create a few deeper docas
434
deep = self.col.DocA()
435
deep['_id'] = 'doca' # XXX same id of doca, this will be erased by doca when saving docb
438
docb['b']['deep']['doc_a_deep'] = deep
442
test_doc = self.col.DocB.get_from_id(docb['_id'])
443
assert test_doc['b']['doc_a']['a']['foo'] == 3, test_doc['b']['doc_a']['a']
444
assert test_doc['b']['deep']['doc_a_deep']['a']['foo'] == 3, test_doc['b']['deep']['doc_a_deep']['a']['foo']
446
def test_autorefs_embed_in_list_with_bad_reference(self):
447
class User(Document):
448
structure = {'name':unicode}
449
self.connection.register([User])
451
class Group(Document):
455
'members':[User], #users
457
self.connection.register([User, Group])
459
user = self.col.User()
460
user['_id'] = u'fixe'
461
user['name'] = u'fixe'
464
user2 = self.col.User()
465
user['_id'] = u'namlook'
466
user2['name'] = u'namlook'
469
group = self.col.Group()
470
group['members'].append(user)
471
self.assertRaises(AutoReferenceError, group.save)
473
def test_autorefs_with_dynamic_collection(self):
474
class DocA(Document):
475
structure = {'a':unicode}
477
class DocB(Document):
478
structure = {'b':DocA}
480
self.connection.register([DocA, DocB])
482
doca = self.connection.test.doca.DocA()
486
docb = self.connection.test.docb.DocB()
490
assert docb['b']['a'] == 'bla'
491
assert docb['b'].collection.name == "doca"
493
doca2 = self.connection.test.doca2.DocA()
497
docb2 = self.connection.test.docb.DocB()
501
assert docb2['b']['a'] == 'foo'
502
assert docb2['b'].collection.name == 'doca2'
503
assert docb2.collection.name == 'docb'
505
assert list(self.connection.test.docb.DocB.fetch()) == [docb, docb2]
507
def test_autorefs_with_dynamic_db(self):
508
class DocA(Document):
509
structure = {'a':unicode}
511
class DocB(Document):
512
structure = {'b':DocA}
514
self.connection.register([DocA, DocB])
516
doca = self.connection.dba.mongokit.DocA()
520
docb = self.connection.dbb.mongokit.DocB()
524
assert docb['b']['a'] == 'bla'
525
docb = self.connection.dbb.mongokit.DocB.get_from_id(docb['_id'])
526
assert isinstance(docb['b'], DocA)
528
def test_autoref_without_validation(self):
529
class DocA(Document):
533
self.connection.register([DocA])
535
doca = self.col.DocA()
540
class DocB(Document):
545
skip_validation = True
546
self.connection.register([DocB])
548
docb = self.col.DocB()
550
docb['b']['doc_a'] = doca
553
def test_autoref_updated(self):
554
class DocA(Document):
558
self.connection.register([DocA])
560
doca = self.col.DocA()
565
doca2 = self.col.DocA()
566
doca2['_id'] = 'doca2'
567
doca2['a']['foo'] = 6
570
class DocB(Document):
572
"b":{"doc_a":[DocA]},
575
self.connection.register([DocB])
577
docb = self.col.DocB()
580
assert docb == {'b': {'doc_a': []}, '_id': 'docb'}
581
docb['b']['doc_a'] = [doca, doca2]
583
assert docb == {'b': {'doc_a': [{u'a': {u'foo': 3}, u'_id': u'doca'}, {u'a': {u'foo': 6}, u'_id': u'doca2'}]}, '_id': 'docb'}
584
docb['b']['doc_a'].pop(0)
586
assert docb == {'b': {'doc_a': [{u'a': {u'foo': 6}, u'_id': u'doca2'}]}, '_id': 'docb'}
587
fetched_docb = self.col.DocB.get_from_id('docb')
588
assert fetched_docb == {u'_id': u'docb', u'b': {u'doc_a': [{u'a': {u'foo': 6}, u'_id': u'doca2'}]}}
590
docb = self.col.DocB()
593
assert docb == {'b': {'doc_a': []}, '_id': 'docb'}
594
docb['b']['doc_a'] = [doca, doca2]
596
assert docb == {'b': {'doc_a': [{u'a': {u'foo': 3}, u'_id': u'doca'}, {u'a': {u'foo': 6}, u'_id': u'doca2'}]}, '_id': 'docb'}, docb
597
docb['b']['doc_a'].pop(0)
598
docb['b']['doc_a'].append(doca)
600
assert docb == {'b': {'doc_a': [{u'a': {u'foo': 6}, u'_id': u'doca2'}, {u'a': {u'foo': 3}, u'_id': u'doca'}]}, '_id': 'docb'}, docb
601
fetched_docb = self.col.DocB.get_from_id('docb')
602
assert fetched_docb == {u'_id': u'docb', u'b': {u'doc_a': [{u'a': {u'foo': 6}, u'_id': u'doca2'}, {u'a': {u'foo': 3}, u'_id': u'doca'}]}}
604
def test_autoref_updated_with_default_values(self):
605
class DocA(Document):
610
default_values = {'a.foo':2}
611
required_fields = ['abis.bar']
613
self.connection.register([DocA])
614
doca = self.col.DocA()
616
doca['abis']['bar'] = 3
619
class DocB(Document):
625
self.connection.register([DocB])
626
docb = self.col.DocB()
628
docb['b']['doc_a'] = doca
629
assert docb == {'b': {'doc_a': {'a': {'foo': 2}, 'abis': {'bar': 3}, '_id': 'doca'}}, '_id': 'docb'}, docb
630
docb['b']['doc_a']['a']['foo'] = 4
632
assert docb == {'b': {'doc_a': {'a': {'foo': 4}, 'abis': {'bar': 3}, '_id': 'doca'}}, '_id': 'docb'}, docb
633
assert doca['a']['foo'] == 4
635
def test_autoref_with_None(self):
636
class RootDocument(Document):
637
use_dot_notation=True
641
class User(RootDocument):
642
collection_name = "users"
647
required_fields = [ "email", "password" ]
653
self.connection.register([User])
657
u['password'] = u'....'
659
assert u['_id'] != None
661
class ExampleSession(RootDocument):
662
#collection_name = "sessions"
668
# raise an assertion because User is a CallableUser, not User
669
self.connection.register([ExampleSession])
670
ex = self.col.ExampleSession()
671
self.assertRaises(SchemaTypeError, ex.validate)
673
def test_autoref_without_database_specified(self):
674
class EmbedDoc(Document):
680
use_dot_notation=True
682
force_autorefs_current_db = True
686
self.connection.register([EmbedDoc, Doc])
688
embed = self.col.EmbedDoc()
689
embed['foo'] = u'bar'
692
raw_doc = {'embed':DBRef(collection=self.col.name, id=embed['_id'])}
693
self.col.insert(raw_doc)
695
doc = self.col.Doc.find_one({'_id':raw_doc['_id']})
697
def test_recreate_and_reregister_class_with_reference(self):
698
class CompanyDocument(Document):
699
collection_name = "test_companies"
701
use_dot_notation = True
706
class UserDocument(Document):
707
collection_name = "test_users"
709
use_dot_notation = True
712
"company": CompanyDocument,
715
class SessionDocument(Document):
716
collection_name = "test_sessions"
718
use_dot_notation = True
721
"owner": UserDocument,
723
self.connection.register([CompanyDocument, UserDocument, SessionDocument])
725
company = self.col.database[CompanyDocument.collection_name].CompanyDocument()
726
company.name = u"Company"
729
company_owner = self.col.database[UserDocument.collection_name].UserDocument()
730
company_owner.email = u"manager@test.com"
731
company_owner.company = company
734
s = self.col.database[SessionDocument.collection_name].SessionDocument()
735
s.token = u'asddadsad'
736
s.owner = company_owner
739
sbis= self.col.database[SessionDocument.collection_name].SessionDocument.find_one({"token": u"asddadsad" })
740
assert sbis == s, sbis
742
class CompanyDocument(Document):
743
collection_name = "test_companies"
749
class UserDocument(Document):
750
collection_name = "test_users"
754
"company": CompanyDocument,
757
class SessionDocument(Document):
758
collection_name = "test_sessions"
762
"owner": UserDocument,
764
self.connection.register([CompanyDocument, UserDocument, SessionDocument])
766
sbis= self.col.database[SessionDocument.collection_name].SessionDocument.find_one({"token": u"asddadsad" })
767
assert sbis == s, sbis
770
def test_nested_autorefs(self):
771
class DocA(Document):
777
class DocB(Document):
784
class DocC(Document):
792
class DocD(Document):
798
self.connection.register([DocA, DocB, DocC, DocD])
800
doca = self.col.DocA()
801
doca['name'] = u'Test A'
804
docb = self.col.DocB()
805
docb['name'] = u'Test B'
809
docc = self.col.DocC()
810
docc['name'] = u'Test C'
815
docd = self.col.DocD()
816
docd['name'] = u'Test D'
820
doca = self.col.DocA.find_one({'name': 'Test A'})
821
docb = self.col.DocB.find_one({'name': 'Test B'})
822
docc = self.col.DocC.find_one({'name': 'Test C'})
823
docd = self.col.DocD.find_one({'name': 'Test D'})
826
def test_nested_autoref_in_list_and_dict(self):
827
class DocA(Document):
834
class DocB(Document):
838
'something' : unicode,
844
self.connection.register([DocA, DocB])
846
doca = self.col.DocA()
847
doca['name'] = u'Test A'
850
docc = self.col.DocA()
851
docc['name'] = u'Test C'
854
docb = self.col.DocB()
855
docb['name'] = u'Test B'
856
docb['test'].append({u'something': u'foo', 'doca': doca})
857
docb['test'].append({u'something': u'foo', 'doca': docc})
860
raw_docb = self.col.find_one({'name':'Test B'})
861
assert isinstance(raw_docb['test'][0]['doca'], DBRef), raw_docb['test'][0]
863
def test_dereference(self):
865
class DocA(Document):
871
self.connection.register([DocA])
873
doca = self.col.DocA()
874
doca['name'] = u'Test A'
877
docb = self.connection.test2.mongokit.DocA()
878
docb['name'] = u'Test B'
881
dbref = doca.get_dbref()
883
self.assertRaises(TypeError, self.connection.test.dereference, 1)
884
self.assertRaises(ValueError, self.connection.test.dereference, docb.get_dbref(), DocA)
885
assert self.connection.test.dereference(dbref) == {'_id':doca['_id'], 'name': 'Test A'}
886
assert isinstance(self.connection.test.dereference(dbref), dict)
887
assert self.connection.test.dereference(dbref, DocA) == {'_id':doca['_id'], 'name': 'Test A'}
888
assert isinstance(self.connection.test.dereference(dbref, DocA), DocA)
890
def test_autorefs_with_list(self):
891
class VDocument(Document):
893
use_dot_notation = True
895
skip_validation = True
897
def __init__(self, *args, **kwargs):
898
super(VDocument, self).__init__(*args, **kwargs)
900
def save(self, *args, **kwargs):
901
kwargs.update({'validate':True})
902
return super(VDocument, self).save(*args, **kwargs)
905
structure = {'name':[ObjectId], 'blah':[unicode], 'foo': [{'x':unicode}]}
906
self.connection.register([H, VDocument])
910
h.name.append(obj_id)
911
h.blah.append(u'some string')
912
h.foo.append({'x':u'hey'})
914
assert h == {'blah': [u'some string'], 'foo': [{'x': u'hey'}], 'name': [obj_id], '_id': h['_id']}
916
def test_autorefs_with_list2(self):
917
class DocA(Document):
918
structure = {'name':unicode}
920
class DocB(Document):
929
self.connection.register([DocA, DocB])
931
doca = self.col.DocA()
932
doca['_id'] = u'doca'
933
doca['name'] = u"foo"
937
{'_id': 'docb', 'docs':[
939
'doca':[DBRef(database='test', collection='mongokit', id='doca')],
944
assert self.col.DocB.find_one({'_id':'docb'}) == {u'docs': [{u'doca': [{u'_id': u'doca', u'name': u'foo'}], u'inc': 2}], u'_id': u'docb'}
946
def test_autorefs_with_required(self):
950
@self.connection.register
951
class User(Document):
956
@self.connection.register
957
class Event(Document):
962
required_fields = ['user', 'title']
965
user = self.connection.test.users.User()
967
event = self.connection.test.events.Event()
969
event['title'] = u"Test"