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

« back to all changes in this revision

Viewing changes to test/ext/declarative/test_mixin.py

  • Committer: Package Import Robot
  • Author(s): Piotr Ożarowski, Jakub Wilk, Piotr Ożarowski
  • Date: 2013-07-06 20:53:52 UTC
  • mfrom: (1.4.23) (16.1.17 experimental)
  • Revision ID: package-import@ubuntu.com-20130706205352-ryppl1eto3illd79
Tags: 0.8.2-1
[ Jakub Wilk ]
* Use canonical URIs for Vcs-* fields.

[ Piotr Ożarowski ]
* New upstream release
* Upload to unstable
* Build depend on python3-all instead of -dev, extensions are not built for
  Python 3.X 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from sqlalchemy.testing import eq_, assert_raises, \
 
2
    assert_raises_message, is_
 
3
from sqlalchemy.ext import declarative as decl
 
4
import sqlalchemy as sa
 
5
from sqlalchemy import testing
 
6
from sqlalchemy import Integer, String, ForeignKey
 
7
from sqlalchemy.testing.schema import Table, Column
 
8
from sqlalchemy.orm import relationship, create_session, class_mapper, \
 
9
    configure_mappers, clear_mappers, \
 
10
    deferred, column_property, \
 
11
    Session
 
12
from sqlalchemy.util import classproperty
 
13
from sqlalchemy.ext.declarative import declared_attr
 
14
from sqlalchemy.testing import fixtures
 
15
 
 
16
Base = None
 
17
 
 
18
class DeclarativeTestBase(fixtures.TestBase, testing.AssertsExecutionResults):
 
19
    def setup(self):
 
20
        global Base
 
21
        Base = decl.declarative_base(testing.db)
 
22
 
 
23
    def teardown(self):
 
24
        Session.close_all()
 
25
        clear_mappers()
 
26
        Base.metadata.drop_all()
 
27
 
 
28
class DeclarativeMixinTest(DeclarativeTestBase):
 
29
 
 
30
    def test_simple(self):
 
31
 
 
32
        class MyMixin(object):
 
33
 
 
34
            id = Column(Integer, primary_key=True,
 
35
                        test_needs_autoincrement=True)
 
36
 
 
37
            def foo(self):
 
38
                return 'bar' + str(self.id)
 
39
 
 
40
        class MyModel(Base, MyMixin):
 
41
 
 
42
            __tablename__ = 'test'
 
43
            name = Column(String(100), nullable=False, index=True)
 
44
 
 
45
        Base.metadata.create_all()
 
46
        session = create_session()
 
47
        session.add(MyModel(name='testing'))
 
48
        session.flush()
 
49
        session.expunge_all()
 
50
        obj = session.query(MyModel).one()
 
51
        eq_(obj.id, 1)
 
52
        eq_(obj.name, 'testing')
 
53
        eq_(obj.foo(), 'bar1')
 
54
 
 
55
    def test_unique_column(self):
 
56
 
 
57
        class MyMixin(object):
 
58
 
 
59
            id = Column(Integer, primary_key=True)
 
60
            value = Column(String, unique=True)
 
61
 
 
62
        class MyModel(Base, MyMixin):
 
63
 
 
64
            __tablename__ = 'test'
 
65
 
 
66
        assert MyModel.__table__.c.value.unique
 
67
 
 
68
    def test_hierarchical_bases(self):
 
69
 
 
70
        class MyMixinParent:
 
71
 
 
72
            id = Column(Integer, primary_key=True,
 
73
                        test_needs_autoincrement=True)
 
74
 
 
75
            def foo(self):
 
76
                return 'bar' + str(self.id)
 
77
 
 
78
        class MyMixin(MyMixinParent):
 
79
 
 
80
            baz = Column(String(100), nullable=False, index=True)
 
81
 
 
82
        class MyModel(Base, MyMixin):
 
83
 
 
84
            __tablename__ = 'test'
 
85
            name = Column(String(100), nullable=False, index=True)
 
86
 
 
87
        Base.metadata.create_all()
 
88
        session = create_session()
 
89
        session.add(MyModel(name='testing', baz='fu'))
 
90
        session.flush()
 
91
        session.expunge_all()
 
92
        obj = session.query(MyModel).one()
 
93
        eq_(obj.id, 1)
 
94
        eq_(obj.name, 'testing')
 
95
        eq_(obj.foo(), 'bar1')
 
96
        eq_(obj.baz, 'fu')
 
97
 
 
98
    def test_mixin_overrides(self):
 
99
        """test a mixin that overrides a column on a superclass."""
 
100
 
 
101
        class MixinA(object):
 
102
            foo = Column(String(50))
 
103
 
 
104
        class MixinB(MixinA):
 
105
            foo = Column(Integer)
 
106
 
 
107
        class MyModelA(Base, MixinA):
 
108
            __tablename__ = 'testa'
 
109
            id = Column(Integer, primary_key=True)
 
110
 
 
111
        class MyModelB(Base, MixinB):
 
112
            __tablename__ = 'testb'
 
113
            id = Column(Integer, primary_key=True)
 
114
 
 
115
        eq_(MyModelA.__table__.c.foo.type.__class__, String)
 
116
        eq_(MyModelB.__table__.c.foo.type.__class__, Integer)
 
117
 
 
118
 
 
119
    def test_not_allowed(self):
 
120
 
 
121
        class MyMixin:
 
122
            foo = Column(Integer, ForeignKey('bar.id'))
 
123
 
 
124
        def go():
 
125
            class MyModel(Base, MyMixin):
 
126
                __tablename__ = 'foo'
 
127
 
 
128
        assert_raises(sa.exc.InvalidRequestError, go)
 
129
 
 
130
        class MyRelMixin:
 
131
            foo = relationship('Bar')
 
132
 
 
133
        def go():
 
134
            class MyModel(Base, MyRelMixin):
 
135
 
 
136
                __tablename__ = 'foo'
 
137
 
 
138
        assert_raises(sa.exc.InvalidRequestError, go)
 
139
 
 
140
        class MyDefMixin:
 
141
            foo = deferred(Column('foo', String))
 
142
 
 
143
        def go():
 
144
            class MyModel(Base, MyDefMixin):
 
145
                __tablename__ = 'foo'
 
146
 
 
147
        assert_raises(sa.exc.InvalidRequestError, go)
 
148
 
 
149
        class MyCPropMixin:
 
150
            foo = column_property(Column('foo', String))
 
151
 
 
152
        def go():
 
153
            class MyModel(Base, MyCPropMixin):
 
154
                __tablename__ = 'foo'
 
155
 
 
156
        assert_raises(sa.exc.InvalidRequestError, go)
 
157
 
 
158
    def test_table_name_inherited(self):
 
159
 
 
160
        class MyMixin:
 
161
            @declared_attr
 
162
            def __tablename__(cls):
 
163
                return cls.__name__.lower()
 
164
            id = Column(Integer, primary_key=True)
 
165
 
 
166
        class MyModel(Base, MyMixin):
 
167
            pass
 
168
 
 
169
        eq_(MyModel.__table__.name, 'mymodel')
 
170
 
 
171
    def test_classproperty_still_works(self):
 
172
        class MyMixin(object):
 
173
            @classproperty
 
174
            def __tablename__(cls):
 
175
                return cls.__name__.lower()
 
176
            id = Column(Integer, primary_key=True)
 
177
 
 
178
        class MyModel(Base, MyMixin):
 
179
            __tablename__ = 'overridden'
 
180
 
 
181
        eq_(MyModel.__table__.name, 'overridden')
 
182
 
 
183
    def test_table_name_not_inherited(self):
 
184
 
 
185
        class MyMixin:
 
186
            @declared_attr
 
187
            def __tablename__(cls):
 
188
                return cls.__name__.lower()
 
189
            id = Column(Integer, primary_key=True)
 
190
 
 
191
        class MyModel(Base, MyMixin):
 
192
            __tablename__ = 'overridden'
 
193
 
 
194
        eq_(MyModel.__table__.name, 'overridden')
 
195
 
 
196
    def test_table_name_inheritance_order(self):
 
197
 
 
198
        class MyMixin1:
 
199
            @declared_attr
 
200
            def __tablename__(cls):
 
201
                return cls.__name__.lower() + '1'
 
202
 
 
203
        class MyMixin2:
 
204
            @declared_attr
 
205
            def __tablename__(cls):
 
206
                return cls.__name__.lower() + '2'
 
207
 
 
208
        class MyModel(Base, MyMixin1, MyMixin2):
 
209
            id = Column(Integer, primary_key=True)
 
210
 
 
211
        eq_(MyModel.__table__.name, 'mymodel1')
 
212
 
 
213
    def test_table_name_dependent_on_subclass(self):
 
214
 
 
215
        class MyHistoryMixin:
 
216
            @declared_attr
 
217
            def __tablename__(cls):
 
218
                return cls.parent_name + '_changelog'
 
219
 
 
220
        class MyModel(Base, MyHistoryMixin):
 
221
            parent_name = 'foo'
 
222
            id = Column(Integer, primary_key=True)
 
223
 
 
224
        eq_(MyModel.__table__.name, 'foo_changelog')
 
225
 
 
226
    def test_table_args_inherited(self):
 
227
 
 
228
        class MyMixin:
 
229
            __table_args__ = {'mysql_engine': 'InnoDB'}
 
230
 
 
231
        class MyModel(Base, MyMixin):
 
232
            __tablename__ = 'test'
 
233
            id = Column(Integer, primary_key=True)
 
234
 
 
235
        eq_(MyModel.__table__.kwargs, {'mysql_engine': 'InnoDB'})
 
236
 
 
237
    def test_table_args_inherited_descriptor(self):
 
238
 
 
239
        class MyMixin:
 
240
            @declared_attr
 
241
            def __table_args__(cls):
 
242
                return {'info': cls.__name__}
 
243
 
 
244
        class MyModel(Base, MyMixin):
 
245
            __tablename__ = 'test'
 
246
            id = Column(Integer, primary_key=True)
 
247
 
 
248
        eq_(MyModel.__table__.info, 'MyModel')
 
249
 
 
250
    def test_table_args_inherited_single_table_inheritance(self):
 
251
 
 
252
        class MyMixin:
 
253
            __table_args__ = {'mysql_engine': 'InnoDB'}
 
254
 
 
255
        class General(Base, MyMixin):
 
256
            __tablename__ = 'test'
 
257
            id = Column(Integer, primary_key=True)
 
258
            type_ = Column(String(50))
 
259
            __mapper__args = {'polymorphic_on': type_}
 
260
 
 
261
        class Specific(General):
 
262
            __mapper_args__ = {'polymorphic_identity': 'specific'}
 
263
 
 
264
        assert Specific.__table__ is General.__table__
 
265
        eq_(General.__table__.kwargs, {'mysql_engine': 'InnoDB'})
 
266
 
 
267
    def test_columns_single_table_inheritance(self):
 
268
        """Test a column on a mixin with an alternate attribute name,
 
269
        mapped to a superclass and single-table inheritance subclass.
 
270
        The superclass table gets the column, the subclass shares
 
271
        the MapperProperty.
 
272
 
 
273
        """
 
274
 
 
275
        class MyMixin(object):
 
276
            foo = Column('foo', Integer)
 
277
            bar = Column('bar_newname', Integer)
 
278
 
 
279
        class General(Base, MyMixin):
 
280
            __tablename__ = 'test'
 
281
            id = Column(Integer, primary_key=True)
 
282
            type_ = Column(String(50))
 
283
            __mapper__args = {'polymorphic_on': type_}
 
284
 
 
285
        class Specific(General):
 
286
            __mapper_args__ = {'polymorphic_identity': 'specific'}
 
287
 
 
288
        assert General.bar.prop.columns[0] is General.__table__.c.bar_newname
 
289
        assert len(General.bar.prop.columns) == 1
 
290
        assert Specific.bar.prop is General.bar.prop
 
291
 
 
292
    @testing.skip_if(lambda: testing.against('oracle'),
 
293
                    "Test has an empty insert in it at the moment")
 
294
    def test_columns_single_inheritance_conflict_resolution(self):
 
295
        """Test that a declared_attr can return the existing column and it will
 
296
        be ignored.  this allows conditional columns to be added.
 
297
 
 
298
        See [ticket:2472].
 
299
 
 
300
        """
 
301
        class Person(Base):
 
302
            __tablename__ = 'person'
 
303
            id = Column(Integer, primary_key=True)
 
304
 
 
305
        class Mixin(object):
 
306
            @declared_attr
 
307
            def target_id(cls):
 
308
                return cls.__table__.c.get('target_id',
 
309
                        Column(Integer, ForeignKey('other.id'))
 
310
                    )
 
311
 
 
312
            @declared_attr
 
313
            def target(cls):
 
314
                return relationship("Other")
 
315
 
 
316
        class Engineer(Mixin, Person):
 
317
            """single table inheritance"""
 
318
 
 
319
        class Manager(Mixin, Person):
 
320
            """single table inheritance"""
 
321
 
 
322
        class Other(Base):
 
323
            __tablename__ = 'other'
 
324
            id = Column(Integer, primary_key=True)
 
325
 
 
326
        is_(
 
327
            Engineer.target_id.property.columns[0],
 
328
            Person.__table__.c.target_id
 
329
        )
 
330
        is_(
 
331
            Manager.target_id.property.columns[0],
 
332
            Person.__table__.c.target_id
 
333
        )
 
334
        # do a brief round trip on this
 
335
        Base.metadata.create_all()
 
336
        session = Session()
 
337
        o1, o2 = Other(), Other()
 
338
        session.add_all([
 
339
            Engineer(target=o1),
 
340
            Manager(target=o2),
 
341
            Manager(target=o1)
 
342
            ])
 
343
        session.commit()
 
344
        eq_(session.query(Engineer).first().target, o1)
 
345
 
 
346
 
 
347
    def test_columns_joined_table_inheritance(self):
 
348
        """Test a column on a mixin with an alternate attribute name,
 
349
        mapped to a superclass and joined-table inheritance subclass.
 
350
        Both tables get the column, in the case of the subclass the two
 
351
        columns are joined under one MapperProperty.
 
352
 
 
353
        """
 
354
 
 
355
        class MyMixin(object):
 
356
            foo = Column('foo', Integer)
 
357
            bar = Column('bar_newname', Integer)
 
358
 
 
359
        class General(Base, MyMixin):
 
360
            __tablename__ = 'test'
 
361
            id = Column(Integer, primary_key=True)
 
362
            type_ = Column(String(50))
 
363
            __mapper__args = {'polymorphic_on': type_}
 
364
 
 
365
        class Specific(General):
 
366
            __tablename__ = 'sub'
 
367
            id = Column(Integer, ForeignKey('test.id'), primary_key=True)
 
368
            __mapper_args__ = {'polymorphic_identity': 'specific'}
 
369
 
 
370
        assert General.bar.prop.columns[0] is General.__table__.c.bar_newname
 
371
        assert len(General.bar.prop.columns) == 1
 
372
        assert Specific.bar.prop is General.bar.prop
 
373
        eq_(len(Specific.bar.prop.columns), 1)
 
374
        assert Specific.bar.prop.columns[0] is General.__table__.c.bar_newname
 
375
 
 
376
    def test_column_join_checks_superclass_type(self):
 
377
        """Test that the logic which joins subclass props to those
 
378
        of the superclass checks that the superclass property is a column.
 
379
 
 
380
        """
 
381
 
 
382
        class General(Base):
 
383
            __tablename__ = 'test'
 
384
            id = Column(Integer, primary_key=True)
 
385
            general_id = Column(Integer, ForeignKey('test.id'))
 
386
            type_ = relationship("General")
 
387
 
 
388
        class Specific(General):
 
389
            __tablename__ = 'sub'
 
390
            id = Column(Integer, ForeignKey('test.id'), primary_key=True)
 
391
            type_ = Column('foob', String(50))
 
392
 
 
393
        assert isinstance(General.type_.property, sa.orm.RelationshipProperty)
 
394
        assert Specific.type_.property.columns[0] is Specific.__table__.c.foob
 
395
 
 
396
    def test_column_join_checks_subclass_type(self):
 
397
        """Test that the logic which joins subclass props to those
 
398
        of the superclass checks that the subclass property is a column.
 
399
 
 
400
        """
 
401
 
 
402
        def go():
 
403
            class General(Base):
 
404
                __tablename__ = 'test'
 
405
                id = Column(Integer, primary_key=True)
 
406
                type_ = Column('foob', Integer)
 
407
 
 
408
            class Specific(General):
 
409
                __tablename__ = 'sub'
 
410
                id = Column(Integer, ForeignKey('test.id'), primary_key=True)
 
411
                specific_id = Column(Integer, ForeignKey('sub.id'))
 
412
                type_ = relationship("Specific")
 
413
        assert_raises_message(
 
414
            sa.exc.ArgumentError, "column 'foob' conflicts with property", go
 
415
        )
 
416
 
 
417
    def test_table_args_overridden(self):
 
418
 
 
419
        class MyMixin:
 
420
            __table_args__ = {'mysql_engine': 'Foo'}
 
421
 
 
422
        class MyModel(Base, MyMixin):
 
423
            __tablename__ = 'test'
 
424
            __table_args__ = {'mysql_engine': 'InnoDB'}
 
425
            id = Column(Integer, primary_key=True)
 
426
 
 
427
        eq_(MyModel.__table__.kwargs, {'mysql_engine': 'InnoDB'})
 
428
 
 
429
    def test_mapper_args_declared_attr(self):
 
430
 
 
431
        class ComputedMapperArgs:
 
432
            @declared_attr
 
433
            def __mapper_args__(cls):
 
434
                if cls.__name__ == 'Person':
 
435
                    return {'polymorphic_on': cls.discriminator}
 
436
                else:
 
437
                    return {'polymorphic_identity': cls.__name__}
 
438
 
 
439
        class Person(Base, ComputedMapperArgs):
 
440
            __tablename__ = 'people'
 
441
            id = Column(Integer, primary_key=True)
 
442
            discriminator = Column('type', String(50))
 
443
 
 
444
        class Engineer(Person):
 
445
            pass
 
446
 
 
447
        configure_mappers()
 
448
        assert class_mapper(Person).polymorphic_on \
 
449
            is Person.__table__.c.type
 
450
        eq_(class_mapper(Engineer).polymorphic_identity, 'Engineer')
 
451
 
 
452
    def test_mapper_args_declared_attr_two(self):
 
453
 
 
454
        # same as test_mapper_args_declared_attr, but we repeat
 
455
        # ComputedMapperArgs on both classes for no apparent reason.
 
456
 
 
457
        class ComputedMapperArgs:
 
458
            @declared_attr
 
459
            def __mapper_args__(cls):
 
460
                if cls.__name__ == 'Person':
 
461
                    return {'polymorphic_on': cls.discriminator}
 
462
                else:
 
463
                    return {'polymorphic_identity': cls.__name__}
 
464
 
 
465
        class Person(Base, ComputedMapperArgs):
 
466
 
 
467
            __tablename__ = 'people'
 
468
            id = Column(Integer, primary_key=True)
 
469
            discriminator = Column('type', String(50))
 
470
 
 
471
        class Engineer(Person, ComputedMapperArgs):
 
472
            pass
 
473
 
 
474
        configure_mappers()
 
475
        assert class_mapper(Person).polymorphic_on \
 
476
            is Person.__table__.c.type
 
477
        eq_(class_mapper(Engineer).polymorphic_identity, 'Engineer')
 
478
 
 
479
    def test_table_args_composite(self):
 
480
 
 
481
        class MyMixin1:
 
482
 
 
483
            __table_args__ = {'info': {'baz': 'bob'}}
 
484
 
 
485
        class MyMixin2:
 
486
 
 
487
            __table_args__ = {'info': {'foo': 'bar'}}
 
488
 
 
489
        class MyModel(Base, MyMixin1, MyMixin2):
 
490
 
 
491
            __tablename__ = 'test'
 
492
 
 
493
            @declared_attr
 
494
            def __table_args__(self):
 
495
                info = {}
 
496
                args = dict(info=info)
 
497
                info.update(MyMixin1.__table_args__['info'])
 
498
                info.update(MyMixin2.__table_args__['info'])
 
499
                return args
 
500
            id = Column(Integer, primary_key=True)
 
501
 
 
502
        eq_(MyModel.__table__.info, {'foo': 'bar', 'baz': 'bob'})
 
503
 
 
504
    def test_mapper_args_inherited(self):
 
505
 
 
506
        class MyMixin:
 
507
 
 
508
            __mapper_args__ = {'always_refresh': True}
 
509
 
 
510
        class MyModel(Base, MyMixin):
 
511
 
 
512
            __tablename__ = 'test'
 
513
            id = Column(Integer, primary_key=True)
 
514
 
 
515
        eq_(MyModel.__mapper__.always_refresh, True)
 
516
 
 
517
    def test_mapper_args_inherited_descriptor(self):
 
518
 
 
519
        class MyMixin:
 
520
 
 
521
            @declared_attr
 
522
            def __mapper_args__(cls):
 
523
 
 
524
                # tenuous, but illustrates the problem!
 
525
 
 
526
                if cls.__name__ == 'MyModel':
 
527
                    return dict(always_refresh=True)
 
528
                else:
 
529
                    return dict(always_refresh=False)
 
530
 
 
531
        class MyModel(Base, MyMixin):
 
532
 
 
533
            __tablename__ = 'test'
 
534
            id = Column(Integer, primary_key=True)
 
535
 
 
536
        eq_(MyModel.__mapper__.always_refresh, True)
 
537
 
 
538
    def test_mapper_args_polymorphic_on_inherited(self):
 
539
 
 
540
        class MyMixin:
 
541
 
 
542
            type_ = Column(String(50))
 
543
            __mapper_args__ = {'polymorphic_on': type_}
 
544
 
 
545
        class MyModel(Base, MyMixin):
 
546
 
 
547
            __tablename__ = 'test'
 
548
            id = Column(Integer, primary_key=True)
 
549
 
 
550
        col = MyModel.__mapper__.polymorphic_on
 
551
        eq_(col.name, 'type_')
 
552
        assert col.table is not None
 
553
 
 
554
    def test_mapper_args_overridden(self):
 
555
 
 
556
        class MyMixin:
 
557
 
 
558
            __mapper_args__ = dict(always_refresh=True)
 
559
 
 
560
        class MyModel(Base, MyMixin):
 
561
 
 
562
            __tablename__ = 'test'
 
563
            __mapper_args__ = dict(always_refresh=False)
 
564
            id = Column(Integer, primary_key=True)
 
565
 
 
566
        eq_(MyModel.__mapper__.always_refresh, False)
 
567
 
 
568
    def test_mapper_args_composite(self):
 
569
 
 
570
        class MyMixin1:
 
571
 
 
572
            type_ = Column(String(50))
 
573
            __mapper_args__ = {'polymorphic_on': type_}
 
574
 
 
575
        class MyMixin2:
 
576
 
 
577
            __mapper_args__ = {'always_refresh': True}
 
578
 
 
579
        class MyModel(Base, MyMixin1, MyMixin2):
 
580
 
 
581
            __tablename__ = 'test'
 
582
 
 
583
            @declared_attr
 
584
            def __mapper_args__(cls):
 
585
                args = {}
 
586
                args.update(MyMixin1.__mapper_args__)
 
587
                args.update(MyMixin2.__mapper_args__)
 
588
                if cls.__name__ != 'MyModel':
 
589
                    args.pop('polymorphic_on')
 
590
                    args['polymorphic_identity'] = cls.__name__
 
591
 
 
592
                return args
 
593
            id = Column(Integer, primary_key=True)
 
594
 
 
595
        class MySubModel(MyModel):
 
596
            pass
 
597
 
 
598
        eq_(
 
599
            MyModel.__mapper__.polymorphic_on.name,
 
600
            'type_'
 
601
        )
 
602
        assert MyModel.__mapper__.polymorphic_on.table is not None
 
603
        eq_(MyModel.__mapper__.always_refresh, True)
 
604
        eq_(MySubModel.__mapper__.always_refresh, True)
 
605
        eq_(MySubModel.__mapper__.polymorphic_identity, 'MySubModel')
 
606
 
 
607
    def test_mapper_args_property(self):
 
608
        class MyModel(Base):
 
609
 
 
610
            @declared_attr
 
611
            def __tablename__(cls):
 
612
                return cls.__name__.lower()
 
613
 
 
614
            @declared_attr
 
615
            def __table_args__(cls):
 
616
                return {'mysql_engine':'InnoDB'}
 
617
 
 
618
            @declared_attr
 
619
            def __mapper_args__(cls):
 
620
                args = {}
 
621
                args['polymorphic_identity'] = cls.__name__
 
622
                return args
 
623
            id = Column(Integer, primary_key=True)
 
624
 
 
625
        class MySubModel(MyModel):
 
626
            id = Column(Integer, ForeignKey('mymodel.id'), primary_key=True)
 
627
 
 
628
        class MySubModel2(MyModel):
 
629
            __tablename__ = 'sometable'
 
630
            id = Column(Integer, ForeignKey('mymodel.id'), primary_key=True)
 
631
 
 
632
        eq_(MyModel.__mapper__.polymorphic_identity, 'MyModel')
 
633
        eq_(MySubModel.__mapper__.polymorphic_identity, 'MySubModel')
 
634
        eq_(MyModel.__table__.kwargs['mysql_engine'], 'InnoDB')
 
635
        eq_(MySubModel.__table__.kwargs['mysql_engine'], 'InnoDB')
 
636
        eq_(MySubModel2.__table__.kwargs['mysql_engine'], 'InnoDB')
 
637
        eq_(MyModel.__table__.name, 'mymodel')
 
638
        eq_(MySubModel.__table__.name, 'mysubmodel')
 
639
 
 
640
    def test_mapper_args_custom_base(self):
 
641
        """test the @declared_attr approach from a custom base."""
 
642
 
 
643
        class Base(object):
 
644
            @declared_attr
 
645
            def __tablename__(cls):
 
646
                return cls.__name__.lower()
 
647
 
 
648
            @declared_attr
 
649
            def __table_args__(cls):
 
650
                return {'mysql_engine':'InnoDB'}
 
651
 
 
652
            @declared_attr
 
653
            def id(self):
 
654
                return Column(Integer, primary_key=True)
 
655
 
 
656
        Base = decl.declarative_base(cls=Base)
 
657
 
 
658
        class MyClass(Base):
 
659
            pass
 
660
 
 
661
        class MyOtherClass(Base):
 
662
            pass
 
663
 
 
664
        eq_(MyClass.__table__.kwargs['mysql_engine'], 'InnoDB')
 
665
        eq_(MyClass.__table__.name, 'myclass')
 
666
        eq_(MyOtherClass.__table__.name, 'myotherclass')
 
667
        assert MyClass.__table__.c.id.table is MyClass.__table__
 
668
        assert MyOtherClass.__table__.c.id.table is MyOtherClass.__table__
 
669
 
 
670
    def test_single_table_no_propagation(self):
 
671
 
 
672
        class IdColumn:
 
673
 
 
674
            id = Column(Integer, primary_key=True)
 
675
 
 
676
        class Generic(Base, IdColumn):
 
677
 
 
678
            __tablename__ = 'base'
 
679
            discriminator = Column('type', String(50))
 
680
            __mapper_args__ = dict(polymorphic_on=discriminator)
 
681
            value = Column(Integer())
 
682
 
 
683
        class Specific(Generic):
 
684
 
 
685
            __mapper_args__ = dict(polymorphic_identity='specific')
 
686
 
 
687
        assert Specific.__table__ is Generic.__table__
 
688
        eq_(Generic.__table__.c.keys(), ['id', 'type', 'value'])
 
689
        assert class_mapper(Specific).polymorphic_on \
 
690
            is Generic.__table__.c.type
 
691
        eq_(class_mapper(Specific).polymorphic_identity, 'specific')
 
692
 
 
693
    def test_joined_table_propagation(self):
 
694
 
 
695
        class CommonMixin:
 
696
 
 
697
            @declared_attr
 
698
            def __tablename__(cls):
 
699
                return cls.__name__.lower()
 
700
            __table_args__ = {'mysql_engine': 'InnoDB'}
 
701
            timestamp = Column(Integer)
 
702
            id = Column(Integer, primary_key=True)
 
703
 
 
704
        class Generic(Base, CommonMixin):
 
705
 
 
706
            discriminator = Column('python_type', String(50))
 
707
            __mapper_args__ = dict(polymorphic_on=discriminator)
 
708
 
 
709
        class Specific(Generic):
 
710
 
 
711
            __mapper_args__ = dict(polymorphic_identity='specific')
 
712
            id = Column(Integer, ForeignKey('generic.id'),
 
713
                        primary_key=True)
 
714
 
 
715
        eq_(Generic.__table__.name, 'generic')
 
716
        eq_(Specific.__table__.name, 'specific')
 
717
        eq_(Generic.__table__.c.keys(), ['timestamp', 'id',
 
718
            'python_type'])
 
719
        eq_(Specific.__table__.c.keys(), ['id'])
 
720
        eq_(Generic.__table__.kwargs, {'mysql_engine': 'InnoDB'})
 
721
        eq_(Specific.__table__.kwargs, {'mysql_engine': 'InnoDB'})
 
722
 
 
723
    def test_some_propagation(self):
 
724
 
 
725
        class CommonMixin:
 
726
 
 
727
            @declared_attr
 
728
            def __tablename__(cls):
 
729
                return cls.__name__.lower()
 
730
            __table_args__ = {'mysql_engine': 'InnoDB'}
 
731
            timestamp = Column(Integer)
 
732
 
 
733
        class BaseType(Base, CommonMixin):
 
734
 
 
735
            discriminator = Column('type', String(50))
 
736
            __mapper_args__ = dict(polymorphic_on=discriminator)
 
737
            id = Column(Integer, primary_key=True)
 
738
            value = Column(Integer())
 
739
 
 
740
        class Single(BaseType):
 
741
 
 
742
            __tablename__ = None
 
743
            __mapper_args__ = dict(polymorphic_identity='type1')
 
744
 
 
745
        class Joined(BaseType):
 
746
 
 
747
            __mapper_args__ = dict(polymorphic_identity='type2')
 
748
            id = Column(Integer, ForeignKey('basetype.id'),
 
749
                        primary_key=True)
 
750
 
 
751
        eq_(BaseType.__table__.name, 'basetype')
 
752
        eq_(BaseType.__table__.c.keys(), ['timestamp', 'type', 'id',
 
753
            'value'])
 
754
        eq_(BaseType.__table__.kwargs, {'mysql_engine': 'InnoDB'})
 
755
        assert Single.__table__ is BaseType.__table__
 
756
        eq_(Joined.__table__.name, 'joined')
 
757
        eq_(Joined.__table__.c.keys(), ['id'])
 
758
        eq_(Joined.__table__.kwargs, {'mysql_engine': 'InnoDB'})
 
759
 
 
760
    def test_col_copy_vs_declared_attr_joined_propagation(self):
 
761
        class Mixin(object):
 
762
            a = Column(Integer)
 
763
 
 
764
            @declared_attr
 
765
            def b(cls):
 
766
                return Column(Integer)
 
767
 
 
768
        class A(Mixin, Base):
 
769
            __tablename__ = 'a'
 
770
            id = Column(Integer, primary_key=True)
 
771
 
 
772
        class B(A):
 
773
            __tablename__ = 'b'
 
774
            id = Column(Integer, ForeignKey('a.id'), primary_key=True)
 
775
 
 
776
        assert 'a' in A.__table__.c
 
777
        assert 'b' in A.__table__.c
 
778
        assert 'a' not in B.__table__.c
 
779
        assert 'b' not in B.__table__.c
 
780
 
 
781
    def test_col_copy_vs_declared_attr_joined_propagation_newname(self):
 
782
        class Mixin(object):
 
783
            a = Column('a1', Integer)
 
784
 
 
785
            @declared_attr
 
786
            def b(cls):
 
787
                return Column('b1', Integer)
 
788
 
 
789
        class A(Mixin, Base):
 
790
            __tablename__ = 'a'
 
791
            id = Column(Integer, primary_key=True)
 
792
 
 
793
        class B(A):
 
794
            __tablename__ = 'b'
 
795
            id = Column(Integer, ForeignKey('a.id'), primary_key=True)
 
796
 
 
797
        assert 'a1' in A.__table__.c
 
798
        assert 'b1' in A.__table__.c
 
799
        assert 'a1' not in B.__table__.c
 
800
        assert 'b1' not in B.__table__.c
 
801
 
 
802
    def test_col_copy_vs_declared_attr_single_propagation(self):
 
803
        class Mixin(object):
 
804
            a = Column(Integer)
 
805
 
 
806
            @declared_attr
 
807
            def b(cls):
 
808
                return Column(Integer)
 
809
 
 
810
        class A(Mixin, Base):
 
811
            __tablename__ = 'a'
 
812
            id = Column(Integer, primary_key=True)
 
813
 
 
814
        class B(A):
 
815
            pass
 
816
 
 
817
        assert 'a' in A.__table__.c
 
818
        assert 'b' in A.__table__.c
 
819
 
 
820
    def test_non_propagating_mixin(self):
 
821
 
 
822
        class NoJoinedTableNameMixin:
 
823
 
 
824
            @declared_attr
 
825
            def __tablename__(cls):
 
826
                if decl.has_inherited_table(cls):
 
827
                    return None
 
828
                return cls.__name__.lower()
 
829
 
 
830
        class BaseType(Base, NoJoinedTableNameMixin):
 
831
 
 
832
            discriminator = Column('type', String(50))
 
833
            __mapper_args__ = dict(polymorphic_on=discriminator)
 
834
            id = Column(Integer, primary_key=True)
 
835
            value = Column(Integer())
 
836
 
 
837
        class Specific(BaseType):
 
838
 
 
839
            __mapper_args__ = dict(polymorphic_identity='specific')
 
840
 
 
841
        eq_(BaseType.__table__.name, 'basetype')
 
842
        eq_(BaseType.__table__.c.keys(), ['type', 'id', 'value'])
 
843
        assert Specific.__table__ is BaseType.__table__
 
844
        assert class_mapper(Specific).polymorphic_on \
 
845
            is BaseType.__table__.c.type
 
846
        eq_(class_mapper(Specific).polymorphic_identity, 'specific')
 
847
 
 
848
    def test_non_propagating_mixin_used_for_joined(self):
 
849
 
 
850
        class TableNameMixin:
 
851
 
 
852
            @declared_attr
 
853
            def __tablename__(cls):
 
854
                if decl.has_inherited_table(cls) and TableNameMixin \
 
855
                    not in cls.__bases__:
 
856
                    return None
 
857
                return cls.__name__.lower()
 
858
 
 
859
        class BaseType(Base, TableNameMixin):
 
860
 
 
861
            discriminator = Column('type', String(50))
 
862
            __mapper_args__ = dict(polymorphic_on=discriminator)
 
863
            id = Column(Integer, primary_key=True)
 
864
            value = Column(Integer())
 
865
 
 
866
        class Specific(BaseType, TableNameMixin):
 
867
 
 
868
            __mapper_args__ = dict(polymorphic_identity='specific')
 
869
            id = Column(Integer, ForeignKey('basetype.id'),
 
870
                        primary_key=True)
 
871
 
 
872
        eq_(BaseType.__table__.name, 'basetype')
 
873
        eq_(BaseType.__table__.c.keys(), ['type', 'id', 'value'])
 
874
        eq_(Specific.__table__.name, 'specific')
 
875
        eq_(Specific.__table__.c.keys(), ['id'])
 
876
 
 
877
    def test_single_back_propagate(self):
 
878
 
 
879
        class ColumnMixin:
 
880
 
 
881
            timestamp = Column(Integer)
 
882
 
 
883
        class BaseType(Base):
 
884
 
 
885
            __tablename__ = 'foo'
 
886
            discriminator = Column('type', String(50))
 
887
            __mapper_args__ = dict(polymorphic_on=discriminator)
 
888
            id = Column(Integer, primary_key=True)
 
889
 
 
890
        class Specific(BaseType, ColumnMixin):
 
891
 
 
892
            __mapper_args__ = dict(polymorphic_identity='specific')
 
893
 
 
894
        eq_(BaseType.__table__.c.keys(), ['type', 'id', 'timestamp'])
 
895
 
 
896
    def test_table_in_model_and_same_column_in_mixin(self):
 
897
 
 
898
        class ColumnMixin:
 
899
 
 
900
            data = Column(Integer)
 
901
 
 
902
        class Model(Base, ColumnMixin):
 
903
 
 
904
            __table__ = Table('foo', Base.metadata, Column('data',
 
905
                              Integer), Column('id', Integer,
 
906
                              primary_key=True))
 
907
 
 
908
        model_col = Model.__table__.c.data
 
909
        mixin_col = ColumnMixin.data
 
910
        assert model_col is not mixin_col
 
911
        eq_(model_col.name, 'data')
 
912
        assert model_col.type.__class__ is mixin_col.type.__class__
 
913
 
 
914
    def test_table_in_model_and_different_named_column_in_mixin(self):
 
915
 
 
916
        class ColumnMixin:
 
917
            tada = Column(Integer)
 
918
 
 
919
        def go():
 
920
 
 
921
            class Model(Base, ColumnMixin):
 
922
 
 
923
                __table__ = Table('foo', Base.metadata,
 
924
                                Column('data',Integer),
 
925
                                Column('id', Integer,primary_key=True))
 
926
                foo = relationship("Dest")
 
927
 
 
928
        assert_raises_message(sa.exc.ArgumentError,
 
929
                              "Can't add additional column 'tada' when "
 
930
                              "specifying __table__", go)
 
931
 
 
932
    def test_table_in_model_and_different_named_alt_key_column_in_mixin(self):
 
933
 
 
934
        # here, the __table__ has a column 'tada'.  We disallow
 
935
        # the add of the 'foobar' column, even though it's
 
936
        # keyed to 'tada'.
 
937
 
 
938
        class ColumnMixin:
 
939
            tada = Column('foobar', Integer)
 
940
 
 
941
        def go():
 
942
 
 
943
            class Model(Base, ColumnMixin):
 
944
 
 
945
                __table__ = Table('foo', Base.metadata,
 
946
                                Column('data',Integer),
 
947
                                Column('tada', Integer),
 
948
                                Column('id', Integer,primary_key=True))
 
949
                foo = relationship("Dest")
 
950
 
 
951
        assert_raises_message(sa.exc.ArgumentError,
 
952
                              "Can't add additional column 'foobar' when "
 
953
                              "specifying __table__", go)
 
954
 
 
955
    def test_table_in_model_overrides_different_typed_column_in_mixin(self):
 
956
 
 
957
        class ColumnMixin:
 
958
 
 
959
            data = Column(String)
 
960
 
 
961
        class Model(Base, ColumnMixin):
 
962
 
 
963
            __table__ = Table('foo', Base.metadata, Column('data',
 
964
                              Integer), Column('id', Integer,
 
965
                              primary_key=True))
 
966
 
 
967
        model_col = Model.__table__.c.data
 
968
        mixin_col = ColumnMixin.data
 
969
        assert model_col is not mixin_col
 
970
        eq_(model_col.name, 'data')
 
971
        assert model_col.type.__class__ is Integer
 
972
 
 
973
    def test_mixin_column_ordering(self):
 
974
 
 
975
        class Foo(object):
 
976
 
 
977
            col1 = Column(Integer)
 
978
            col3 = Column(Integer)
 
979
 
 
980
        class Bar(object):
 
981
 
 
982
            col2 = Column(Integer)
 
983
            col4 = Column(Integer)
 
984
 
 
985
        class Model(Base, Foo, Bar):
 
986
 
 
987
            id = Column(Integer, primary_key=True)
 
988
            __tablename__ = 'model'
 
989
 
 
990
        eq_(Model.__table__.c.keys(), ['col1', 'col3', 'col2', 'col4',
 
991
            'id'])
 
992
 
 
993
    def test_honor_class_mro_one(self):
 
994
        class HasXMixin(object):
 
995
            @declared_attr
 
996
            def x(self):
 
997
                return Column(Integer)
 
998
 
 
999
        class Parent(HasXMixin, Base):
 
1000
            __tablename__ = 'parent'
 
1001
            id = Column(Integer, primary_key=True)
 
1002
 
 
1003
        class Child(Parent):
 
1004
            __tablename__ = 'child'
 
1005
            id = Column(Integer, ForeignKey('parent.id'), primary_key=True)
 
1006
 
 
1007
        assert "x" not in Child.__table__.c
 
1008
 
 
1009
    def test_honor_class_mro_two(self):
 
1010
        class HasXMixin(object):
 
1011
            @declared_attr
 
1012
            def x(self):
 
1013
                return Column(Integer)
 
1014
 
 
1015
        class Parent(HasXMixin, Base):
 
1016
            __tablename__ = 'parent'
 
1017
            id = Column(Integer, primary_key=True)
 
1018
            def x(self):
 
1019
                return "hi"
 
1020
 
 
1021
        class C(Parent):
 
1022
            __tablename__ = 'c'
 
1023
            id = Column(Integer, ForeignKey('parent.id'), primary_key=True)
 
1024
 
 
1025
        assert C().x() == 'hi'
 
1026
 
 
1027
    def test_arbitrary_attrs_one(self):
 
1028
        class HasMixin(object):
 
1029
            @declared_attr
 
1030
            def some_attr(cls):
 
1031
                return cls.__name__ + "SOME ATTR"
 
1032
 
 
1033
        class Mapped(HasMixin, Base):
 
1034
            __tablename__ = 't'
 
1035
            id = Column(Integer, primary_key=True)
 
1036
 
 
1037
        eq_(Mapped.some_attr, "MappedSOME ATTR")
 
1038
        eq_(Mapped.__dict__['some_attr'], "MappedSOME ATTR")
 
1039
 
 
1040
    def test_arbitrary_attrs_two(self):
 
1041
        from sqlalchemy.ext.associationproxy import association_proxy
 
1042
 
 
1043
        class FilterA(Base):
 
1044
            __tablename__ = 'filter_a'
 
1045
            id = Column(Integer(), primary_key=True)
 
1046
            parent_id = Column(Integer(),
 
1047
                    ForeignKey('type_a.id'))
 
1048
            filter = Column(String())
 
1049
            def __init__(self, filter_, **kw):
 
1050
                self.filter = filter_
 
1051
 
 
1052
        class FilterB(Base):
 
1053
            __tablename__ = 'filter_b'
 
1054
            id = Column(Integer(), primary_key=True)
 
1055
            parent_id = Column(Integer(),
 
1056
                    ForeignKey('type_b.id'))
 
1057
            filter = Column(String())
 
1058
            def __init__(self, filter_, **kw):
 
1059
                self.filter = filter_
 
1060
 
 
1061
        class FilterMixin(object):
 
1062
            @declared_attr
 
1063
            def _filters(cls):
 
1064
                return relationship(cls.filter_class,
 
1065
                        cascade='all,delete,delete-orphan')
 
1066
 
 
1067
            @declared_attr
 
1068
            def filters(cls):
 
1069
                return association_proxy('_filters', 'filter')
 
1070
 
 
1071
        class TypeA(Base, FilterMixin):
 
1072
            __tablename__ = 'type_a'
 
1073
            filter_class = FilterA
 
1074
            id = Column(Integer(), primary_key=True)
 
1075
 
 
1076
        class TypeB(Base, FilterMixin):
 
1077
            __tablename__ = 'type_b'
 
1078
            filter_class = FilterB
 
1079
            id = Column(Integer(), primary_key=True)
 
1080
 
 
1081
        TypeA(filters=[u'foo'])
 
1082
        TypeB(filters=[u'foo'])
 
1083
 
 
1084
class DeclarativeMixinPropertyTest(DeclarativeTestBase):
 
1085
 
 
1086
    def test_column_property(self):
 
1087
 
 
1088
        class MyMixin(object):
 
1089
 
 
1090
            @declared_attr
 
1091
            def prop_hoho(cls):
 
1092
                return column_property(Column('prop', String(50)))
 
1093
 
 
1094
        class MyModel(Base, MyMixin):
 
1095
 
 
1096
            __tablename__ = 'test'
 
1097
            id = Column(Integer, primary_key=True,
 
1098
                        test_needs_autoincrement=True)
 
1099
 
 
1100
        class MyOtherModel(Base, MyMixin):
 
1101
 
 
1102
            __tablename__ = 'othertest'
 
1103
            id = Column(Integer, primary_key=True,
 
1104
                        test_needs_autoincrement=True)
 
1105
 
 
1106
        assert MyModel.__table__.c.prop is not None
 
1107
        assert MyOtherModel.__table__.c.prop is not None
 
1108
        assert MyModel.__table__.c.prop \
 
1109
            is not MyOtherModel.__table__.c.prop
 
1110
        assert MyModel.prop_hoho.property.columns \
 
1111
            == [MyModel.__table__.c.prop]
 
1112
        assert MyOtherModel.prop_hoho.property.columns \
 
1113
            == [MyOtherModel.__table__.c.prop]
 
1114
        assert MyModel.prop_hoho.property \
 
1115
            is not MyOtherModel.prop_hoho.property
 
1116
        Base.metadata.create_all()
 
1117
        sess = create_session()
 
1118
        m1, m2 = MyModel(prop_hoho='foo'), MyOtherModel(prop_hoho='bar')
 
1119
        sess.add_all([m1, m2])
 
1120
        sess.flush()
 
1121
        eq_(sess.query(MyModel).filter(MyModel.prop_hoho == 'foo'
 
1122
            ).one(), m1)
 
1123
        eq_(sess.query(MyOtherModel).filter(MyOtherModel.prop_hoho
 
1124
            == 'bar').one(), m2)
 
1125
 
 
1126
    def test_doc(self):
 
1127
        """test documentation transfer.
 
1128
 
 
1129
        the documentation situation with @declared_attr is problematic.
 
1130
        at least see if mapped subclasses get the doc.
 
1131
 
 
1132
        """
 
1133
 
 
1134
        class MyMixin(object):
 
1135
 
 
1136
            @declared_attr
 
1137
            def type_(cls):
 
1138
                """this is a document."""
 
1139
 
 
1140
                return Column(String(50))
 
1141
 
 
1142
            @declared_attr
 
1143
            def t2(cls):
 
1144
                """this is another document."""
 
1145
 
 
1146
                return column_property(Column(String(50)))
 
1147
 
 
1148
        class MyModel(Base, MyMixin):
 
1149
 
 
1150
            __tablename__ = 'test'
 
1151
            id = Column(Integer, primary_key=True)
 
1152
 
 
1153
        configure_mappers()
 
1154
        eq_(MyModel.type_.__doc__, """this is a document.""")
 
1155
        eq_(MyModel.t2.__doc__, """this is another document.""")
 
1156
 
 
1157
    def test_column_in_mapper_args(self):
 
1158
 
 
1159
        class MyMixin(object):
 
1160
 
 
1161
            @declared_attr
 
1162
            def type_(cls):
 
1163
                return Column(String(50))
 
1164
            __mapper_args__ = {'polymorphic_on': type_}
 
1165
 
 
1166
        class MyModel(Base, MyMixin):
 
1167
 
 
1168
            __tablename__ = 'test'
 
1169
            id = Column(Integer, primary_key=True)
 
1170
 
 
1171
        configure_mappers()
 
1172
        col = MyModel.__mapper__.polymorphic_on
 
1173
        eq_(col.name, 'type_')
 
1174
        assert col.table is not None
 
1175
 
 
1176
    def test_deferred(self):
 
1177
 
 
1178
        class MyMixin(object):
 
1179
 
 
1180
            @declared_attr
 
1181
            def data(cls):
 
1182
                return deferred(Column('data', String(50)))
 
1183
 
 
1184
        class MyModel(Base, MyMixin):
 
1185
 
 
1186
            __tablename__ = 'test'
 
1187
            id = Column(Integer, primary_key=True,
 
1188
                        test_needs_autoincrement=True)
 
1189
 
 
1190
        Base.metadata.create_all()
 
1191
        sess = create_session()
 
1192
        sess.add_all([MyModel(data='d1'), MyModel(data='d2')])
 
1193
        sess.flush()
 
1194
        sess.expunge_all()
 
1195
        d1, d2 = sess.query(MyModel).order_by(MyModel.data)
 
1196
        assert 'data' not in d1.__dict__
 
1197
        assert d1.data == 'd1'
 
1198
        assert 'data' in d1.__dict__
 
1199
 
 
1200
    def _test_relationship(self, usestring):
 
1201
 
 
1202
        class RefTargetMixin(object):
 
1203
 
 
1204
            @declared_attr
 
1205
            def target_id(cls):
 
1206
                return Column('target_id', ForeignKey('target.id'))
 
1207
            if usestring:
 
1208
 
 
1209
                @declared_attr
 
1210
                def target(cls):
 
1211
                    return relationship('Target',
 
1212
                            primaryjoin='Target.id==%s.target_id'
 
1213
                            % cls.__name__)
 
1214
            else:
 
1215
 
 
1216
                @declared_attr
 
1217
                def target(cls):
 
1218
                    return relationship('Target')
 
1219
 
 
1220
        class Foo(Base, RefTargetMixin):
 
1221
 
 
1222
            __tablename__ = 'foo'
 
1223
            id = Column(Integer, primary_key=True,
 
1224
                        test_needs_autoincrement=True)
 
1225
 
 
1226
        class Bar(Base, RefTargetMixin):
 
1227
 
 
1228
            __tablename__ = 'bar'
 
1229
            id = Column(Integer, primary_key=True,
 
1230
                        test_needs_autoincrement=True)
 
1231
 
 
1232
        class Target(Base):
 
1233
 
 
1234
            __tablename__ = 'target'
 
1235
            id = Column(Integer, primary_key=True,
 
1236
                        test_needs_autoincrement=True)
 
1237
 
 
1238
        Base.metadata.create_all()
 
1239
        sess = create_session()
 
1240
        t1, t2 = Target(), Target()
 
1241
        f1, f2, b1 = Foo(target=t1), Foo(target=t2), Bar(target=t1)
 
1242
        sess.add_all([f1, f2, b1])
 
1243
        sess.flush()
 
1244
        eq_(sess.query(Foo).filter(Foo.target == t2).one(), f2)
 
1245
        eq_(sess.query(Bar).filter(Bar.target == t2).first(), None)
 
1246
        sess.expire_all()
 
1247
        eq_(f1.target, t1)
 
1248
 
 
1249
    def test_relationship(self):
 
1250
        self._test_relationship(False)
 
1251
 
 
1252
    def test_relationship_primryjoin(self):
 
1253
        self._test_relationship(True)
 
1254