~ubuntu-branches/debian/jessie/sqlalchemy/jessie

« back to all changes in this revision

Viewing changes to test/orm/test_unitofwork.py

  • Committer: Bazaar Package Importer
  • Author(s): Piotr Ożarowski
  • Date: 2011-08-01 23:18:16 UTC
  • mfrom: (1.4.15 upstream) (16.1.14 experimental)
  • Revision ID: james.westby@ubuntu.com-20110801231816-6lx797pi3q1fpqst
Tags: 0.7.2-1
* New upstream release
* Bump minimum required python-mako version to 0.4.1 (closes: 635898)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# coding: utf-8
2
2
"""Tests unitofwork operations."""
3
3
 
4
 
from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
 
4
from test.lib.testing import eq_, assert_raises, assert_raises_message
5
5
import datetime
6
6
import operator
7
7
from sqlalchemy.orm import mapper as orm_mapper
8
8
 
9
9
import sqlalchemy as sa
10
 
from sqlalchemy.test import engines, testing, pickleable
11
 
from sqlalchemy import Integer, String, ForeignKey, literal_column
12
 
from sqlalchemy.test.schema import Table
13
 
from sqlalchemy.test.schema import Column
 
10
from sqlalchemy import Integer, String, ForeignKey, literal_column, event
 
11
from test.lib import engines, testing, pickleable
 
12
from test.lib.schema import Table
 
13
from test.lib.schema import Column
14
14
from sqlalchemy.orm import mapper, relationship, create_session, \
15
15
    column_property, attributes, Session, reconstructor, object_session
16
 
from sqlalchemy.test.testing import eq_, ne_
17
 
from sqlalchemy.test.util import gc_collect
18
 
from test.orm import _base, _fixtures
19
 
from test.engine import _base as engine_base
20
 
from sqlalchemy.test.assertsql import AllOf, CompiledSQL
 
16
from test.lib.testing import eq_, ne_
 
17
from test.lib.util import gc_collect
 
18
from test.lib import fixtures
 
19
from test.orm import _fixtures
 
20
from test.lib import fixtures
 
21
from test.lib.assertsql import AllOf, CompiledSQL
21
22
import gc
22
23
 
23
24
class UnitOfWorkTest(object):
28
29
 
29
30
    @classmethod
30
31
    def setup_classes(cls):
31
 
        class User(_base.ComparableEntity):
 
32
        class User(cls.Comparable):
32
33
            pass
33
 
        class Address(_base.ComparableEntity):
 
34
        class Address(cls.Comparable):
34
35
            pass
35
36
 
36
 
    @testing.resolve_artifact_names
37
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
 
38
43
        am = mapper(Address, addresses)
39
44
        m = mapper(User, users, properties=dict(
40
45
            addresses = relationship(am, backref='user', lazy='joined')))
46
51
        a.user = u
47
52
        session.add(u)
48
53
 
49
 
        self.assert_(u.addresses == [a])
 
54
        eq_(u.addresses, [a])
50
55
        session.commit()
51
56
        session.expunge_all()
52
57
 
54
59
        assert u.addresses[0].user == u
55
60
        session.close()
56
61
 
57
 
class UnicodeTest(_base.MappedTest):
 
62
class UnicodeTest(fixtures.MappedTest):
58
63
    __requires__ = ('unicode_connections',)
59
64
 
60
65
    @classmethod
76
81
 
77
82
    @classmethod
78
83
    def setup_classes(cls):
79
 
        class Test(_base.BasicEntity):
 
84
        class Test(cls.Basic):
80
85
            pass
81
 
        class Test2(_base.BasicEntity):
 
86
        class Test2(cls.Basic):
82
87
            pass
83
88
 
84
 
    @testing.resolve_artifact_names
85
89
    def test_basic(self):
 
90
        Test, uni_t1 = self.classes.Test, self.tables.uni_t1
 
91
 
86
92
        mapper(Test, uni_t1)
87
93
 
88
94
        txt = u"\u0160\u0110\u0106\u010c\u017d"
95
101
 
96
102
        self.assert_(t1.txt == txt)
97
103
 
98
 
    @testing.resolve_artifact_names
99
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
 
100
110
        mapper(Test, uni_t1, properties={
101
111
            't2s': relationship(Test2)})
102
112
        mapper(Test2, uni_t2)
114
124
        t1 = session.query(Test).filter_by(id=t1.id).one()
115
125
        assert len(t1.t2s) == 2
116
126
 
117
 
class UnicodeSchemaTest(engine_base.AltEngineTest, _base.MappedTest):
 
127
class UnicodeSchemaTest(fixtures.MappedTest):
118
128
    __requires__ = ('unicode_connections', 'unicode_ddl',)
119
129
 
 
130
    run_dispose_bind = 'once'
 
131
 
120
132
    @classmethod
121
133
    def create_engine(cls):
122
134
        return engines.utf8_engine()
150
162
 
151
163
    @testing.fails_on('mssql+pyodbc',
152
164
                      'pyodbc returns a non unicode encoding of the results description.')
153
 
    @testing.resolve_artifact_names
154
165
    def test_mapping(self):
155
 
        class A(_base.ComparableEntity):
 
166
        t2, t1 = self.tables.t2, self.tables.t1
 
167
 
 
168
        class A(fixtures.ComparableEntity):
156
169
            pass
157
 
        class B(_base.ComparableEntity):
 
170
        class B(fixtures.ComparableEntity):
158
171
            pass
159
172
 
160
173
        mapper(A, t1, properties={
188
201
 
189
202
    @testing.fails_on('mssql+pyodbc',
190
203
                      'pyodbc returns a non unicode encoding of the results description.')
191
 
    @testing.resolve_artifact_names
192
204
    def test_inheritance_mapping(self):
193
 
        class A(_base.ComparableEntity):
 
205
        t2, t1 = self.tables.t2, self.tables.t1
 
206
 
 
207
        class A(fixtures.ComparableEntity):
194
208
            pass
195
209
        class B(A):
196
210
            pass
211
225
 
212
226
        eq_([A(b=5), B(e=7)], session.query(A).all())
213
227
 
214
 
class BinaryHistTest(_base.MappedTest, testing.AssertsExecutionResults):
 
228
class BinaryHistTest(fixtures.MappedTest, testing.AssertsExecutionResults):
215
229
    @classmethod
216
230
    def define_tables(cls, metadata):
217
231
        Table('t1', metadata,
221
235
 
222
236
    @classmethod
223
237
    def setup_classes(cls):
224
 
        class Foo(_base.BasicEntity):
 
238
        class Foo(cls.Basic):
225
239
            pass
226
240
 
227
 
    @testing.resolve_artifact_names
228
241
    def test_binary_equality(self):
 
242
        Foo, t1 = self.classes.Foo, self.tables.t1
 
243
 
229
244
 
230
245
        # Py3K
231
246
        #data = b"this is some data"
252
267
            s.flush()
253
268
        self.assert_sql_count(testing.db, go, 0)
254
269
 
255
 
class MutableTypesTest(_base.MappedTest):
256
 
 
257
 
    @classmethod
258
 
    def define_tables(cls, metadata):
259
 
        Table('mutable_t', metadata,
260
 
            Column('id', Integer, primary_key=True,
261
 
                   test_needs_autoincrement=True),
262
 
            Column('data', sa.PickleType),
263
 
            Column('val', sa.Unicode(30)))
264
 
 
265
 
    @classmethod
266
 
    def setup_classes(cls):
267
 
        class Foo(_base.BasicEntity):
268
 
            pass
269
 
 
270
 
    @classmethod
271
 
    @testing.resolve_artifact_names
272
 
    def setup_mappers(cls):
273
 
        mapper(Foo, mutable_t)
274
 
 
275
 
    @testing.resolve_artifact_names
276
 
    def test_modified_status(self):
277
 
        f1 = Foo(data = pickleable.Bar(4,5))
278
 
 
279
 
        session = Session()
280
 
        session.add(f1)
281
 
        session.commit()
282
 
 
283
 
        f2 = session.query(Foo).first()
284
 
        assert 'data' in sa.orm.attributes.instance_state(f2).unmodified
285
 
        eq_(f2.data, f1.data)
286
 
 
287
 
        f2.data.y = 19
288
 
        assert f2 in session.dirty
289
 
        assert 'data' not in sa.orm.attributes.instance_state(f2).unmodified
290
 
 
291
 
    @testing.resolve_artifact_names
292
 
    def test_mutations_persisted(self):
293
 
        f1 = Foo(data = pickleable.Bar(4,5))
294
 
 
295
 
        session = Session()
296
 
        session.add(f1)
297
 
        session.commit()
298
 
        f1.data
299
 
        session.close()
300
 
 
301
 
        f2 = session.query(Foo).first()
302
 
        f2.data.y = 19
303
 
        session.commit()
304
 
        f2.data
305
 
        session.close()
306
 
 
307
 
        f3 = session.query(Foo).first()
308
 
        ne_(f3.data,f1.data)
309
 
        eq_(f3.data, pickleable.Bar(4, 19))
310
 
 
311
 
    @testing.resolve_artifact_names
312
 
    def test_no_unnecessary_update(self):
313
 
        f1 = Foo(data = pickleable.Bar(4,5), val = u'hi')
314
 
 
315
 
        session = Session()
316
 
        session.add(f1)
317
 
        session.commit()
318
 
 
319
 
        self.sql_count_(0, session.commit)
320
 
 
321
 
        f1.val = u'someothervalue'
322
 
        self.assert_sql(testing.db, session.commit, [
323
 
            ("UPDATE mutable_t SET val=:val "
324
 
             "WHERE mutable_t.id = :mutable_t_id",
325
 
             {'mutable_t_id': f1.id, 'val': u'someothervalue'})])
326
 
 
327
 
        f1.val = u'hi'
328
 
        f1.data.x = 9
329
 
        self.assert_sql(testing.db, session.commit, [
330
 
            ("UPDATE mutable_t SET data=:data, val=:val "
331
 
             "WHERE mutable_t.id = :mutable_t_id",
332
 
             {'mutable_t_id': f1.id, 'val': u'hi', 'data':f1.data})])
333
 
 
334
 
    @testing.resolve_artifact_names
335
 
    def test_mutated_state_resurrected(self):
336
 
        f1 = Foo(data = pickleable.Bar(4,5), val = u'hi')
337
 
 
338
 
        session = Session()
339
 
        session.add(f1)
340
 
        session.commit()
341
 
 
342
 
        f1.data.y = 19
343
 
        del f1
344
 
 
345
 
        gc_collect()
346
 
        assert len(session.identity_map) == 1
347
 
 
348
 
        session.commit()
349
 
 
350
 
        assert session.query(Foo).one().data == pickleable.Bar(4, 19)
351
 
 
352
 
    @testing.resolve_artifact_names
353
 
    def test_mutated_plus_scalar_state_change_resurrected(self):
354
 
        """test that a non-mutable attribute event subsequent to
355
 
        a mutable event prevents the object from falling into
356
 
        resurrected state.
357
 
 
358
 
         """
359
 
        f1 = Foo(data = pickleable.Bar(4, 5), val=u'some val')
360
 
        session = Session()
361
 
        session.add(f1)
362
 
        session.commit()
363
 
        f1.data.x = 10
364
 
        f1.data.y = 15
365
 
        f1.val=u'some new val'
366
 
 
367
 
        assert sa.orm.attributes.instance_state(f1)._strong_obj is not None
368
 
 
369
 
        del f1
370
 
        session.commit()
371
 
        eq_(
372
 
            session.query(Foo.val).all(),
373
 
            [('some new val', )]
374
 
        )
375
 
 
376
 
    @testing.resolve_artifact_names
377
 
    def test_non_mutated_state_not_resurrected(self):
378
 
        f1 = Foo(data = pickleable.Bar(4,5))
379
 
 
380
 
        session = Session()
381
 
        session.add(f1)
382
 
        session.commit()
383
 
 
384
 
        session = Session()
385
 
        f1 = session.query(Foo).first()
386
 
        del f1
387
 
        gc_collect()
388
 
 
389
 
        assert len(session.identity_map) == 0
390
 
        f1 = session.query(Foo).first()
391
 
        assert not attributes.instance_state(f1).modified
392
 
 
393
 
    @testing.resolve_artifact_names
394
 
    def test_scalar_no_net_change_no_update(self):
395
 
        """Test that a no-net-change on a scalar attribute event
396
 
        doesn't cause an UPDATE for a mutable state.
397
 
 
398
 
         """
399
 
 
400
 
        f1 = Foo(val=u'hi')
401
 
 
402
 
        session = Session()
403
 
        session.add(f1)
404
 
        session.commit()
405
 
        session.close()
406
 
 
407
 
        f1 = session.query(Foo).first()
408
 
        f1.val = u'hi'
409
 
        self.sql_count_(0, session.commit)
410
 
 
411
 
    @testing.resolve_artifact_names
412
 
    def test_expire_attribute_set(self):
413
 
        """test one SELECT emitted when assigning to an expired
414
 
        mutable attribute - this will become 0 in 0.7.
415
 
 
416
 
        """
417
 
 
418
 
        f1 = Foo(data = pickleable.Bar(4, 5), val=u'some val')
419
 
        session = Session()
420
 
        session.add(f1)
421
 
        session.commit()
422
 
 
423
 
        assert 'data' not in f1.__dict__
424
 
        def go():
425
 
            f1.data = pickleable.Bar(10, 15)
426
 
        self.sql_count_(1, go)
427
 
        session.commit()
428
 
 
429
 
        eq_(f1.data.x, 10)
430
 
 
431
 
    @testing.resolve_artifact_names
432
 
    def test_expire_mutate(self):
433
 
        """test mutations are detected on an expired mutable
434
 
        attribute."""
435
 
 
436
 
        f1 = Foo(data = pickleable.Bar(4, 5), val=u'some val')
437
 
        session = Session()
438
 
        session.add(f1)
439
 
        session.commit()
440
 
 
441
 
        assert 'data' not in f1.__dict__
442
 
        def go():
443
 
            f1.data.x = 10
444
 
        self.sql_count_(1, go)
445
 
        session.commit()
446
 
 
447
 
        eq_(f1.data.x, 10)
448
 
 
449
 
    @testing.resolve_artifact_names
450
 
    def test_deferred_attribute_set(self):
451
 
        """test one SELECT emitted when assigning to a deferred
452
 
        mutable attribute - this will become 0 in 0.7.
453
 
 
454
 
        """
455
 
        sa.orm.clear_mappers()
456
 
        mapper(Foo, mutable_t, properties={
457
 
            'data':sa.orm.deferred(mutable_t.c.data)
458
 
        })
459
 
 
460
 
        f1 = Foo(data = pickleable.Bar(4, 5), val=u'some val')
461
 
        session = Session()
462
 
        session.add(f1)
463
 
        session.commit()
464
 
 
465
 
        session.close()
466
 
 
467
 
        f1 = session.query(Foo).first()
468
 
        def go():
469
 
            f1.data = pickleable.Bar(10, 15)
470
 
        self.sql_count_(1, go)
471
 
        session.commit()
472
 
 
473
 
        eq_(f1.data.x, 10)
474
 
 
475
 
    @testing.resolve_artifact_names
476
 
    def test_deferred_mutate(self):
477
 
        """test mutations are detected on a deferred mutable
478
 
        attribute."""
479
 
 
480
 
        sa.orm.clear_mappers()
481
 
        mapper(Foo, mutable_t, properties={
482
 
            'data':sa.orm.deferred(mutable_t.c.data)
483
 
        })
484
 
 
485
 
        f1 = Foo(data = pickleable.Bar(4, 5), val=u'some val')
486
 
        session = Session()
487
 
        session.add(f1)
488
 
        session.commit()
489
 
 
490
 
        session.close()
491
 
 
492
 
        f1 = session.query(Foo).first()
493
 
        def go():
494
 
            f1.data.x = 10
495
 
        self.sql_count_(1, go)
496
 
        session.commit()
497
 
 
498
 
        def go():
499
 
            eq_(f1.data.x, 10)
500
 
        self.sql_count_(1, go)
501
 
 
502
 
 
503
 
class PickledDictsTest(_base.MappedTest):
504
 
 
505
 
    @classmethod
506
 
    def define_tables(cls, metadata):
507
 
        Table('mutable_t', metadata,
508
 
            Column('id', Integer, primary_key=True,
509
 
                   test_needs_autoincrement=True),
510
 
            Column('data', sa.PickleType(comparator=operator.eq)))
511
 
 
512
 
    @classmethod
513
 
    def setup_classes(cls):
514
 
        class Foo(_base.BasicEntity):
515
 
            pass
516
 
 
517
 
    @classmethod
518
 
    @testing.resolve_artifact_names
519
 
    def setup_mappers(cls):
520
 
        mapper(Foo, mutable_t)
521
 
 
522
 
    @testing.resolve_artifact_names
523
 
    def test_dicts(self):
524
 
        """Dictionaries may not pickle the same way twice."""
525
 
 
526
 
        f1 = Foo()
527
 
        f1.data = [ {
528
 
            'personne': {'nom': u'Smith',
529
 
                         'pers_id': 1,
530
 
                         'prenom': u'john',
531
 
                         'civilite': u'Mr',
532
 
                         'int_3': False,
533
 
                         'int_2': False,
534
 
                         'int_1': u'23',
535
 
                         'VenSoir': True,
536
 
                         'str_1': u'Test',
537
 
                         'SamMidi': False,
538
 
                         'str_2': u'chien',
539
 
                         'DimMidi': False,
540
 
                         'SamSoir': True,
541
 
                         'SamAcc': False} } ]
542
 
 
543
 
        session = create_session(autocommit=False)
544
 
        session.add(f1)
545
 
        session.commit()
546
 
 
547
 
        self.sql_count_(0, session.commit)
548
 
 
549
 
        f1.data = [ {
550
 
            'personne': {'nom': u'Smith',
551
 
                         'pers_id': 1,
552
 
                         'prenom': u'john',
553
 
                         'civilite': u'Mr',
554
 
                         'int_3': False,
555
 
                         'int_2': False,
556
 
                         'int_1': u'23',
557
 
                         'VenSoir': True,
558
 
                         'str_1': u'Test',
559
 
                         'SamMidi': False,
560
 
                         'str_2': u'chien',
561
 
                         'DimMidi': False,
562
 
                         'SamSoir': True,
563
 
                         'SamAcc': False} } ]
564
 
 
565
 
        self.sql_count_(0, session.commit)
566
 
 
567
 
        f1.data[0]['personne']['VenSoir']= False
568
 
        self.sql_count_(1, session.commit)
569
 
 
570
 
        session.expunge_all()
571
 
        f = session.query(Foo).get(f1.id)
572
 
        eq_(f.data,
573
 
            [ {
574
 
            'personne': {'nom': u'Smith',
575
 
                         'pers_id': 1,
576
 
                         'prenom': u'john',
577
 
                         'civilite': u'Mr',
578
 
                         'int_3': False,
579
 
                         'int_2': False,
580
 
                         'int_1': u'23',
581
 
                         'VenSoir': False,
582
 
                         'str_1': u'Test',
583
 
                         'SamMidi': False,
584
 
                         'str_2': u'chien',
585
 
                         'DimMidi': False,
586
 
                         'SamSoir': True,
587
 
                         'SamAcc': False} } ])
588
 
 
589
 
 
590
 
class PKTest(_base.MappedTest):
 
270
class PKTest(fixtures.MappedTest):
591
271
 
592
272
    @classmethod
593
273
    def define_tables(cls, metadata):
610
290
 
611
291
    @classmethod
612
292
    def setup_classes(cls):
613
 
        class Entry(_base.BasicEntity):
 
293
        class Entry(cls.Basic):
614
294
            pass
615
295
 
616
296
    # not supported on sqlite since sqlite's auto-pk generation only works with
617
297
    # single column primary keys
618
298
    @testing.fails_on('sqlite', 'FIXME: unknown')
619
 
    @testing.resolve_artifact_names
620
299
    def test_primary_key(self):
 
300
        Entry, multipk1 = self.classes.Entry, self.tables.multipk1
 
301
 
621
302
        mapper(Entry, multipk1)
622
303
 
623
304
        e = Entry(name='entry1', value='this is entry 1', multi_rev=2)
634
315
        eq_(state.key, state2.key)
635
316
 
636
317
    # this one works with sqlite since we are manually setting up pk values
637
 
    @testing.resolve_artifact_names
638
318
    def test_manual_pk(self):
 
319
        Entry, multipk2 = self.classes.Entry, self.tables.multipk2
 
320
 
639
321
        mapper(Entry, multipk2)
640
322
 
641
323
        e = Entry(pk_col_1='pk1', pk_col_2='pk1_related', data='im the data')
644
326
        session.add(e)
645
327
        session.flush()
646
328
 
647
 
    @testing.resolve_artifact_names
648
329
    def test_key_pks(self):
 
330
        Entry, multipk3 = self.classes.Entry, self.tables.multipk3
 
331
 
649
332
        mapper(Entry, multipk3)
650
333
 
651
334
        e = Entry(primary= 'pk1', secondary='pk2',
656
339
        session.flush()
657
340
 
658
341
 
659
 
class ForeignPKTest(_base.MappedTest):
 
342
class ForeignPKTest(fixtures.MappedTest):
660
343
    """Detection of the relationship direction on PK joins."""
661
344
 
662
345
    @classmethod
673
356
 
674
357
    @classmethod
675
358
    def setup_classes(cls):
676
 
        class Person(_base.BasicEntity):
 
359
        class Person(cls.Basic):
677
360
            pass
678
 
        class PersonSite(_base.BasicEntity):
 
361
        class PersonSite(cls.Basic):
679
362
            pass
680
363
 
681
 
    @testing.resolve_artifact_names
682
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
 
683
370
        m1 = mapper(PersonSite, peoplesites)
684
371
        m2 = mapper(Person, people, properties={
685
372
            'sites' : relationship(PersonSite)})
686
373
 
687
 
        sa.orm.compile_mappers()
 
374
        sa.orm.configure_mappers()
688
375
        eq_(list(m2.get_property('sites').synchronize_pairs),
689
376
            [(people.c.person, peoplesites.c.person)])
690
377
 
701
388
        eq_(peoplesites.count(peoplesites.c.person=='im the key').scalar(), 1)
702
389
 
703
390
 
704
 
class ClauseAttributesTest(_base.MappedTest):
 
391
class ClauseAttributesTest(fixtures.MappedTest):
705
392
 
706
393
    @classmethod
707
394
    def define_tables(cls, metadata):
713
400
 
714
401
    @classmethod
715
402
    def setup_classes(cls):
716
 
        class User(_base.ComparableEntity):
 
403
        class User(cls.Comparable):
717
404
            pass
718
405
 
719
406
    @classmethod
720
 
    @testing.resolve_artifact_names
721
407
    def setup_mappers(cls):
 
408
        User, users_t = cls.classes.User, cls.tables.users_t
 
409
 
722
410
        mapper(User, users_t)
723
411
 
724
 
    @testing.resolve_artifact_names
725
412
    def test_update(self):
 
413
        User = self.classes.User
 
414
 
726
415
        u = User(name='test')
727
416
 
728
417
        session = create_session()
737
426
            assert (u.counter == 2) is True  # ensure its not a ClauseElement
738
427
        self.sql_count_(1, go)
739
428
 
740
 
    @testing.resolve_artifact_names
741
429
    def test_multi_update(self):
 
430
        User = self.classes.User
 
431
 
742
432
        u = User(name='test')
743
433
 
744
434
        session = create_session()
760
450
        eq_(u.name, 'test2')
761
451
        eq_(u.counter,  2)
762
452
 
763
 
    @testing.resolve_artifact_names
764
453
    def test_insert(self):
 
454
        User = self.classes.User
 
455
 
765
456
        u = User(name='test', counter=sa.select([5]))
766
457
 
767
458
        session = create_session()
771
462
        assert (u.counter == 5) is True
772
463
 
773
464
 
774
 
class PassiveDeletesTest(_base.MappedTest):
 
465
class PassiveDeletesTest(fixtures.MappedTest):
775
466
    __requires__ = ('foreign_keys',)
776
467
 
777
468
    @classmethod
792
483
 
793
484
    @classmethod
794
485
    def setup_classes(cls):
795
 
        class MyClass(_base.BasicEntity):
 
486
        class MyClass(cls.Basic):
796
487
            pass
797
 
        class MyOtherClass(_base.BasicEntity):
 
488
        class MyOtherClass(cls.Basic):
798
489
            pass
799
490
 
800
 
    @testing.resolve_artifact_names
801
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
 
802
497
        mapper(MyOtherClass, myothertable)
803
498
        mapper(MyClass, mytable, properties={
804
499
            'children':relationship(MyOtherClass,
824
519
        assert myothertable.count().scalar() == 0
825
520
 
826
521
    @testing.emits_warning(r".*'passive_deletes' is normally configured on one-to-many")
827
 
    @testing.resolve_artifact_names
828
522
    def test_backwards_pd(self):
829
523
        """Test that passive_deletes=True disables a delete from an m2o.
830
524
 
832
526
        that it works nonetheless.
833
527
 
834
528
        """
 
529
 
 
530
        myothertable, MyClass, MyOtherClass, mytable = (self.tables.myothertable,
 
531
                                self.classes.MyClass,
 
532
                                self.classes.MyOtherClass,
 
533
                                self.tables.mytable)
 
534
 
835
535
        mapper(MyOtherClass, myothertable, properties={
836
536
            'myclass':relationship(MyClass, cascade="all, delete", passive_deletes=True)
837
537
        })
855
555
        assert mytable.count().scalar() == 1
856
556
        assert myothertable.count().scalar() == 0
857
557
 
858
 
    @testing.resolve_artifact_names
859
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
 
860
564
        mapper(MyOtherClass, myothertable, properties={
861
565
            'myclass':relationship(MyClass, cascade="all, delete", passive_deletes=True)
862
566
        })
863
567
        mapper(MyClass, mytable)
864
 
        assert_raises(sa.exc.SAWarning, sa.orm.compile_mappers)
 
568
        assert_raises(sa.exc.SAWarning, sa.orm.configure_mappers)
865
569
 
866
 
class ExtraPassiveDeletesTest(_base.MappedTest):
 
570
class ExtraPassiveDeletesTest(fixtures.MappedTest):
867
571
    __requires__ = ('foreign_keys',)
868
572
 
869
573
    @classmethod
884
588
 
885
589
    @classmethod
886
590
    def setup_classes(cls):
887
 
        class MyClass(_base.BasicEntity):
 
591
        class MyClass(cls.Basic):
888
592
            pass
889
 
        class MyOtherClass(_base.BasicEntity):
 
593
        class MyOtherClass(cls.Basic):
890
594
            pass
891
595
 
892
 
    @testing.resolve_artifact_names
893
596
    def test_assertions(self):
 
597
        myothertable, MyOtherClass = self.tables.myothertable, self.classes.MyOtherClass
 
598
 
894
599
        mapper(MyOtherClass, myothertable)
895
600
        assert_raises_message(
896
601
            sa.exc.ArgumentError,
901
606
                                    cascade="all"
902
607
        )
903
608
 
904
 
    @testing.resolve_artifact_names
905
609
    def test_extra_passive(self):
 
610
        myothertable, MyClass, MyOtherClass, mytable = (self.tables.myothertable,
 
611
                                self.classes.MyClass,
 
612
                                self.classes.MyOtherClass,
 
613
                                self.tables.mytable)
 
614
 
906
615
        mapper(MyOtherClass, myothertable)
907
616
        mapper(MyClass, mytable, properties={
908
617
            'children': relationship(MyOtherClass,
924
633
        session.delete(mc)
925
634
        assert_raises(sa.exc.DBAPIError, session.flush)
926
635
 
927
 
    @testing.resolve_artifact_names
928
636
    def test_extra_passive_2(self):
 
637
        myothertable, MyClass, MyOtherClass, mytable = (self.tables.myothertable,
 
638
                                self.classes.MyClass,
 
639
                                self.classes.MyOtherClass,
 
640
                                self.tables.mytable)
 
641
 
929
642
        mapper(MyOtherClass, myothertable)
930
643
        mapper(MyClass, mytable, properties={
931
644
            'children': relationship(MyOtherClass,
946
659
        mc.children[0].data = 'some new data'
947
660
        assert_raises(sa.exc.DBAPIError, session.flush)
948
661
 
949
 
    @testing.resolve_artifact_names
950
662
    def test_dont_emit(self):
 
663
        myothertable, MyClass, MyOtherClass, mytable = (self.tables.myothertable,
 
664
                                self.classes.MyClass,
 
665
                                self.classes.MyOtherClass,
 
666
                                self.tables.mytable)
 
667
 
951
668
        mapper(MyOtherClass, myothertable)
952
669
        mapper(MyClass, mytable, properties={
953
670
            'children': relationship(MyOtherClass,
964
681
        # no load for "children" should occur
965
682
        self.assert_sql_count(testing.db, session.flush, 1)
966
683
 
967
 
class ColumnCollisionTest(_base.MappedTest):
 
684
class ColumnCollisionTest(fixtures.MappedTest):
968
685
    """Ensure the mapper doesn't break bind param naming rules on flush."""
969
686
 
970
687
    @classmethod
975
692
            Column('title', String(50))
976
693
        )
977
694
 
978
 
    @testing.resolve_artifact_names
979
695
    def test_naming(self):
980
 
        class Book(_base.ComparableEntity):
 
696
        book = self.tables.book
 
697
 
 
698
        class Book(fixtures.ComparableEntity):
981
699
            pass
982
700
 
983
701
        mapper(Book, book)
997
715
 
998
716
 
999
717
 
1000
 
class DefaultTest(_base.MappedTest):
 
718
class DefaultTest(fixtures.MappedTest):
1001
719
    """Exercise mappings on columns with DefaultGenerators.
1002
720
 
1003
721
    Tests that when saving objects whose table contains DefaultGenerators,
1020
738
            hohoval = 9
1021
739
            althohoval = 15
1022
740
 
1023
 
        cls.other_artifacts['hohoval'] = hohoval
1024
 
        cls.other_artifacts['althohoval'] = althohoval
 
741
        cls.other['hohoval'] = hohoval
 
742
        cls.other['althohoval'] = althohoval
1025
743
 
1026
744
        dt = Table('default_t', metadata,
1027
745
            Column('id', Integer, primary_key=True,
1051
769
 
1052
770
    @classmethod
1053
771
    def setup_classes(cls):
1054
 
        class Hoho(_base.ComparableEntity):
 
772
        class Hoho(cls.Comparable):
1055
773
            pass
1056
 
        class Secondary(_base.ComparableEntity):
 
774
        class Secondary(cls.Comparable):
1057
775
            pass
1058
776
 
1059
777
    @testing.fails_on('firebird', 'Data type unknown on the parameter')
1060
 
    @testing.resolve_artifact_names
1061
778
    def test_insert(self):
 
779
        althohoval, hohoval, default_t, Hoho = (self.other.althohoval,
 
780
                                self.other.hohoval,
 
781
                                self.tables.default_t,
 
782
                                self.classes.Hoho)
 
783
 
1062
784
        mapper(Hoho, default_t)
1063
785
 
1064
786
        h1 = Hoho(hoho=althohoval)
1102
824
        eq_(h5.foober, 'im the new foober')
1103
825
 
1104
826
    @testing.fails_on('firebird', 'Data type unknown on the parameter')
1105
 
    @testing.resolve_artifact_names
1106
827
    def test_eager_defaults(self):
 
828
        hohoval, default_t, Hoho = (self.other.hohoval,
 
829
                                self.tables.default_t,
 
830
                                self.classes.Hoho)
 
831
 
1107
832
        mapper(Hoho, default_t, eager_defaults=True)
1108
833
 
1109
834
        h1 = Hoho()
1114
839
 
1115
840
        self.sql_count_(0, lambda: eq_(h1.hoho, hohoval))
1116
841
 
1117
 
    @testing.resolve_artifact_names
1118
842
    def test_insert_nopostfetch(self):
 
843
        default_t, Hoho = self.tables.default_t, self.classes.Hoho
 
844
 
1119
845
        # populates from the FetchValues explicitly so there is no
1120
846
        # "post-update"
1121
847
        mapper(Hoho, default_t)
1132
858
        self.sql_count_(0, go)
1133
859
 
1134
860
    @testing.fails_on('firebird', 'Data type unknown on the parameter')
1135
 
    @testing.resolve_artifact_names
1136
861
    def test_update(self):
 
862
        default_t, Hoho = self.tables.default_t, self.classes.Hoho
 
863
 
1137
864
        mapper(Hoho, default_t)
1138
865
 
1139
866
        h1 = Hoho()
1147
874
        eq_(h1.foober, 'im the update')
1148
875
 
1149
876
    @testing.fails_on('firebird', 'Data type unknown on the parameter')
1150
 
    @testing.resolve_artifact_names
1151
877
    def test_used_in_relationship(self):
1152
878
        """A server-side default can be used as the target of a foreign key"""
1153
879
 
 
880
        Hoho, hohoval, default_t, secondary_table, Secondary = (self.classes.Hoho,
 
881
                                self.other.hohoval,
 
882
                                self.tables.default_t,
 
883
                                self.tables.secondary_table,
 
884
                                self.classes.Secondary)
 
885
 
 
886
 
1154
887
        mapper(Hoho, default_t, properties={
1155
888
            'secondaries':relationship(Secondary, order_by=secondary_table.c.id)})
1156
889
        mapper(Secondary, secondary_table)
1180
913
                    Secondary(data='s1'),
1181
914
                    Secondary(data='s2')]))
1182
915
 
1183
 
class ColumnPropertyTest(_base.MappedTest):
 
916
class ColumnPropertyTest(fixtures.MappedTest):
1184
917
    @classmethod
1185
918
    def define_tables(cls, metadata):
1186
919
        Table('data', metadata, 
1196
929
 
1197
930
    @classmethod
1198
931
    def setup_mappers(cls):
1199
 
        class Data(_base.BasicEntity):
 
932
        class Data(cls.Basic):
1200
933
            pass
1201
934
 
1202
 
    @testing.resolve_artifact_names
1203
935
    def test_refreshes(self):
 
936
        Data, data = self.classes.Data, self.tables.data
 
937
 
1204
938
        mapper(Data, data, properties={
1205
939
            'aplusb':column_property(data.c.a + literal_column("' '") + data.c.b)
1206
940
        })
1207
941
        self._test()
1208
942
 
1209
 
    @testing.resolve_artifact_names
1210
943
    def test_refreshes_post_init(self):
 
944
        Data, data = self.classes.Data, self.tables.data
 
945
 
1211
946
        m = mapper(Data, data)
1212
947
        m.add_property('aplusb', column_property(data.c.a + literal_column("' '") + data.c.b))
1213
948
        self._test()
1214
949
 
1215
 
    @testing.resolve_artifact_names
1216
950
    def test_with_inheritance(self):
 
951
        subdata, data, Data = (self.tables.subdata,
 
952
                                self.tables.data,
 
953
                                self.classes.Data)
 
954
 
1217
955
        class SubData(Data):
1218
956
            pass
1219
957
        mapper(Data, data, properties={
1227
965
        sess.flush()
1228
966
        eq_(sd1.aplusb, "hello there")
1229
967
 
1230
 
    @testing.resolve_artifact_names
1231
968
    def _test(self):
 
969
        Data = self.classes.Data
 
970
 
1232
971
        sess = create_session()
1233
972
 
1234
973
        d1 = Data(a="hello", b="there")
1249
988
class OneToManyTest(_fixtures.FixtureTest):
1250
989
    run_inserts = None
1251
990
 
1252
 
    @testing.resolve_artifact_names
1253
991
    def test_one_to_many_1(self):
1254
992
        """Basic save of one to many."""
1255
993
 
 
994
        Address, addresses, users, User = (self.classes.Address,
 
995
                                self.tables.addresses,
 
996
                                self.tables.users,
 
997
                                self.classes.User)
 
998
 
 
999
 
1256
1000
        m = mapper(User, users, properties=dict(
1257
1001
            addresses = relationship(mapper(Address, addresses), lazy='select')
1258
1002
        ))
1289
1033
            [addressid, userid, 'somethingnew@foo.com'])
1290
1034
        self.assert_(u.id == userid and a2.id == addressid)
1291
1035
 
1292
 
    @testing.resolve_artifact_names
1293
1036
    def test_one_to_many_2(self):
1294
1037
        """Modifying the child items of an object."""
1295
1038
 
 
1039
        Address, addresses, users, User = (self.classes.Address,
 
1040
                                self.tables.addresses,
 
1041
                                self.tables.users,
 
1042
                                self.classes.User)
 
1043
 
 
1044
 
1296
1045
        m = mapper(User, users, properties=dict(
1297
1046
            addresses = relationship(mapper(Address, addresses), lazy='select')))
1298
1047
 
1332
1081
             "WHERE addresses.id = :addresses_id",
1333
1082
             {'user_id': u1.id, 'addresses_id': a3.id})])
1334
1083
 
1335
 
    @testing.resolve_artifact_names
1336
1084
    def test_child_move(self):
1337
1085
        """Moving a child from one parent to another, with a delete.
1338
1086
 
1341
1089
        module.
1342
1090
 
1343
1091
        """
 
1092
 
 
1093
        Address, addresses, users, User = (self.classes.Address,
 
1094
                                self.tables.addresses,
 
1095
                                self.tables.users,
 
1096
                                self.classes.User)
 
1097
 
1344
1098
        m = mapper(User, users, properties=dict(
1345
1099
            addresses = relationship(mapper(Address, addresses), lazy='select')))
1346
1100
 
1363
1117
        u2 = session.query(User).get(u2.id)
1364
1118
        eq_(len(u2.addresses), 1)
1365
1119
 
1366
 
    @testing.resolve_artifact_names
1367
1120
    def test_child_move_2(self):
 
1121
        Address, addresses, users, User = (self.classes.Address,
 
1122
                                self.tables.addresses,
 
1123
                                self.tables.users,
 
1124
                                self.classes.User)
 
1125
 
1368
1126
        m = mapper(User, users, properties=dict(
1369
1127
            addresses = relationship(mapper(Address, addresses), lazy='select')))
1370
1128
 
1386
1144
        u2 = session.query(User).get(u2.id)
1387
1145
        eq_(len(u2.addresses), 1)
1388
1146
 
1389
 
    @testing.resolve_artifact_names
1390
1147
    def test_o2m_delete_parent(self):
 
1148
        Address, addresses, users, User = (self.classes.Address,
 
1149
                                self.tables.addresses,
 
1150
                                self.tables.users,
 
1151
                                self.classes.User)
 
1152
 
1391
1153
        m = mapper(User, users, properties=dict(
1392
1154
            address = relationship(mapper(Address, addresses),
1393
1155
                               lazy='select',
1409
1171
        assert sa.orm.attributes.instance_state(a).key in session.identity_map
1410
1172
        assert sa.orm.attributes.instance_state(u).key not in session.identity_map
1411
1173
 
1412
 
    @testing.resolve_artifact_names
1413
1174
    def test_one_to_one(self):
 
1175
        Address, addresses, users, User = (self.classes.Address,
 
1176
                                self.tables.addresses,
 
1177
                                self.tables.users,
 
1178
                                self.classes.User)
 
1179
 
1414
1180
        m = mapper(User, users, properties=dict(
1415
1181
            address = relationship(mapper(Address, addresses),
1416
1182
                               lazy='select',
1429
1195
        u.address.email_address = 'imnew@foo.com'
1430
1196
        session.flush()
1431
1197
 
1432
 
    @testing.resolve_artifact_names
1433
1198
    def test_bidirectional(self):
 
1199
        users, Address, addresses, User = (self.tables.users,
 
1200
                                self.classes.Address,
 
1201
                                self.tables.addresses,
 
1202
                                self.classes.User)
 
1203
 
1434
1204
        m1 = mapper(User, users)
1435
1205
        m2 = mapper(Address, addresses, properties=dict(
1436
1206
            user = relationship(m1, lazy='joined', backref='addresses')))
1445
1215
        session.delete(u)
1446
1216
        session.flush()
1447
1217
 
1448
 
    @testing.resolve_artifact_names
1449
1218
    def test_double_relationship(self):
 
1219
        Address, addresses, users, User = (self.classes.Address,
 
1220
                                self.tables.addresses,
 
1221
                                self.tables.users,
 
1222
                                self.classes.User)
 
1223
 
1450
1224
        m2 = mapper(Address, addresses)
1451
1225
        m = mapper(User, users, properties={
1452
1226
            'boston_addresses' : relationship(m2, primaryjoin=
1469
1243
class SaveTest(_fixtures.FixtureTest):
1470
1244
    run_inserts = None
1471
1245
 
1472
 
    @testing.resolve_artifact_names
1473
1246
    def test_basic(self):
 
1247
        User, users = self.classes.User, self.tables.users
 
1248
 
1474
1249
        m = mapper(User, users)
1475
1250
 
1476
1251
        # save two users
1508
1283
        eq_(u2.id, userlist[1].id)
1509
1284
        eq_(userlist[1].name, 'savetester2')
1510
1285
 
1511
 
    @testing.resolve_artifact_names
1512
1286
    def test_synonym(self):
1513
 
        class SUser(_base.BasicEntity):
 
1287
        users = self.tables.users
 
1288
 
 
1289
        class SUser(fixtures.BasicEntity):
1514
1290
            def _get_name(self):
1515
1291
                return "User:" + self.name
1516
1292
            def _set_name(self, name):
1532
1308
        u = session.query(SUser).first()
1533
1309
        eq_(u.syn_name, 'User:some name:User')
1534
1310
 
1535
 
    @testing.resolve_artifact_names
1536
1311
    def test_lazyattr_commit(self):
1537
1312
        """Lazily loaded relationships.
1538
1313
 
1540
1315
        'passive' call on that list does not blow away its value
1541
1316
 
1542
1317
        """
 
1318
 
 
1319
        users, Address, addresses, User = (self.tables.users,
 
1320
                                self.classes.Address,
 
1321
                                self.tables.addresses,
 
1322
                                self.classes.User)
 
1323
 
1543
1324
        mapper(User, users, properties = {
1544
1325
            'addresses': relationship(mapper(Address, addresses))})
1545
1326
 
1559
1340
        session.flush()
1560
1341
        eq_(len(u.addresses), 4)
1561
1342
 
1562
 
    @testing.resolve_artifact_names
1563
1343
    def test_inherits(self):
 
1344
        """a user object that also has the users mailing address."""
 
1345
 
 
1346
        users, addresses, User = (self.tables.users,
 
1347
                                self.tables.addresses,
 
1348
                                self.classes.User)
 
1349
 
1564
1350
        m1 = mapper(User, users)
1565
1351
 
1566
1352
        class AddressUser(User):
1567
 
            """a user object that also has the users mailing address."""
1568
1353
            pass
1569
1354
 
1570
1355
        # define a mapper for AddressUser that inherits the User.mapper, and
1582
1367
        eq_(au.user_id, rt.user_id)
1583
1368
        eq_(rt.id, rt.id)
1584
1369
 
1585
 
    @testing.resolve_artifact_names
1586
1370
    def test_deferred(self):
1587
1371
        """Deferred column operations"""
1588
1372
 
 
1373
        orders, Order = self.tables.orders, self.classes.Order
 
1374
 
 
1375
 
1589
1376
        mapper(Order, orders, properties={
1590
1377
            'description': sa.orm.deferred(orders.c.description)})
1591
1378
 
1626
1413
    # why no support on oracle ?  because oracle doesn't save
1627
1414
    # "blank" strings; it saves a single space character.
1628
1415
    @testing.fails_on('oracle', 'FIXME: unknown')
1629
 
    @testing.resolve_artifact_names
1630
1416
    def test_dont_update_blanks(self):
 
1417
        User, users = self.classes.User, self.tables.users
 
1418
 
1631
1419
        mapper(User, users)
1632
1420
 
1633
1421
        u = User(name='')
1640
1428
        u.name = ''
1641
1429
        self.sql_count_(0, session.flush)
1642
1430
 
1643
 
    @testing.resolve_artifact_names
1644
1431
    def test_multi_table_selectable(self):
1645
1432
        """Mapped selectables that span tables.
1646
1433
 
1647
1434
        Also tests redefinition of the keynames for the column properties.
1648
1435
 
1649
1436
        """
 
1437
 
 
1438
        addresses, users, User = (self.tables.addresses,
 
1439
                                self.tables.users,
 
1440
                                self.classes.User)
 
1441
 
1650
1442
        usersaddresses = sa.join(users, addresses,
1651
1443
                                 users.c.id == addresses.c.user_id)
1652
1444
 
1684
1476
        u = session.query(User).get(id)
1685
1477
        assert u.name == 'imnew'
1686
1478
 
1687
 
    @testing.resolve_artifact_names
1688
1479
    def test_history_get(self):
1689
1480
        """The history lazy-fetches data when it wasn't otherwise loaded."""
 
1481
 
 
1482
        users, Address, addresses, User = (self.tables.users,
 
1483
                                self.classes.Address,
 
1484
                                self.tables.addresses,
 
1485
                                self.classes.User)
 
1486
 
1690
1487
        mapper(User, users, properties={
1691
1488
            'addresses':relationship(Address, cascade="all, delete-orphan")})
1692
1489
        mapper(Address, addresses)
1705
1502
        assert users.count().scalar() == 0
1706
1503
        assert addresses.count().scalar() == 0
1707
1504
 
1708
 
    @testing.resolve_artifact_names
1709
1505
    def test_batch_mode(self):
1710
1506
        """The 'batch=False' flag on mapper()"""
1711
1507
 
 
1508
        users, User = self.tables.users, self.classes.User
 
1509
 
 
1510
 
1712
1511
        names = []
1713
 
        class TestExtension(sa.orm.MapperExtension):
 
1512
        class Events(object):
1714
1513
            def before_insert(self, mapper, connection, instance):
1715
1514
                self.current_instance = instance
1716
1515
                names.append(instance.name)
1717
1516
            def after_insert(self, mapper, connection, instance):
1718
1517
                assert instance is self.current_instance
1719
1518
 
1720
 
        mapper(User, users, extension=TestExtension(), batch=False)
 
1519
        mapper(User, users, batch=False)
 
1520
 
 
1521
        evt = Events()
 
1522
        event.listen(User, "before_insert", evt.before_insert)
 
1523
        event.listen(User, "after_insert", evt.after_insert)
 
1524
 
1721
1525
        u1 = User(name='user1')
1722
1526
        u2 = User(name='user2')
1723
1527
 
1738
1542
 
1739
1543
        sa.orm.clear_mappers()
1740
1544
 
1741
 
        m = mapper(User, users, extension=TestExtension())
 
1545
        m = mapper(User, users)
 
1546
        evt = Events()
 
1547
        event.listen(User, "before_insert", evt.before_insert)
 
1548
        event.listen(User, "after_insert", evt.after_insert)
 
1549
 
1742
1550
        u1 = User(name='user1')
1743
1551
        u2 = User(name='user2')
1744
1552
        session.add_all((u1, u2))
1748
1556
class ManyToOneTest(_fixtures.FixtureTest):
1749
1557
    run_inserts = None
1750
1558
 
1751
 
    @testing.resolve_artifact_names
1752
1559
    def test_m2o_one_to_one(self):
 
1560
        users, Address, addresses, User = (self.tables.users,
 
1561
                                self.classes.Address,
 
1562
                                self.tables.addresses,
 
1563
                                self.classes.User)
 
1564
 
1753
1565
        # TODO: put assertion in here !!!
1754
1566
        m = mapper(Address, addresses, properties=dict(
1755
1567
            user = relationship(mapper(User, users), lazy='select', uselist=False)))
1800
1612
        eq_(l.first().values(),
1801
1613
            [a.user.id, 'asdf8d', a.id, a.user_id, 'theater@foo.com'])
1802
1614
 
1803
 
    @testing.resolve_artifact_names
1804
1615
    def test_many_to_one_1(self):
 
1616
        users, Address, addresses, User = (self.tables.users,
 
1617
                                self.classes.Address,
 
1618
                                self.tables.addresses,
 
1619
                                self.classes.User)
 
1620
 
1805
1621
        m = mapper(Address, addresses, properties=dict(
1806
1622
            user = relationship(mapper(User, users), lazy='select')))
1807
1623
 
1825
1641
        u1 = session.query(User).get(u1.id)
1826
1642
        assert a1.user is None
1827
1643
 
1828
 
    @testing.resolve_artifact_names
1829
1644
    def test_many_to_one_2(self):
 
1645
        users, Address, addresses, User = (self.tables.users,
 
1646
                                self.classes.Address,
 
1647
                                self.tables.addresses,
 
1648
                                self.classes.User)
 
1649
 
1830
1650
        m = mapper(Address, addresses, properties=dict(
1831
1651
            user = relationship(mapper(User, users), lazy='select')))
1832
1652
 
1856
1676
        assert a1.user is None
1857
1677
        assert a2.user is u1
1858
1678
 
1859
 
    @testing.resolve_artifact_names
1860
1679
    def test_many_to_one_3(self):
 
1680
        users, Address, addresses, User = (self.tables.users,
 
1681
                                self.classes.Address,
 
1682
                                self.tables.addresses,
 
1683
                                self.classes.User)
 
1684
 
1861
1685
        m = mapper(Address, addresses, properties=dict(
1862
1686
            user = relationship(mapper(User, users), lazy='select')))
1863
1687
 
1884
1708
        u2 = session.query(User).get(u2.id)
1885
1709
        assert a1.user is u2
1886
1710
 
1887
 
    @testing.resolve_artifact_names
1888
1711
    def test_bidirectional_no_load(self):
 
1712
        users, Address, addresses, User = (self.tables.users,
 
1713
                                self.classes.Address,
 
1714
                                self.tables.addresses,
 
1715
                                self.classes.User)
 
1716
 
1889
1717
        mapper(User, users, properties={
1890
1718
            'addresses':relationship(Address, backref='user', lazy='noload')})
1891
1719
        mapper(Address, addresses)
1912
1740
class ManyToManyTest(_fixtures.FixtureTest):
1913
1741
    run_inserts = None
1914
1742
 
1915
 
    @testing.resolve_artifact_names
1916
1743
    def test_many_to_many(self):
 
1744
        keywords, items, item_keywords, Keyword, Item = (self.tables.keywords,
 
1745
                                self.tables.items,
 
1746
                                self.tables.item_keywords,
 
1747
                                self.classes.Keyword,
 
1748
                                self.classes.Item)
 
1749
 
1917
1750
        mapper(Keyword, keywords)
1918
1751
 
1919
1752
        m = mapper(Item, items, properties=dict(
2015
1848
        session.delete(objects[3])
2016
1849
        session.flush()
2017
1850
 
2018
 
    @testing.resolve_artifact_names
2019
1851
    def test_many_to_many_remove(self):
2020
1852
        """Setting a collection to empty deletes many-to-many rows.
2021
1853
 
2023
1855
        history and allows the many-to-many rows to be deleted
2024
1856
 
2025
1857
        """
 
1858
 
 
1859
        keywords, items, item_keywords, Keyword, Item = (self.tables.keywords,
 
1860
                                self.tables.items,
 
1861
                                self.tables.item_keywords,
 
1862
                                self.classes.Keyword,
 
1863
                                self.classes.Item)
 
1864
 
2026
1865
        mapper(Keyword, keywords)
2027
1866
        mapper(Item, items, properties=dict(
2028
1867
            keywords = relationship(Keyword, item_keywords, lazy='joined'),
2043
1882
        session.flush()
2044
1883
        assert item_keywords.count().scalar() == 0
2045
1884
 
2046
 
    @testing.resolve_artifact_names
2047
1885
    def test_scalar(self):
2048
1886
        """sa.dependency won't delete an m2m relationship referencing None."""
2049
1887
 
 
1888
        keywords, items, item_keywords, Keyword, Item = (self.tables.keywords,
 
1889
                                self.tables.items,
 
1890
                                self.tables.item_keywords,
 
1891
                                self.classes.Keyword,
 
1892
                                self.classes.Item)
 
1893
 
 
1894
 
2050
1895
        mapper(Keyword, keywords)
2051
1896
 
2052
1897
        mapper(Item, items, properties=dict(
2059
1904
        session.delete(i)
2060
1905
        session.flush()
2061
1906
 
2062
 
    @testing.resolve_artifact_names
2063
1907
    def test_many_to_many_update(self):
2064
1908
        """Assorted history operations on a many to many"""
 
1909
 
 
1910
        keywords, items, item_keywords, Keyword, Item = (self.tables.keywords,
 
1911
                                self.tables.items,
 
1912
                                self.tables.item_keywords,
 
1913
                                self.classes.Keyword,
 
1914
                                self.classes.Item)
 
1915
 
2065
1916
        mapper(Keyword, keywords)
2066
1917
        mapper(Item, items, properties=dict(
2067
1918
            keywords=relationship(Keyword,
2089
1940
        item = session.query(Item).get(item.id)
2090
1941
        assert item.keywords == [k1, k2]
2091
1942
 
2092
 
    @testing.resolve_artifact_names
2093
1943
    def test_association(self):
2094
1944
        """Basic test of an association object"""
2095
1945
 
2096
 
        class IKAssociation(_base.ComparableEntity):
 
1946
        keywords, items, item_keywords, Keyword, Item = (self.tables.keywords,
 
1947
                                self.tables.items,
 
1948
                                self.tables.item_keywords,
 
1949
                                self.classes.Keyword,
 
1950
                                self.classes.Item)
 
1951
 
 
1952
 
 
1953
        class IKAssociation(fixtures.ComparableEntity):
2097
1954
            pass
2098
1955
 
2099
1956
        mapper(Keyword, keywords)
2144
2001
class SaveTest2(_fixtures.FixtureTest):
2145
2002
    run_inserts = None
2146
2003
 
2147
 
    @testing.resolve_artifact_names
2148
2004
    def test_m2o_nonmatch(self):
 
2005
        users, Address, addresses, User = (self.tables.users,
 
2006
                                self.classes.Address,
 
2007
                                self.tables.addresses,
 
2008
                                self.classes.User)
 
2009
 
2149
2010
        mapper(User, users)
2150
2011
        mapper(Address, addresses, properties=dict(
2151
2012
            user = relationship(User, lazy='select', uselist=False)))
2174
2035
             {'user_id': 2, 'email_address': 'a2'}),
2175
2036
        )
2176
2037
 
2177
 
class SaveTest3(_base.MappedTest):
 
2038
class SaveTest3(fixtures.MappedTest):
2178
2039
    @classmethod
2179
2040
    def define_tables(cls, metadata):
2180
2041
        Table('items', metadata,
2194
2055
 
2195
2056
    @classmethod
2196
2057
    def setup_classes(cls):
2197
 
        class Keyword(_base.BasicEntity):
 
2058
        class Keyword(cls.Basic):
2198
2059
            pass
2199
 
        class Item(_base.BasicEntity):
 
2060
        class Item(cls.Basic):
2200
2061
            pass
2201
2062
 
2202
 
    @testing.resolve_artifact_names
2203
2063
    def test_manytomany_xtracol_delete(self):
2204
2064
        """A many-to-many on a table that has an extra column can properly delete rows from the table without referencing the extra column"""
2205
2065
 
 
2066
        keywords, items, assoc, Keyword, Item = (self.tables.keywords,
 
2067
                                self.tables.items,
 
2068
                                self.tables.assoc,
 
2069
                                self.classes.Keyword,
 
2070
                                self.classes.Item)
 
2071
 
 
2072
 
2206
2073
        mapper(Keyword, keywords)
2207
2074
        mapper(Item, items, properties=dict(
2208
2075
                keywords = relationship(Keyword, secondary=assoc, lazy='joined'),))
2223
2090
        session.flush()
2224
2091
        assert assoc.count().scalar() == 0
2225
2092
 
2226
 
class BooleanColTest(_base.MappedTest):
 
2093
class BooleanColTest(fixtures.MappedTest):
2227
2094
    @classmethod
2228
2095
    def define_tables(cls, metadata):
2229
2096
        Table('t1_t', metadata,
2231
2098
            Column('name', String(30)),
2232
2099
            Column('value', sa.Boolean))
2233
2100
 
2234
 
    @testing.resolve_artifact_names
2235
2101
    def test_boolean(self):
 
2102
        t1_t = self.tables.t1_t
 
2103
 
2236
2104
        # use the regular mapper
2237
 
        class T(_base.ComparableEntity):
 
2105
        class T(fixtures.ComparableEntity):
2238
2106
            pass
2239
2107
        orm_mapper(T, t1_t, order_by=t1_t.c.id)
2240
2108
 
2265
2133
        sess.flush()
2266
2134
        eq_(sess.query(T).filter(T.value==True).all(), [T(value=True, name="t1"),T(value=True, name="t3")])
2267
2135
 
2268
 
class DontAllowFlushOnLoadingObjectTest(_base.MappedTest):
2269
 
    """Test that objects with NULL identity keys aren't permitted to complete a flush.
2270
 
 
2271
 
    User-defined callables that execute during a load may modify state
2272
 
    on instances which results in their being autoflushed, before attributes
2273
 
    are populated.  If the primary key identifiers are missing, an explicit assertion
2274
 
    is needed to check that the object doesn't go through the flush process with
2275
 
    no net changes and gets placed in the identity map with an incorrect 
2276
 
    identity key.
2277
 
 
2278
 
    """
2279
 
    @classmethod
2280
 
    def define_tables(cls, metadata):
2281
 
        t1 = Table('t1', metadata,
2282
 
            Column('id', Integer, primary_key=True),
2283
 
            Column('data', String(30)),
2284
 
        )
2285
 
 
2286
 
    @testing.resolve_artifact_names
2287
 
    def test_flush_raises(self):
2288
 
        class T1(_base.ComparableEntity):
2289
 
            @reconstructor
2290
 
            def go(self):
2291
 
                # blow away 'id', no change event.
2292
 
                # this simulates a callable occurring
2293
 
                # before 'id' was even populated, i.e. a callable
2294
 
                # within an attribute_mapped_collection
2295
 
                self.__dict__.pop('id', None)
2296
 
 
2297
 
                # generate a change event, perhaps this occurs because
2298
 
                # someone wrote a broken attribute_mapped_collection that 
2299
 
                # inappropriately fires off change events when it should not,
2300
 
                # now we're dirty
2301
 
                self.data = 'foo bar'
2302
 
 
2303
 
                # blow away that change, so an UPDATE does not occur
2304
 
                # (since it would break)
2305
 
                self.__dict__.pop('data', None)
2306
 
 
2307
 
                # flush ! any lazyloader here would trigger
2308
 
                # autoflush, for example.
2309
 
                sess.flush()
2310
 
 
2311
 
        mapper(T1, t1)
2312
 
 
2313
 
        sess = Session()
2314
 
        sess.add(T1(data='test', id=5))
2315
 
        sess.commit()
2316
 
        sess.close()
2317
 
 
2318
 
        # make sure that invalid state doesn't get into the session
2319
 
        # with the wrong key.  If the identity key is not NULL, at least
2320
 
        # the population process would continue after the erroneous flush
2321
 
        # and thing would right themselves.
2322
 
        assert_raises_message(sa.orm.exc.FlushError,
2323
 
                        r"Instance \<T1 at .+?\> has a NULL "
2324
 
                        "identity key.  If this is an auto-generated value, "
2325
 
                        "check that the database table allows generation ",
2326
 
                      sess.query(T1).first)
2327
 
 
2328
 
 
2329
 
 
2330
 
class RowSwitchTest(_base.MappedTest):
 
2136
 
 
2137
class RowSwitchTest(fixtures.MappedTest):
2331
2138
    @classmethod
2332
2139
    def define_tables(cls, metadata):
2333
2140
        # parent
2353
2160
 
2354
2161
    @classmethod
2355
2162
    def setup_classes(cls):
2356
 
        class T5(_base.ComparableEntity):
2357
 
            pass
2358
 
 
2359
 
        class T6(_base.ComparableEntity):
2360
 
            pass
2361
 
 
2362
 
        class T7(_base.ComparableEntity):
2363
 
            pass
2364
 
 
2365
 
    @testing.resolve_artifact_names
 
2163
        class T5(cls.Comparable):
 
2164
            pass
 
2165
 
 
2166
        class T6(cls.Comparable):
 
2167
            pass
 
2168
 
 
2169
        class T7(cls.Comparable):
 
2170
            pass
 
2171
 
2366
2172
    def test_onetomany(self):
 
2173
        t6, T6, t5, T5 = (self.tables.t6,
 
2174
                                self.classes.T6,
 
2175
                                self.tables.t5,
 
2176
                                self.classes.T5)
 
2177
 
2367
2178
        mapper(T5, t5, properties={
2368
2179
            't6s':relationship(T6, cascade="all, delete-orphan")
2369
2180
        })
2404
2215
            [(3, 'third t6', 1), (4, 'fourth t6', 1)]
2405
2216
        )
2406
2217
 
2407
 
    @testing.resolve_artifact_names
2408
2218
    def test_manytomany(self):
 
2219
        t7, t5, t5t7, T5, T7 = (self.tables.t7,
 
2220
                                self.tables.t5,
 
2221
                                self.tables.t5t7,
 
2222
                                self.classes.T5,
 
2223
                                self.classes.T7)
 
2224
 
2409
2225
        mapper(T5, t5, properties={
2410
2226
            't7s':relationship(T7, secondary=t5t7, cascade="all")
2411
2227
        })
2440
2256
        assert list(sess.execute(t5.select(), mapper=T5)) == [(1, 'some other t5')]
2441
2257
        assert list(sess.execute(t7.select(), mapper=T5)) == [(3, 'third t7'), (4, 'fourth t7')]
2442
2258
 
2443
 
    @testing.resolve_artifact_names
2444
2259
    def test_manytoone(self):
 
2260
        t6, T6, t5, T5 = (self.tables.t6,
 
2261
                                self.classes.T6,
 
2262
                                self.tables.t5,
 
2263
                                self.classes.T5)
 
2264
 
2445
2265
 
2446
2266
        mapper(T6, t6, properties={
2447
2267
            't5':relationship(T5)
2468
2288
        assert list(sess.execute(t5.select(), mapper=T5)) == [(2, 'some other t5')]
2469
2289
        assert list(sess.execute(t6.select(), mapper=T5)) == [(1, 'some other t6', 2)]
2470
2290
 
2471
 
class InheritingRowSwitchTest(_base.MappedTest):
 
2291
class InheritingRowSwitchTest(fixtures.MappedTest):
2472
2292
    @classmethod
2473
2293
    def define_tables(cls, metadata):
2474
2294
        Table('parent', metadata,
2483
2303
 
2484
2304
    @classmethod
2485
2305
    def setup_classes(cls):
2486
 
        class P(_base.ComparableEntity):
 
2306
        class P(cls.Comparable):
2487
2307
            pass
2488
2308
 
2489
2309
        class C(P):
2490
2310
            pass
2491
2311
 
2492
 
    @testing.resolve_artifact_names
2493
2312
    def test_row_switch_no_child_table(self):
 
2313
        P, C, parent, child = (self.classes.P,
 
2314
                                self.classes.C,
 
2315
                                self.tables.parent,
 
2316
                                self.tables.child)
 
2317
 
2494
2318
        mapper(P, parent)
2495
2319
        mapper(C, child, inherits=P)
2496
2320
 
2519
2343
            )
2520
2344
        )
2521
2345
 
2522
 
 
2523
 
 
2524
 
class TransactionTest(_base.MappedTest):
 
2346
class TransactionTest(fixtures.MappedTest):
2525
2347
    __requires__ = ('deferrable_constraints',)
2526
2348
 
2527
2349
    __whitelist__ = ('sqlite',)
2541
2363
                   ))
2542
2364
    @classmethod
2543
2365
    def setup_classes(cls):
2544
 
        class T1(_base.ComparableEntity):
 
2366
        class T1(cls.Comparable):
2545
2367
            pass
2546
2368
 
2547
 
        class T2(_base.ComparableEntity):
 
2369
        class T2(cls.Comparable):
2548
2370
            pass
2549
2371
 
2550
2372
    @classmethod
2551
 
    @testing.resolve_artifact_names
2552
2373
    def setup_mappers(cls):
 
2374
        T2, T1, t2, t1 = (cls.classes.T2,
 
2375
                                cls.classes.T1,
 
2376
                                cls.tables.t2,
 
2377
                                cls.tables.t1)
 
2378
 
2553
2379
        orm_mapper(T1, t1)
2554
2380
        orm_mapper(T2, t2)
2555
2381
 
2556
 
    @testing.resolve_artifact_names
2557
2382
    def test_close_transaction_on_commit_fail(self):
 
2383
        T2, t1 = self.classes.T2, self.tables.t1
 
2384
 
2558
2385
        session = create_session(autocommit=True)
2559
2386
 
2560
2387
        # with a deferred constraint, this fails at COMMIT time instead
2574
2401
        if testing.against('postgresql'):
2575
2402
            t1.bind.engine.dispose()
2576
2403
 
 
2404
class PartialNullPKTest(fixtures.MappedTest):
 
2405
    # sqlite totally fine with NULLs in pk columns.
 
2406
    # no other DB is like this.
 
2407
    __only_on__ = ('sqlite',)
 
2408
 
 
2409
    @classmethod
 
2410
    def define_tables(cls, metadata):
 
2411
        Table('t1', metadata,
 
2412
            Column('col1', String(10), primary_key=True, nullable=True),
 
2413
            Column('col2', String(10), primary_key=True, nullable=True),
 
2414
            Column('col3', String(50))
 
2415
            )
 
2416
 
 
2417
    @classmethod
 
2418
    def setup_classes(cls):
 
2419
        class T1(cls.Basic):
 
2420
            pass
 
2421
 
 
2422
    @classmethod
 
2423
    def setup_mappers(cls):
 
2424
        orm_mapper(cls.classes.T1, cls.tables.t1)
 
2425
 
 
2426
    def test_key_switch(self):
 
2427
        T1 = self.classes.T1
 
2428
        s = Session()
 
2429
        s.add(T1(col1="1", col2=None))
 
2430
 
 
2431
        t1 = s.query(T1).first()
 
2432
        t1.col2 = 5
 
2433
        assert_raises_message(
 
2434
            sa.exc.FlushError,
 
2435
            "Can't update table using NULL for primary key value",
 
2436
            s.commit
 
2437
        )
 
2438
 
 
2439
    def test_plain_update(self):
 
2440
        T1 = self.classes.T1
 
2441
        s = Session()
 
2442
        s.add(T1(col1="1", col2=None))
 
2443
 
 
2444
        t1 = s.query(T1).first()
 
2445
        t1.col3 = 'hi'
 
2446
        assert_raises_message(
 
2447
            sa.exc.FlushError,
 
2448
            "Can't update table using NULL for primary key value",
 
2449
            s.commit
 
2450
        )
 
2451
 
 
2452
    def test_delete(self):
 
2453
        T1 = self.classes.T1
 
2454
        s = Session()
 
2455
        s.add(T1(col1="1", col2=None))
 
2456
 
 
2457
        t1 = s.query(T1).first()
 
2458
        s.delete(t1)
 
2459
        assert_raises_message(
 
2460
            sa.exc.FlushError,
 
2461
            "Can't delete from table using NULL for primary key value",
 
2462
            s.commit
 
2463
        )
 
2464
 
 
2465
    def test_total_null(self):
 
2466
        T1 = self.classes.T1
 
2467
        s = Session()
 
2468
        s.add(T1(col1=None, col2=None))
 
2469
        assert_raises_message(
 
2470
            sa.exc.FlushError,
 
2471
            r"Instance \<T1 at .+?\> has a NULL "
 
2472
            "identity key.  If this is an auto-generated value, "
 
2473
            "check that the database table allows generation ",
 
2474
            s.commit
 
2475
        )