/SQLAlchemy-0.7.8/test/orm/test_unitofwork.py
Python | 2520 lines | 2354 code | 146 blank | 20 comment | 3 complexity | acc97907fe2520ca34bc167f620b437f MD5 | raw file
Large files files are truncated, but you can click here to view the full file
1# coding: utf-8
2"""Tests unitofwork operations."""
3
4from test.lib.testing import eq_, assert_raises, assert_raises_message
5import datetime
6import operator
7from sqlalchemy.orm import mapper as orm_mapper
8
9import sqlalchemy as sa
10from sqlalchemy import Integer, String, ForeignKey, literal_column, event
11from test.lib import engines, testing, pickleable
12from test.lib.schema import Table
13from test.lib.schema import Column
14from sqlalchemy.orm import mapper, relationship, create_session, \
15 column_property, attributes, Session, reconstructor, object_session
16from test.lib.testing import eq_, ne_
17from test.lib.util import gc_collect
18from test.lib import fixtures
19from test.orm import _fixtures
20from test.lib import fixtures
21from test.lib.assertsql import AllOf, CompiledSQL
22import gc
23
24class UnitOfWorkTest(object):
25 pass
26
27class HistoryTest(_fixtures.FixtureTest):
28 run_inserts = None
29
30 @classmethod
31 def setup_classes(cls):
32 class User(cls.Comparable):
33 pass
34 class Address(cls.Comparable):
35 pass
36
37 def test_backref(self):
38 Address, addresses, users, User = (self.classes.Address,
39 self.tables.addresses,
40 self.tables.users,
41 self.classes.User)
42
43 am = mapper(Address, addresses)
44 m = mapper(User, users, properties=dict(
45 addresses = relationship(am, backref='user', lazy='joined')))
46
47 session = create_session(autocommit=False)
48
49 u = User(name='u1')
50 a = Address(email_address='u1@e')
51 a.user = u
52 session.add(u)
53
54 eq_(u.addresses, [a])
55 session.commit()
56 session.expunge_all()
57
58 u = session.query(m).one()
59 assert u.addresses[0].user == u
60 session.close()
61
62class UnicodeTest(fixtures.MappedTest):
63 __requires__ = ('unicode_connections',)
64
65 @classmethod
66 def define_tables(cls, metadata):
67 if testing.against('mysql+oursql'):
68 from sqlalchemy.dialects.mysql import VARCHAR
69 uni_type = VARCHAR(50, collation='utf8_unicode_ci')
70 else:
71 uni_type = sa.Unicode(50)
72
73 Table('uni_t1', metadata,
74 Column('id', Integer, primary_key=True,
75 test_needs_autoincrement=True),
76 Column('txt', uni_type, unique=True))
77 Table('uni_t2', metadata,
78 Column('id', Integer, primary_key=True,
79 test_needs_autoincrement=True),
80 Column('txt', uni_type, ForeignKey('uni_t1')))
81
82 @classmethod
83 def setup_classes(cls):
84 class Test(cls.Basic):
85 pass
86 class Test2(cls.Basic):
87 pass
88
89 def test_basic(self):
90 Test, uni_t1 = self.classes.Test, self.tables.uni_t1
91
92 mapper(Test, uni_t1)
93
94 txt = u"\u0160\u0110\u0106\u010c\u017d"
95 t1 = Test(id=1, txt=txt)
96 self.assert_(t1.txt == txt)
97
98 session = create_session(autocommit=False)
99 session.add(t1)
100 session.commit()
101
102 self.assert_(t1.txt == txt)
103
104 def test_relationship(self):
105 Test, uni_t2, uni_t1, Test2 = (self.classes.Test,
106 self.tables.uni_t2,
107 self.tables.uni_t1,
108 self.classes.Test2)
109
110 mapper(Test, uni_t1, properties={
111 't2s': relationship(Test2)})
112 mapper(Test2, uni_t2)
113
114 txt = u"\u0160\u0110\u0106\u010c\u017d"
115 t1 = Test(txt=txt)
116 t1.t2s.append(Test2())
117 t1.t2s.append(Test2())
118 session = create_session(autocommit=False)
119 session.add(t1)
120 session.commit()
121 session.close()
122
123 session = create_session()
124 t1 = session.query(Test).filter_by(id=t1.id).one()
125 assert len(t1.t2s) == 2
126
127class UnicodeSchemaTest(fixtures.MappedTest):
128 __requires__ = ('unicode_connections', 'unicode_ddl',)
129
130 run_dispose_bind = 'once'
131
132 @classmethod
133 def create_engine(cls):
134 return engines.utf8_engine()
135
136 @classmethod
137 def define_tables(cls, metadata):
138 t1 = Table('unitable1', metadata,
139 Column(u'méil', Integer, primary_key=True, key='a', test_needs_autoincrement=True),
140 Column(u'\u6e2c\u8a66', Integer, key='b'),
141 Column('type', String(20)),
142 test_needs_fk=True,
143 test_needs_autoincrement=True)
144 t2 = Table(u'Unitéble2', metadata,
145 Column(u'méil', Integer, primary_key=True, key="cc", test_needs_autoincrement=True),
146 Column(u'\u6e2c\u8a66', Integer,
147 ForeignKey(u'unitable1.a'), key="d"),
148 Column(u'\u6e2c\u8a66_2', Integer, key="e"),
149 test_needs_fk=True,
150 test_needs_autoincrement=True)
151
152 cls.tables['t1'] = t1
153 cls.tables['t2'] = t2
154
155 @classmethod
156 def setup_class(cls):
157 super(UnicodeSchemaTest, cls).setup_class()
158
159 @classmethod
160 def teardown_class(cls):
161 super(UnicodeSchemaTest, cls).teardown_class()
162
163 @testing.fails_on('mssql+pyodbc',
164 'pyodbc returns a non unicode encoding of the results description.')
165 def test_mapping(self):
166 t2, t1 = self.tables.t2, self.tables.t1
167
168 class A(fixtures.ComparableEntity):
169 pass
170 class B(fixtures.ComparableEntity):
171 pass
172
173 mapper(A, t1, properties={
174 't2s':relationship(B)})
175 mapper(B, t2)
176
177 a1 = A()
178 b1 = B()
179 a1.t2s.append(b1)
180
181 session = create_session()
182 session.add(a1)
183 session.flush()
184 session.expunge_all()
185
186 new_a1 = session.query(A).filter(t1.c.a == a1.a).one()
187 assert new_a1.a == a1.a
188 assert new_a1.t2s[0].d == b1.d
189 session.expunge_all()
190
191 new_a1 = (session.query(A).options(sa.orm.joinedload('t2s')).
192 filter(t1.c.a == a1.a)).one()
193 assert new_a1.a == a1.a
194 assert new_a1.t2s[0].d == b1.d
195 session.expunge_all()
196
197 new_a1 = session.query(A).filter(A.a == a1.a).one()
198 assert new_a1.a == a1.a
199 assert new_a1.t2s[0].d == b1.d
200 session.expunge_all()
201
202 @testing.fails_on('mssql+pyodbc',
203 'pyodbc returns a non unicode encoding of the results description.')
204 def test_inheritance_mapping(self):
205 t2, t1 = self.tables.t2, self.tables.t1
206
207 class A(fixtures.ComparableEntity):
208 pass
209 class B(A):
210 pass
211
212 mapper(A, t1,
213 polymorphic_on=t1.c.type,
214 polymorphic_identity='a')
215 mapper(B, t2,
216 inherits=A,
217 polymorphic_identity='b')
218 a1 = A(b=5)
219 b1 = B(e=7)
220
221 session = create_session()
222 session.add_all((a1, b1))
223 session.flush()
224 session.expunge_all()
225
226 eq_([A(b=5), B(e=7)], session.query(A).all())
227
228class BinaryHistTest(fixtures.MappedTest, testing.AssertsExecutionResults):
229 @classmethod
230 def define_tables(cls, metadata):
231 Table('t1', metadata,
232 Column('id', sa.Integer, primary_key=True, test_needs_autoincrement=True),
233 Column('data', sa.LargeBinary),
234 )
235
236 @classmethod
237 def setup_classes(cls):
238 class Foo(cls.Basic):
239 pass
240
241 def test_binary_equality(self):
242 Foo, t1 = self.classes.Foo, self.tables.t1
243
244
245 # Py3K
246 #data = b"this is some data"
247 # Py2K
248 data = "this is some data"
249 # end Py2K
250
251 mapper(Foo, t1)
252
253 s = create_session()
254
255 f1 = Foo(data=data)
256 s.add(f1)
257 s.flush()
258 s.expire_all()
259 f1 = s.query(Foo).first()
260 assert f1.data == data
261 f1.data = data
262 eq_(
263 sa.orm.attributes.get_history(f1, "data"),
264 ((), [data], ())
265 )
266 def go():
267 s.flush()
268 self.assert_sql_count(testing.db, go, 0)
269
270class PKTest(fixtures.MappedTest):
271
272 @classmethod
273 def define_tables(cls, metadata):
274 Table('multipk1', metadata,
275 Column('multi_id', Integer, primary_key=True,
276 test_needs_autoincrement=True),
277 Column('multi_rev', Integer, primary_key=True),
278 Column('name', String(50), nullable=False),
279 Column('value', String(100)))
280
281 Table('multipk2', metadata,
282 Column('pk_col_1', String(30), primary_key=True),
283 Column('pk_col_2', String(30), primary_key=True),
284 Column('data', String(30)))
285 Table('multipk3', metadata,
286 Column('pri_code', String(30), key='primary', primary_key=True),
287 Column('sec_code', String(30), key='secondary', primary_key=True),
288 Column('date_assigned', sa.Date, key='assigned', primary_key=True),
289 Column('data', String(30)))
290
291 @classmethod
292 def setup_classes(cls):
293 class Entry(cls.Basic):
294 pass
295
296 # not supported on sqlite since sqlite's auto-pk generation only works with
297 # single column primary keys
298 @testing.fails_on('sqlite', 'FIXME: unknown')
299 def test_primary_key(self):
300 Entry, multipk1 = self.classes.Entry, self.tables.multipk1
301
302 mapper(Entry, multipk1)
303
304 e = Entry(name='entry1', value='this is entry 1', multi_rev=2)
305
306 session = create_session()
307 session.add(e)
308 session.flush()
309 session.expunge_all()
310
311 e2 = session.query(Entry).get((e.multi_id, 2))
312 self.assert_(e is not e2)
313 state = sa.orm.attributes.instance_state(e)
314 state2 = sa.orm.attributes.instance_state(e2)
315 eq_(state.key, state2.key)
316
317 # this one works with sqlite since we are manually setting up pk values
318 def test_manual_pk(self):
319 Entry, multipk2 = self.classes.Entry, self.tables.multipk2
320
321 mapper(Entry, multipk2)
322
323 e = Entry(pk_col_1='pk1', pk_col_2='pk1_related', data='im the data')
324
325 session = create_session()
326 session.add(e)
327 session.flush()
328
329 def test_key_pks(self):
330 Entry, multipk3 = self.classes.Entry, self.tables.multipk3
331
332 mapper(Entry, multipk3)
333
334 e = Entry(primary= 'pk1', secondary='pk2',
335 assigned=datetime.date.today(), data='some more data')
336
337 session = create_session()
338 session.add(e)
339 session.flush()
340
341
342class ForeignPKTest(fixtures.MappedTest):
343 """Detection of the relationship direction on PK joins."""
344
345 @classmethod
346 def define_tables(cls, metadata):
347 Table("people", metadata,
348 Column('person', String(10), primary_key=True),
349 Column('firstname', String(10)),
350 Column('lastname', String(10)))
351
352 Table("peoplesites", metadata,
353 Column('person', String(10), ForeignKey("people.person"),
354 primary_key=True),
355 Column('site', String(10)))
356
357 @classmethod
358 def setup_classes(cls):
359 class Person(cls.Basic):
360 pass
361 class PersonSite(cls.Basic):
362 pass
363
364 def test_basic(self):
365 peoplesites, PersonSite, Person, people = (self.tables.peoplesites,
366 self.classes.PersonSite,
367 self.classes.Person,
368 self.tables.people)
369
370 m1 = mapper(PersonSite, peoplesites)
371 m2 = mapper(Person, people, properties={
372 'sites' : relationship(PersonSite)})
373
374 sa.orm.configure_mappers()
375 eq_(list(m2.get_property('sites').synchronize_pairs),
376 [(people.c.person, peoplesites.c.person)])
377
378 p = Person(person='im the key', firstname='asdf')
379 ps = PersonSite(site='asdf')
380 p.sites.append(ps)
381
382 session = create_session()
383 session.add(p)
384 session.flush()
385
386 p_count = people.count(people.c.person=='im the key').scalar()
387 eq_(p_count, 1)
388 eq_(peoplesites.count(peoplesites.c.person=='im the key').scalar(), 1)
389
390
391class ClauseAttributesTest(fixtures.MappedTest):
392
393 @classmethod
394 def define_tables(cls, metadata):
395 Table('users_t', metadata,
396 Column('id', Integer, primary_key=True,
397 test_needs_autoincrement=True),
398 Column('name', String(30)),
399 Column('counter', Integer, default=1))
400
401 @classmethod
402 def setup_classes(cls):
403 class User(cls.Comparable):
404 pass
405
406 @classmethod
407 def setup_mappers(cls):
408 User, users_t = cls.classes.User, cls.tables.users_t
409
410 mapper(User, users_t)
411
412 def test_update(self):
413 User = self.classes.User
414
415 u = User(name='test')
416
417 session = create_session()
418 session.add(u)
419 session.flush()
420
421 eq_(u.counter, 1)
422 u.counter = User.counter + 1
423 session.flush()
424
425 def go():
426 assert (u.counter == 2) is True # ensure its not a ClauseElement
427 self.sql_count_(1, go)
428
429 def test_multi_update(self):
430 User = self.classes.User
431
432 u = User(name='test')
433
434 session = create_session()
435 session.add(u)
436 session.flush()
437
438 eq_(u.counter, 1)
439 u.name = 'test2'
440 u.counter = User.counter + 1
441 session.flush()
442
443 def go():
444 eq_(u.name, 'test2')
445 assert (u.counter == 2) is True
446 self.sql_count_(1, go)
447
448 session.expunge_all()
449 u = session.query(User).get(u.id)
450 eq_(u.name, 'test2')
451 eq_(u.counter, 2)
452
453 def test_insert(self):
454 User = self.classes.User
455
456 u = User(name='test', counter=sa.select([5]))
457
458 session = create_session()
459 session.add(u)
460 session.flush()
461
462 assert (u.counter == 5) is True
463
464
465class PassiveDeletesTest(fixtures.MappedTest):
466 __requires__ = ('foreign_keys',)
467
468 @classmethod
469 def define_tables(cls, metadata):
470 Table('mytable', metadata,
471 Column('id', Integer, primary_key=True, test_needs_autoincrement=True),
472 Column('data', String(30)),
473 test_needs_fk=True)
474
475 Table('myothertable', metadata,
476 Column('id', Integer, primary_key=True, test_needs_autoincrement=True),
477 Column('parent_id', Integer),
478 Column('data', String(30)),
479 sa.ForeignKeyConstraint(['parent_id'],
480 ['mytable.id'],
481 ondelete="CASCADE"),
482 test_needs_fk=True)
483
484 @classmethod
485 def setup_classes(cls):
486 class MyClass(cls.Basic):
487 pass
488 class MyOtherClass(cls.Basic):
489 pass
490
491 def test_basic(self):
492 myothertable, MyClass, MyOtherClass, mytable = (self.tables.myothertable,
493 self.classes.MyClass,
494 self.classes.MyOtherClass,
495 self.tables.mytable)
496
497 mapper(MyOtherClass, myothertable)
498 mapper(MyClass, mytable, properties={
499 'children':relationship(MyOtherClass,
500 passive_deletes=True,
501 cascade="all")})
502 session = create_session()
503 mc = MyClass()
504 mc.children.append(MyOtherClass())
505 mc.children.append(MyOtherClass())
506 mc.children.append(MyOtherClass())
507 mc.children.append(MyOtherClass())
508
509 session.add(mc)
510 session.flush()
511 session.expunge_all()
512
513 assert myothertable.count().scalar() == 4
514 mc = session.query(MyClass).get(mc.id)
515 session.delete(mc)
516 session.flush()
517
518 assert mytable.count().scalar() == 0
519 assert myothertable.count().scalar() == 0
520
521 @testing.emits_warning(r".*'passive_deletes' is normally configured on one-to-many")
522 def test_backwards_pd(self):
523 """Test that passive_deletes=True disables a delete from an m2o.
524
525 This is not the usual usage and it now raises a warning, but test
526 that it works nonetheless.
527
528 """
529
530 myothertable, MyClass, MyOtherClass, mytable = (self.tables.myothertable,
531 self.classes.MyClass,
532 self.classes.MyOtherClass,
533 self.tables.mytable)
534
535 mapper(MyOtherClass, myothertable, properties={
536 'myclass':relationship(MyClass, cascade="all, delete", passive_deletes=True)
537 })
538 mapper(MyClass, mytable)
539
540 session = create_session()
541 mc = MyClass()
542 mco = MyOtherClass()
543 mco.myclass = mc
544 session.add(mco)
545 session.flush()
546
547 assert mytable.count().scalar() == 1
548 assert myothertable.count().scalar() == 1
549
550 session.expire(mco, ['myclass'])
551 session.delete(mco)
552 session.flush()
553
554 # mytable wasn't deleted, is the point.
555 assert mytable.count().scalar() == 1
556 assert myothertable.count().scalar() == 0
557
558 def test_aaa_m2o_emits_warning(self):
559 myothertable, MyClass, MyOtherClass, mytable = (self.tables.myothertable,
560 self.classes.MyClass,
561 self.classes.MyOtherClass,
562 self.tables.mytable)
563
564 mapper(MyOtherClass, myothertable, properties={
565 'myclass':relationship(MyClass, cascade="all, delete", passive_deletes=True)
566 })
567 mapper(MyClass, mytable)
568 assert_raises(sa.exc.SAWarning, sa.orm.configure_mappers)
569
570class BatchDeleteIgnoresRowcountTest(fixtures.DeclarativeMappedTest):
571 __requires__ = ('foreign_keys',)
572 @classmethod
573 def setup_classes(cls):
574 class A(cls.DeclarativeBasic):
575 __tablename__ = 'A'
576 __table_args__ = dict(test_needs_fk=True)
577 id = Column(Integer, primary_key=True)
578 parent_id = Column(Integer, ForeignKey('A.id', ondelete='CASCADE'))
579
580 def test_delete_both(self):
581 A = self.classes.A
582 session = Session(testing.db)
583
584 a1, a2 = A(id=1),A(id=2, parent_id=1)
585
586 session.add_all([a1, a2])
587 session.flush()
588
589 session.delete(a1)
590 session.delete(a2)
591
592 # no issue with multi-row count here
593 session.flush()
594
595class ExtraPassiveDeletesTest(fixtures.MappedTest):
596 __requires__ = ('foreign_keys',)
597
598 @classmethod
599 def define_tables(cls, metadata):
600 Table('mytable', metadata,
601 Column('id', Integer, primary_key=True, test_needs_autoincrement=True),
602 Column('data', String(30)),
603 test_needs_fk=True)
604
605 Table('myothertable', metadata,
606 Column('id', Integer, primary_key=True, test_needs_autoincrement=True),
607 Column('parent_id', Integer),
608 Column('data', String(30)),
609 # no CASCADE, the same as ON DELETE RESTRICT
610 sa.ForeignKeyConstraint(['parent_id'],
611 ['mytable.id']),
612 test_needs_fk=True)
613
614 @classmethod
615 def setup_classes(cls):
616 class MyClass(cls.Basic):
617 pass
618 class MyOtherClass(cls.Basic):
619 pass
620
621 def test_assertions(self):
622 myothertable, MyOtherClass = self.tables.myothertable, self.classes.MyOtherClass
623
624 mapper(MyOtherClass, myothertable)
625 assert_raises_message(
626 sa.exc.ArgumentError,
627 "Can't set passive_deletes='all' in conjunction with 'delete' "
628 "or 'delete-orphan' cascade",
629 relationship, MyOtherClass,
630 passive_deletes='all',
631 cascade="all"
632 )
633
634 def test_extra_passive(self):
635 myothertable, MyClass, MyOtherClass, mytable = (self.tables.myothertable,
636 self.classes.MyClass,
637 self.classes.MyOtherClass,
638 self.tables.mytable)
639
640 mapper(MyOtherClass, myothertable)
641 mapper(MyClass, mytable, properties={
642 'children': relationship(MyOtherClass,
643 passive_deletes='all',
644 cascade="save-update")})
645
646 session = create_session()
647 mc = MyClass()
648 mc.children.append(MyOtherClass())
649 mc.children.append(MyOtherClass())
650 mc.children.append(MyOtherClass())
651 mc.children.append(MyOtherClass())
652 session.add(mc)
653 session.flush()
654 session.expunge_all()
655
656 assert myothertable.count().scalar() == 4
657 mc = session.query(MyClass).get(mc.id)
658 session.delete(mc)
659 assert_raises(sa.exc.DBAPIError, session.flush)
660
661 def test_extra_passive_2(self):
662 myothertable, MyClass, MyOtherClass, mytable = (self.tables.myothertable,
663 self.classes.MyClass,
664 self.classes.MyOtherClass,
665 self.tables.mytable)
666
667 mapper(MyOtherClass, myothertable)
668 mapper(MyClass, mytable, properties={
669 'children': relationship(MyOtherClass,
670 passive_deletes='all',
671 cascade="save-update")})
672
673 session = create_session()
674 mc = MyClass()
675 mc.children.append(MyOtherClass())
676 session.add(mc)
677 session.flush()
678 session.expunge_all()
679
680 assert myothertable.count().scalar() == 1
681
682 mc = session.query(MyClass).get(mc.id)
683 session.delete(mc)
684 mc.children[0].data = 'some new data'
685 assert_raises(sa.exc.DBAPIError, session.flush)
686
687 def test_dont_emit(self):
688 myothertable, MyClass, MyOtherClass, mytable = (self.tables.myothertable,
689 self.classes.MyClass,
690 self.classes.MyOtherClass,
691 self.tables.mytable)
692
693 mapper(MyOtherClass, myothertable)
694 mapper(MyClass, mytable, properties={
695 'children': relationship(MyOtherClass,
696 passive_deletes='all',
697 cascade="save-update")})
698 session = Session()
699 mc = MyClass()
700 session.add(mc)
701 session.commit()
702 mc.id
703
704 session.delete(mc)
705
706 # no load for "children" should occur
707 self.assert_sql_count(testing.db, session.flush, 1)
708
709class ColumnCollisionTest(fixtures.MappedTest):
710 """Ensure the mapper doesn't break bind param naming rules on flush."""
711
712 @classmethod
713 def define_tables(cls, metadata):
714 Table('book', metadata,
715 Column('id', Integer, primary_key=True, test_needs_autoincrement=True),
716 Column('book_id', String(50)),
717 Column('title', String(50))
718 )
719
720 def test_naming(self):
721 book = self.tables.book
722
723 class Book(fixtures.ComparableEntity):
724 pass
725
726 mapper(Book, book)
727 sess = create_session()
728
729 b1 = Book(book_id='abc', title='def')
730 sess.add(b1)
731 sess.flush()
732
733 b1.title = 'ghi'
734 sess.flush()
735 sess.close()
736 eq_(
737 sess.query(Book).first(),
738 Book(book_id='abc', title='ghi')
739 )
740
741
742
743class DefaultTest(fixtures.MappedTest):
744 """Exercise mappings on columns with DefaultGenerators.
745
746 Tests that when saving objects whose table contains DefaultGenerators,
747 either python-side, preexec or database-side, the newly saved instances
748 receive all the default values either through a post-fetch or getting the
749 pre-exec'ed defaults back from the engine.
750
751 """
752
753 @classmethod
754 def define_tables(cls, metadata):
755 use_string_defaults = testing.against('postgresql', 'oracle', 'sqlite', 'mssql')
756
757 if use_string_defaults:
758 hohotype = String(30)
759 hohoval = "im hoho"
760 althohoval = "im different hoho"
761 else:
762 hohotype = Integer
763 hohoval = 9
764 althohoval = 15
765
766 cls.other['hohoval'] = hohoval
767 cls.other['althohoval'] = althohoval
768
769 dt = Table('default_t', metadata,
770 Column('id', Integer, primary_key=True,
771 test_needs_autoincrement=True),
772 Column('hoho', hohotype, server_default=str(hohoval)),
773 Column('counter', Integer, default=sa.func.char_length("1234567", type_=Integer)),
774 Column('foober', String(30), default="im foober", onupdate="im the update"),
775 mysql_engine='MyISAM')
776
777 st = Table('secondary_table', metadata,
778 Column('id', Integer, primary_key=True, test_needs_autoincrement=True),
779 Column('data', String(50)),
780 mysql_engine='MyISAM')
781
782 if testing.against('postgresql', 'oracle'):
783 dt.append_column(
784 Column('secondary_id', Integer, sa.Sequence('sec_id_seq'),
785 unique=True))
786 st.append_column(
787 Column('fk_val', Integer,
788 ForeignKey('default_t.secondary_id')))
789 elif testing.against('mssql'):
790 st.append_column(
791 Column('fk_val', Integer,
792 ForeignKey('default_t.id')))
793 else:
794 st.append_column(
795 Column('hoho', hohotype, ForeignKey('default_t.hoho')))
796
797 @classmethod
798 def setup_classes(cls):
799 class Hoho(cls.Comparable):
800 pass
801 class Secondary(cls.Comparable):
802 pass
803
804 @testing.fails_on('firebird', 'Data type unknown on the parameter')
805 def test_insert(self):
806 althohoval, hohoval, default_t, Hoho = (self.other.althohoval,
807 self.other.hohoval,
808 self.tables.default_t,
809 self.classes.Hoho)
810
811 mapper(Hoho, default_t)
812
813 h1 = Hoho(hoho=althohoval)
814 h2 = Hoho(counter=12)
815 h3 = Hoho(hoho=althohoval, counter=12)
816 h4 = Hoho()
817 h5 = Hoho(foober='im the new foober')
818
819 session = create_session(autocommit=False)
820 session.add_all((h1, h2, h3, h4, h5))
821 session.commit()
822
823 eq_(h1.hoho, althohoval)
824 eq_(h3.hoho, althohoval)
825
826 def go():
827 # test deferred load of attribues, one select per instance
828 self.assert_(h2.hoho == h4.hoho == h5.hoho == hohoval)
829 self.sql_count_(3, go)
830
831 def go():
832 self.assert_(h1.counter == h4.counter == h5.counter == 7)
833 self.sql_count_(1, go)
834
835 def go():
836 self.assert_(h3.counter == h2.counter == 12)
837 self.assert_(h2.foober == h3.foober == h4.foober == 'im foober')
838 self.assert_(h5.foober == 'im the new foober')
839 self.sql_count_(0, go)
840
841 session.expunge_all()
842
843 (h1, h2, h3, h4, h5) = session.query(Hoho).order_by(Hoho.id).all()
844
845 eq_(h1.hoho, althohoval)
846 eq_(h3.hoho, althohoval)
847 self.assert_(h2.hoho == h4.hoho == h5.hoho == hohoval)
848 self.assert_(h3.counter == h2.counter == 12)
849 self.assert_(h1.counter == h4.counter == h5.counter == 7)
850 self.assert_(h2.foober == h3.foober == h4.foober == 'im foober')
851 eq_(h5.foober, 'im the new foober')
852
853 @testing.fails_on('firebird', 'Data type unknown on the parameter')
854 def test_eager_defaults(self):
855 hohoval, default_t, Hoho = (self.other.hohoval,
856 self.tables.default_t,
857 self.classes.Hoho)
858
859 mapper(Hoho, default_t, eager_defaults=True)
860
861 h1 = Hoho()
862
863 session = create_session()
864 session.add(h1)
865 session.flush()
866
867 self.sql_count_(0, lambda: eq_(h1.hoho, hohoval))
868
869 def test_insert_nopostfetch(self):
870 default_t, Hoho = self.tables.default_t, self.classes.Hoho
871
872 # populates from the FetchValues explicitly so there is no
873 # "post-update"
874 mapper(Hoho, default_t)
875
876 h1 = Hoho(hoho="15", counter=15)
877 session = create_session()
878 session.add(h1)
879 session.flush()
880
881 def go():
882 eq_(h1.hoho, "15")
883 eq_(h1.counter, 15)
884 eq_(h1.foober, "im foober")
885 self.sql_count_(0, go)
886
887 @testing.fails_on('firebird', 'Data type unknown on the parameter')
888 def test_update(self):
889 default_t, Hoho = self.tables.default_t, self.classes.Hoho
890
891 mapper(Hoho, default_t)
892
893 h1 = Hoho()
894 session = create_session()
895 session.add(h1)
896 session.flush()
897
898 eq_(h1.foober, 'im foober')
899 h1.counter = 19
900 session.flush()
901 eq_(h1.foober, 'im the update')
902
903 @testing.fails_on('firebird', 'Data type unknown on the parameter')
904 def test_used_in_relationship(self):
905 """A server-side default can be used as the target of a foreign key"""
906
907 Hoho, hohoval, default_t, secondary_table, Secondary = (self.classes.Hoho,
908 self.other.hohoval,
909 self.tables.default_t,
910 self.tables.secondary_table,
911 self.classes.Secondary)
912
913
914 mapper(Hoho, default_t, properties={
915 'secondaries':relationship(Secondary, order_by=secondary_table.c.id)})
916 mapper(Secondary, secondary_table)
917
918 h1 = Hoho()
919 s1 = Secondary(data='s1')
920 h1.secondaries.append(s1)
921
922 session = create_session()
923 session.add(h1)
924 session.flush()
925 session.expunge_all()
926
927 eq_(session.query(Hoho).get(h1.id),
928 Hoho(hoho=hohoval,
929 secondaries=[
930 Secondary(data='s1')]))
931
932 h1 = session.query(Hoho).get(h1.id)
933 h1.secondaries.append(Secondary(data='s2'))
934 session.flush()
935 session.expunge_all()
936
937 eq_(session.query(Hoho).get(h1.id),
938 Hoho(hoho=hohoval,
939 secondaries=[
940 Secondary(data='s1'),
941 Secondary(data='s2')]))
942
943class ColumnPropertyTest(fixtures.MappedTest):
944 @classmethod
945 def define_tables(cls, metadata):
946 Table('data', metadata,
947 Column('id', Integer, primary_key=True, test_needs_autoincrement=True),
948 Column('a', String(50)),
949 Column('b', String(50))
950 )
951
952 Table('subdata', metadata,
953 Column('id', Integer, ForeignKey('data.id'), primary_key=True),
954 Column('c', String(50)),
955 )
956
957 @classmethod
958 def setup_mappers(cls):
959 class Data(cls.Basic):
960 pass
961
962 def test_refreshes(self):
963 Data, data = self.classes.Data, self.tables.data
964
965 mapper(Data, data, properties={
966 'aplusb':column_property(data.c.a + literal_column("' '") + data.c.b)
967 })
968 self._test(True)
969
970 def test_no_refresh(self):
971 Data, data = self.classes.Data, self.tables.data
972
973 mapper(Data, data, properties={
974 'aplusb':column_property(data.c.a + literal_column("' '") + data.c.b,
975 expire_on_flush=False)
976 })
977 self._test(False)
978
979 def test_refreshes_post_init(self):
980 Data, data = self.classes.Data, self.tables.data
981
982 m = mapper(Data, data)
983 m.add_property('aplusb', column_property(data.c.a + literal_column("' '") + data.c.b))
984 self._test(True)
985
986 def test_with_inheritance(self):
987 subdata, data, Data = (self.tables.subdata,
988 self.tables.data,
989 self.classes.Data)
990
991 class SubData(Data):
992 pass
993 mapper(Data, data, properties={
994 'aplusb':column_property(data.c.a + literal_column("' '") + data.c.b)
995 })
996 mapper(SubData, subdata, inherits=Data)
997
998 sess = create_session()
999 sd1 = SubData(a="hello", b="there", c="hi")
1000 sess.add(sd1)
1001 sess.flush()
1002 eq_(sd1.aplusb, "hello there")
1003
1004 def _test(self, expect_expiry):
1005 Data = self.classes.Data
1006
1007 sess = create_session()
1008
1009 d1 = Data(a="hello", b="there")
1010 sess.add(d1)
1011 sess.flush()
1012
1013 eq_(d1.aplusb, "hello there")
1014
1015 d1.b = "bye"
1016 sess.flush()
1017 if expect_expiry:
1018 eq_(d1.aplusb, "hello bye")
1019 else:
1020 eq_(d1.aplusb, "hello there")
1021
1022
1023 d1.b = 'foobar'
1024 d1.aplusb = 'im setting this explicitly'
1025 sess.flush()
1026 eq_(d1.aplusb, "im setting this explicitly")
1027
1028class OneToManyTest(_fixtures.FixtureTest):
1029 run_inserts = None
1030
1031 def test_one_to_many_1(self):
1032 """Basic save of one to many."""
1033
1034 Address, addresses, users, User = (self.classes.Address,
1035 self.tables.addresses,
1036 self.tables.users,
1037 self.classes.User)
1038
1039
1040 m = mapper(User, users, properties=dict(
1041 addresses = relationship(mapper(Address, addresses), lazy='select')
1042 ))
1043 u = User(name= 'one2manytester')
1044 a = Address(email_address='one2many@test.org')
1045 u.addresses.append(a)
1046
1047 a2 = Address(email_address='lala@test.org')
1048 u.addresses.append(a2)
1049
1050 session = create_session()
1051 session.add(u)
1052 session.flush()
1053
1054 user_rows = users.select(users.c.id.in_([u.id])).execute().fetchall()
1055 eq_(user_rows[0].values(), [u.id, 'one2manytester'])
1056
1057 address_rows = addresses.select(
1058 addresses.c.id.in_([a.id, a2.id]),
1059 order_by=[addresses.c.email_address]).execute().fetchall()
1060 eq_(address_rows[0].values(), [a2.id, u.id, 'lala@test.org'])
1061 eq_(address_rows[1].values(), [a.id, u.id, 'one2many@test.org'])
1062
1063 userid = u.id
1064 addressid = a2.id
1065
1066 a2.email_address = 'somethingnew@foo.com'
1067
1068 session.flush()
1069
1070 address_rows = addresses.select(
1071 addresses.c.id == addressid).execute().fetchall()
1072 eq_(address_rows[0].values(),
1073 [addressid, userid, 'somethingnew@foo.com'])
1074 self.assert_(u.id == userid and a2.id == addressid)
1075
1076 def test_one_to_many_2(self):
1077 """Modifying the child items of an object."""
1078
1079 Address, addresses, users, User = (self.classes.Address,
1080 self.tables.addresses,
1081 self.tables.users,
1082 self.classes.User)
1083
1084
1085 m = mapper(User, users, properties=dict(
1086 addresses = relationship(mapper(Address, addresses), lazy='select')))
1087
1088 u1 = User(name='user1')
1089 u1.addresses = []
1090 a1 = Address(email_address='emailaddress1')
1091 u1.addresses.append(a1)
1092
1093 u2 = User(name='user2')
1094 u2.addresses = []
1095 a2 = Address(email_address='emailaddress2')
1096 u2.addresses.append(a2)
1097
1098 a3 = Address(email_address='emailaddress3')
1099
1100 session = create_session()
1101 session.add_all((u1, u2, a3))
1102 session.flush()
1103
1104 # modify user2 directly, append an address to user1.
1105 # upon commit, user2 should be updated, user1 should not
1106 # both address1 and address3 should be updated
1107 u2.name = 'user2modified'
1108 u1.addresses.append(a3)
1109 del u1.addresses[0]
1110
1111 self.assert_sql(testing.db, session.flush, [
1112 ("UPDATE users SET name=:name "
1113 "WHERE users.id = :users_id",
1114 {'users_id': u2.id, 'name': 'user2modified'}),
1115
1116 ("UPDATE addresses SET user_id=:user_id "
1117 "WHERE addresses.id = :addresses_id",
1118 {'user_id': None, 'addresses_id': a1.id}),
1119
1120 ("UPDATE addresses SET user_id=:user_id "
1121 "WHERE addresses.id = :addresses_id",
1122 {'user_id': u1.id, 'addresses_id': a3.id})])
1123
1124 def test_child_move(self):
1125 """Moving a child from one parent to another, with a delete.
1126
1127 Tests that deleting the first parent properly updates the child with
1128 the new parent. This tests the 'trackparent' option in the attributes
1129 module.
1130
1131 """
1132
1133 Address, addresses, users, User = (self.classes.Address,
1134 self.tables.addresses,
1135 self.tables.users,
1136 self.classes.User)
1137
1138 m = mapper(User, users, properties=dict(
1139 addresses = relationship(mapper(Address, addresses), lazy='select')))
1140
1141 u1 = User(name='user1')
1142 u2 = User(name='user2')
1143 a = Address(email_address='address1')
1144 u1.addresses.append(a)
1145
1146 session = create_session()
1147 session.add_all((u1, u2))
1148 session.flush()
1149
1150 del u1.addresses[0]
1151 u2.addresses.append(a)
1152 session.delete(u1)
1153
1154 session.flush()
1155 session.expunge_all()
1156
1157 u2 = session.query(User).get(u2.id)
1158 eq_(len(u2.addresses), 1)
1159
1160 def test_child_move_2(self):
1161 Address, addresses, users, User = (self.classes.Address,
1162 self.tables.addresses,
1163 self.tables.users,
1164 self.classes.User)
1165
1166 m = mapper(User, users, properties=dict(
1167 addresses = relationship(mapper(Address, addresses), lazy='select')))
1168
1169 u1 = User(name='user1')
1170 u2 = User(name='user2')
1171 a = Address(email_address='address1')
1172 u1.addresses.append(a)
1173
1174 session = create_session()
1175 session.add_all((u1, u2))
1176 session.flush()
1177
1178 del u1.addresses[0]
1179 u2.addresses.append(a)
1180
1181 session.flush()
1182 session.expunge_all()
1183
1184 u2 = session.query(User).get(u2.id)
1185 eq_(len(u2.addresses), 1)
1186
1187 def test_o2m_delete_parent(self):
1188 Address, addresses, users, User = (self.classes.Address,
1189 self.tables.addresses,
1190 self.tables.users,
1191 self.classes.User)
1192
1193 m = mapper(User, users, properties=dict(
1194 address = relationship(mapper(Address, addresses),
1195 lazy='select',
1196 uselist=False)))
1197
1198 u = User(name='one2onetester')
1199 a = Address(email_address='myonlyaddress@foo.com')
1200 u.address = a
1201
1202 session = create_session()
1203 session.add(u)
1204 session.flush()
1205
1206 session.delete(u)
1207 session.flush()
1208
1209 assert a.id is not None
1210 assert a.user_id is None
1211 assert sa.orm.attributes.instance_state(a).key in session.identity_map
1212 assert sa.orm.attributes.instance_state(u).key not in session.identity_map
1213
1214 def test_one_to_one(self):
1215 Address, addresses, users, User = (self.classes.Address,
1216 self.tables.addresses,
1217 self.tables.users,
1218 self.classes.User)
1219
1220 m = mapper(User, users, properties=dict(
1221 address = relationship(mapper(Address, addresses),
1222 lazy='select',
1223 uselist=False)))
1224
1225 u = User(name='one2onetester')
1226 u.address = Address(email_address='myonlyaddress@foo.com')
1227
1228 session = create_session()
1229 session.add(u)
1230 session.flush()
1231
1232 u.name = 'imnew'
1233 session.flush()
1234
1235 u.address.email_address = 'imnew@foo.com'
1236 session.flush()
1237
1238 def test_bidirectional(self):
1239 users, Address, addresses, User = (self.tables.users,
1240 self.classes.Address,
1241 self.tables.addresses,
1242 self.classes.User)
1243
1244 m1 = mapper(User, users)
1245 m2 = mapper(Address, addresses, properties=dict(
1246 user = relationship(m1, lazy='joined', backref='addresses')))
1247
1248
1249 u = User(name='test')
1250 a = Address(email_address='testaddress', user=u)
1251
1252 session = create_session()
1253 session.add(u)
1254 session.flush()
1255 session.delete(u)
1256 session.flush()
1257
1258 def test_double_relationship(self):
1259 Address, addresses, users, User = (self.classes.Address,
1260 self.tables.addresses,
1261 self.tables.users,
1262 self.classes.User)
1263
1264 m2 = mapper(Address, addresses)
1265 m = mapper(User, users, properties={
1266 'boston_addresses' : relationship(m2, primaryjoin=
1267 sa.and_(users.c.id==addresses.c.user_id,
1268 addresses.c.email_address.like('%boston%'))),
1269 'newyork_addresses' : relationship(m2, primaryjoin=
1270 sa.and_(users.c.id==addresses.c.user_id,
1271 addresses.c.email_address.like('%newyork%')))})
1272
1273 u = User(name='u1')
1274 a = Address(email_address='foo@boston.com')
1275 b = Address(email_address='bar@newyork.com')
1276 u.boston_addresses.append(a)
1277 u.newyork_addresses.append(b)
1278
1279 session = create_session()
1280 session.add(u)
1281 session.flush()
1282
1283class SaveTest(_fixtures.FixtureTest):
1284 run_inserts = None
1285
1286 def test_basic(self):
1287 User, users = self.classes.User, self.tables.users
1288
1289 m = mapper(User, users)
1290
1291 # save two users
1292 u = User(name='savetester')
1293 u2 = User(name='savetester2')
1294
1295 session = create_session()
1296 session.add_all((u, u2))
1297 session.flush()
1298
1299 # assert the first one retreives the same from the identity map
1300 nu = session.query(m).get(u.id)
1301 assert u is nu
1302
1303 # clear out the identity map, so next get forces a SELECT
1304 session.expunge_all()
1305
1306 # check it again, identity should be different but ids the same
1307 nu = session.query(m).get(u.id)
1308 assert u is not nu and u.id == nu.id and nu.name == 'savetester'
1309
1310 # change first users name and save
1311 session = create_session()
1312 session.add(u)
1313 u.name = 'modifiedname'
1314 assert u in session.dirty
1315 session.flush()
1316
1317 # select both
1318 userlist = session.query(User).filter(
1319 users.c.id.in_([u.id, u2.id])).order_by(users.c.name).all()
1320
1321 eq_(u.id, userlist[0].id)
1322 eq_(userlist[0].name, 'modifiedname')
1323 eq_(u2.id, userlist[1].id)
1324 eq_(userlist[1].name, 'savetester2')
1325
1326 def test_synonym(self):
1327 users = self.tables.users
1328
1329 class SUser(fixtures.BasicEntity):
1330 def _get_name(self):
1331 return "User:" + self.name
1332 def _set_name(self, name):
1333 self.name = name + ":User"
1334 syn_name = property(_get_name, _set_name)
1335
1336 mapper(SUser, users, properties={
1337 'syn_name': sa.orm.synonym('name')
1338 })
1339
1340 u = SUser(syn_name="some name")
1341 eq_(u.syn_name, 'User:some name:User')
1342
1343 session = create_session()
1344 session.add(u)
1345 session.flush()
1346 session.expunge_all()
1347
1348 u = session.query(SUser).first()
1349 eq_(u.syn_name, 'User:some name:User')
1350
1351 def test_lazyattr_commit(self):
1352 """Lazily loaded relationships.
1353
1354 When a lazy-loaded list is unloaded, and a commit occurs, that the
1355 'passive' call on that list does not blow away its value
1356
1357 """
1358
1359 users, Address, addresses, User = (self.tables.users,
1360 self.classes.Address,
1361 self.tables.addresses,
1362 self.classes.User)
1363
1364 mapper(User, users, properties = {
1365 'addresses': relationship(mapper(Address, addresses))})
1366
1367 u = User(name='u1')
1368 u.addresses.append(Address(email_address='u1@e1'))
1369 u.addresses.append(Address(email_address='u1@e2'))
1370 u.addresses.append(Address(email_address='u1@e3'))
1371 u.addresses.append(Address(email_address='u1@e4'))
1372
1373 session = create_session()
1374 session.add(u)
1375 session.flush()
1376 session.expunge_all()
1377
1378 u = session.query(User).one()
1379 u.name = 'newname'
1380 session.flush()
1381 eq_(len(u.addresses), 4)
1382
1383 def test_inherits(self):
1384 """a user object that also has the users mailing address."""
1385
1386 users, addresses, User = (self.tables.users,
1387 self.tables.addresses,
1388 self.classes.User)
1389
1390 m1 = mapper(User, users)
1391
1392 class AddressUser(User):
1393 pass
1394
1395 # define a mapper for AddressUser that inherits the User.mapper, and
1396 # joins on the id column
1397 mapper(AddressUser, addresses, inherits=m1)
1398
1399 au = AddressUser(name='u', email_address='u@e')
1400
1401 session = create_session()
1402 session.add(au)
1403 session.flush()
1404 session.expunge_all()
1405
1406 rt = session.query(AddressUser).one()
1407 eq_(au.user_id, rt.user_id)
1408 eq_(rt.id, rt.id)
1409
1410 def test_deferred(self):
1411 """Deferred column operations"""
1412
1413 orders, Order = self.tables.orders, self.classes.Order
1414
1415
1416 mapper(Order, orders, properties={
1417 'description': sa.orm.deferred(orders.c.description)})
1418
1419 # dont set deferred attribute, commit session
1420 o = Order(id=42)
1421 session = create_session(autocommit=False)
1422 session.add(o)
1423 session.commit()
1424
1425 # assert that changes get picked up
1426 o.description = 'foo'
1427 session.commit()
1428
1429 eq_(list(session.execute(orders.select(), mapper=Order)),
1430 [(42, None, None, 'foo', None)])
1431 session.expunge_all()
1432
1433 # assert that a set operation doesn't trigger a load operation
1434 o = session.query(Order).filter(Order.description == 'foo').one()
1435 def go():
1436 o.description = 'hoho'
1437 self.sql_count_(0, go)
1438 session.flush()
1439
1440 eq_(list(session.execute(orders.select(), mapper=Order)),
1441 [(42, None, None, 'hoho', None)])
1442
1443 session.expunge_all()
1444
1445 # test assigning None to an unloaded deferred also works
1446 o = session.query(Order).filter(Order.description == 'hoho').one()
1447 o.description = None
1448 session.flush()
1449 eq_(list(session.execute(orders.select(), mapper=Order)),
1450 [(42, None, None, None, None)])
1451 session.close()
1452
1453 # why no support on oracle ? because oracle doesn't save
1454 # "blank" strings; it saves a single space character.
1455 @testing.fails_on('oracle', 'FIXME: unknown')
1456 def test_dont_update_blanks(self):
1457 User, users = self.classes.User, self.tables.users
1458
1459 mapper(User, users)
1460
1461 u = User(name='')
1462 session = create_session()
1463 session.add(u)
1464 session.flush()
1465 session.expunge_all()
1466
1467 u = session.query(User).get(u.id)
1468 u.name = ''
1469 self.sql_count_(0, session.flush)
1470
1471 def test_multi_table_selectable(self):
1472 """Mapped selectables that span tables.
1473
1474 Also tests redefinition of the keynames for the column properties.
1475
1476 """
1477
1478 addresses, users, User = (self.tables.addresses,
1479 self.tables.users,
1480 self.classes.User)
1481
1482 usersaddresses = sa.join(users, addresses,
1483 users.c.id == addresses.c.user_id)
1484
1485 m = mapper(User, usersaddresses,
1486 properties=dict(
1487 email = addresses.c.email_address,
1488 foo_id = [users.c.id, addresses.c.user_id]))
1489
1490 u = User(name='multitester', email='multi@test.org')
1491 session = create_session()
1492 session.add(u)
1493 session.flush()
1494 session.expunge_all()
1495
1496 id = m.primary_key_from_instance(u)
1497
1498 u = session.query(User).get(id)
1499 assert u.name == 'multitester'
1500
1501 user_rows = users.select(users.c.id.in_([u.foo_id])).execute().fetchall()
1502 eq_(user_rows[0].values(), [u.foo_id, 'multitester'])
1503 address_rows = addresses.select(addresses.c.id.in_([u.id])).execute().fetchall()
1504 eq_(address_rows[0].values(), [u.id, u.foo_id, 'multi@test.org'])
1505
1506 u.email = 'lala@hey.com'
1507 u.name = 'imnew'
1508 session.flush()
1509
1510 user_rows = users.select(users.c.id.in_([u.foo_id])).execute().fetchall()
1511 eq_(user_rows[0].values(), [u.foo_id, 'imnew'])
1512 address_rows = addresses.select(addresses.c.id.in_([u.id])).execute().fetchall()
1513 eq_(address_rows[0].values(), [u.id, u.foo_id, 'lala@hey.com'])
1514
1515 session.expunge_all()
1516 u = session.query(User).get(id)
1517 assert u.name == 'imnew'
1518
1519 def test_history_get(self):
1520 """The history lazy-fetches data when it wasn't otherwise loaded."""
1521
1522 users, Address, addresses, User = (self.tables.users,
1523 self.classes.Address,
1524 self.tables.addresses,
1525 self.classes.User)
1526
1527 mapper(User, users, properties={
1528 'addresses':relationship(Address, cascade="all, delete-orphan")})
1529 mapper(Address, addresses)
1530
1531 u = User(name='u1')
1532 u.addresses.append(Address(email_address='u1@e1'))
1533 u.addresses.append(Address(email_address='u1@e2'))
1534 session = create_session()
1535 session.add(u)
1536 session.flush()
1537 session.expunge_all()
1538
1539 u = session.query(User).get(u.id)
1540 session.delete(u)
1541 session.flush()
1542 assert users.count().scalar() == 0
1543 assert addresses.count().scalar() == 0
1544
1545 def test_batch_mode(self):
1546 """The 'batch=False' flag on mapper()"""
1547
1548 users, User = self.tables.users, self.classes.User
1549
1550
1551 names = []
1552 class Events(object):
1553 def before_insert(self, mapper,…
Large files files are truncated, but you can click here to view the full file