PageRenderTime 57ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/test/ext/declarative/test_inheritance.py

https://bitbucket.org/miracle2k/sqlalchemy
Python | 1159 lines | 913 code | 215 blank | 31 comment | 7 complexity | d014bead2425577d01aae4520491bb5e MD5 | raw file
  1. from test.lib.testing import eq_, assert_raises, \
  2. assert_raises_message, is_
  3. from sqlalchemy.ext import declarative as decl
  4. from sqlalchemy import exc
  5. import sqlalchemy as sa
  6. from test.lib import testing
  7. from sqlalchemy import MetaData, Integer, String, ForeignKey, \
  8. ForeignKeyConstraint, Index
  9. from test.lib.schema import Table, Column
  10. from sqlalchemy.orm import relationship, create_session, class_mapper, \
  11. joinedload, configure_mappers, backref, clear_mappers, \
  12. polymorphic_union, deferred, column_property, composite,\
  13. Session
  14. from test.lib.testing import eq_
  15. from sqlalchemy.util import classproperty
  16. from sqlalchemy.ext.declarative import declared_attr, AbstractConcreteBase, ConcreteBase
  17. from test.lib import fixtures
  18. Base = None
  19. class DeclarativeTestBase(fixtures.TestBase, testing.AssertsExecutionResults):
  20. def setup(self):
  21. global Base
  22. Base = decl.declarative_base(testing.db)
  23. def teardown(self):
  24. Session.close_all()
  25. clear_mappers()
  26. Base.metadata.drop_all()
  27. class DeclarativeInheritanceTest(DeclarativeTestBase):
  28. def test_we_must_copy_mapper_args(self):
  29. class Person(Base):
  30. __tablename__ = 'people'
  31. id = Column(Integer, primary_key=True)
  32. discriminator = Column('type', String(50))
  33. __mapper_args__ = {'polymorphic_on': discriminator,
  34. 'polymorphic_identity': 'person'}
  35. class Engineer(Person):
  36. primary_language = Column(String(50))
  37. assert 'inherits' not in Person.__mapper_args__
  38. assert class_mapper(Engineer).polymorphic_identity is None
  39. assert class_mapper(Engineer).polymorphic_on is Person.__table__.c.type
  40. def test_we_must_only_copy_column_mapper_args(self):
  41. class Person(Base):
  42. __tablename__ = 'people'
  43. id = Column(Integer, primary_key=True)
  44. a=Column(Integer)
  45. b=Column(Integer)
  46. c=Column(Integer)
  47. d=Column(Integer)
  48. discriminator = Column('type', String(50))
  49. __mapper_args__ = {'polymorphic_on': discriminator,
  50. 'polymorphic_identity': 'person',
  51. 'version_id_col': 'a',
  52. 'column_prefix': 'bar',
  53. 'include_properties': ['id', 'a', 'b'],
  54. }
  55. assert class_mapper(Person).version_id_col == 'a'
  56. assert class_mapper(Person).include_properties == set(['id', 'a', 'b'])
  57. def test_custom_join_condition(self):
  58. class Foo(Base):
  59. __tablename__ = 'foo'
  60. id = Column('id', Integer, primary_key=True)
  61. class Bar(Foo):
  62. __tablename__ = 'bar'
  63. id = Column('id', Integer, primary_key=True)
  64. foo_id = Column('foo_id', Integer)
  65. __mapper_args__ = {'inherit_condition': foo_id == Foo.id}
  66. # compile succeeds because inherit_condition is honored
  67. configure_mappers()
  68. def test_joined(self):
  69. class Company(Base, fixtures.ComparableEntity):
  70. __tablename__ = 'companies'
  71. id = Column('id', Integer, primary_key=True,
  72. test_needs_autoincrement=True)
  73. name = Column('name', String(50))
  74. employees = relationship('Person')
  75. class Person(Base, fixtures.ComparableEntity):
  76. __tablename__ = 'people'
  77. id = Column('id', Integer, primary_key=True,
  78. test_needs_autoincrement=True)
  79. company_id = Column('company_id', Integer,
  80. ForeignKey('companies.id'))
  81. name = Column('name', String(50))
  82. discriminator = Column('type', String(50))
  83. __mapper_args__ = {'polymorphic_on': discriminator}
  84. class Engineer(Person):
  85. __tablename__ = 'engineers'
  86. __mapper_args__ = {'polymorphic_identity': 'engineer'}
  87. id = Column('id', Integer, ForeignKey('people.id'),
  88. primary_key=True)
  89. primary_language = Column('primary_language', String(50))
  90. class Manager(Person):
  91. __tablename__ = 'managers'
  92. __mapper_args__ = {'polymorphic_identity': 'manager'}
  93. id = Column('id', Integer, ForeignKey('people.id'),
  94. primary_key=True)
  95. golf_swing = Column('golf_swing', String(50))
  96. Base.metadata.create_all()
  97. sess = create_session()
  98. c1 = Company(name='MegaCorp, Inc.',
  99. employees=[Engineer(name='dilbert',
  100. primary_language='java'), Engineer(name='wally',
  101. primary_language='c++'), Manager(name='dogbert',
  102. golf_swing='fore!')])
  103. c2 = Company(name='Elbonia, Inc.',
  104. employees=[Engineer(name='vlad',
  105. primary_language='cobol')])
  106. sess.add(c1)
  107. sess.add(c2)
  108. sess.flush()
  109. sess.expunge_all()
  110. eq_(sess.query(Company).filter(Company.employees.of_type(Engineer).
  111. any(Engineer.primary_language
  112. == 'cobol')).first(), c2)
  113. # ensure that the Manager mapper was compiled with the Manager id
  114. # column as higher priority. this ensures that "Manager.id"
  115. # is appropriately treated as the "id" column in the "manager"
  116. # table (reversed from 0.6's behavior.)
  117. assert Manager.id.property.columns == [Manager.__table__.c.id, Person.__table__.c.id]
  118. # assert that the "id" column is available without a second
  119. # load. as of 0.7, the ColumnProperty tests all columns
  120. # in it's list to see which is present in the row.
  121. sess.expunge_all()
  122. def go():
  123. assert sess.query(Manager).filter(Manager.name == 'dogbert'
  124. ).one().id
  125. self.assert_sql_count(testing.db, go, 1)
  126. sess.expunge_all()
  127. def go():
  128. assert sess.query(Person).filter(Manager.name == 'dogbert'
  129. ).one().id
  130. self.assert_sql_count(testing.db, go, 1)
  131. def test_add_subcol_after_the_fact(self):
  132. class Person(Base, fixtures.ComparableEntity):
  133. __tablename__ = 'people'
  134. id = Column('id', Integer, primary_key=True,
  135. test_needs_autoincrement=True)
  136. name = Column('name', String(50))
  137. discriminator = Column('type', String(50))
  138. __mapper_args__ = {'polymorphic_on': discriminator}
  139. class Engineer(Person):
  140. __tablename__ = 'engineers'
  141. __mapper_args__ = {'polymorphic_identity': 'engineer'}
  142. id = Column('id', Integer, ForeignKey('people.id'),
  143. primary_key=True)
  144. Engineer.primary_language = Column('primary_language',
  145. String(50))
  146. Base.metadata.create_all()
  147. sess = create_session()
  148. e1 = Engineer(primary_language='java', name='dilbert')
  149. sess.add(e1)
  150. sess.flush()
  151. sess.expunge_all()
  152. eq_(sess.query(Person).first(), Engineer(primary_language='java'
  153. , name='dilbert'))
  154. def test_add_parentcol_after_the_fact(self):
  155. class Person(Base, fixtures.ComparableEntity):
  156. __tablename__ = 'people'
  157. id = Column('id', Integer, primary_key=True,
  158. test_needs_autoincrement=True)
  159. discriminator = Column('type', String(50))
  160. __mapper_args__ = {'polymorphic_on': discriminator}
  161. class Engineer(Person):
  162. __tablename__ = 'engineers'
  163. __mapper_args__ = {'polymorphic_identity': 'engineer'}
  164. primary_language = Column(String(50))
  165. id = Column('id', Integer, ForeignKey('people.id'),
  166. primary_key=True)
  167. Person.name = Column('name', String(50))
  168. Base.metadata.create_all()
  169. sess = create_session()
  170. e1 = Engineer(primary_language='java', name='dilbert')
  171. sess.add(e1)
  172. sess.flush()
  173. sess.expunge_all()
  174. eq_(sess.query(Person).first(),
  175. Engineer(primary_language='java', name='dilbert'))
  176. def test_add_sub_parentcol_after_the_fact(self):
  177. class Person(Base, fixtures.ComparableEntity):
  178. __tablename__ = 'people'
  179. id = Column('id', Integer, primary_key=True,
  180. test_needs_autoincrement=True)
  181. discriminator = Column('type', String(50))
  182. __mapper_args__ = {'polymorphic_on': discriminator}
  183. class Engineer(Person):
  184. __tablename__ = 'engineers'
  185. __mapper_args__ = {'polymorphic_identity': 'engineer'}
  186. primary_language = Column(String(50))
  187. id = Column('id', Integer, ForeignKey('people.id'),
  188. primary_key=True)
  189. class Admin(Engineer):
  190. __tablename__ = 'admins'
  191. __mapper_args__ = {'polymorphic_identity': 'admin'}
  192. workstation = Column(String(50))
  193. id = Column('id', Integer, ForeignKey('engineers.id'),
  194. primary_key=True)
  195. Person.name = Column('name', String(50))
  196. Base.metadata.create_all()
  197. sess = create_session()
  198. e1 = Admin(primary_language='java', name='dilbert',
  199. workstation='foo')
  200. sess.add(e1)
  201. sess.flush()
  202. sess.expunge_all()
  203. eq_(sess.query(Person).first(), Admin(primary_language='java',
  204. name='dilbert', workstation='foo'))
  205. def test_subclass_mixin(self):
  206. class Person(Base, fixtures.ComparableEntity):
  207. __tablename__ = 'people'
  208. id = Column('id', Integer, primary_key=True)
  209. name = Column('name', String(50))
  210. discriminator = Column('type', String(50))
  211. __mapper_args__ = {'polymorphic_on': discriminator}
  212. class MyMixin(object):
  213. pass
  214. class Engineer(MyMixin, Person):
  215. __tablename__ = 'engineers'
  216. __mapper_args__ = {'polymorphic_identity': 'engineer'}
  217. id = Column('id', Integer, ForeignKey('people.id'),
  218. primary_key=True)
  219. primary_language = Column('primary_language', String(50))
  220. assert class_mapper(Engineer).inherits is class_mapper(Person)
  221. def test_with_undefined_foreignkey(self):
  222. class Parent(Base):
  223. __tablename__ = 'parent'
  224. id = Column('id', Integer, primary_key=True)
  225. tp = Column('type', String(50))
  226. __mapper_args__ = dict(polymorphic_on=tp)
  227. class Child1(Parent):
  228. __tablename__ = 'child1'
  229. id = Column('id', Integer, ForeignKey('parent.id'),
  230. primary_key=True)
  231. related_child2 = Column('c2', Integer,
  232. ForeignKey('child2.id'))
  233. __mapper_args__ = dict(polymorphic_identity='child1')
  234. # no exception is raised by the ForeignKey to "child2" even
  235. # though child2 doesn't exist yet
  236. class Child2(Parent):
  237. __tablename__ = 'child2'
  238. id = Column('id', Integer, ForeignKey('parent.id'),
  239. primary_key=True)
  240. related_child1 = Column('c1', Integer)
  241. __mapper_args__ = dict(polymorphic_identity='child2')
  242. sa.orm.configure_mappers() # no exceptions here
  243. def test_foreign_keys_with_col(self):
  244. """Test that foreign keys that reference a literal 'id' subclass
  245. 'id' attribute behave intuitively.
  246. See [ticket:1892].
  247. """
  248. class Booking(Base):
  249. __tablename__ = 'booking'
  250. id = Column(Integer, primary_key=True)
  251. class PlanBooking(Booking):
  252. __tablename__ = 'plan_booking'
  253. id = Column(Integer, ForeignKey(Booking.id),
  254. primary_key=True)
  255. # referencing PlanBooking.id gives us the column
  256. # on plan_booking, not booking
  257. class FeatureBooking(Booking):
  258. __tablename__ = 'feature_booking'
  259. id = Column(Integer, ForeignKey(Booking.id),
  260. primary_key=True)
  261. plan_booking_id = Column(Integer,
  262. ForeignKey(PlanBooking.id))
  263. plan_booking = relationship(PlanBooking,
  264. backref='feature_bookings')
  265. assert FeatureBooking.__table__.c.plan_booking_id.\
  266. references(PlanBooking.__table__.c.id)
  267. assert FeatureBooking.__table__.c.id.\
  268. references(Booking.__table__.c.id)
  269. def test_single_colsonbase(self):
  270. """test single inheritance where all the columns are on the base
  271. class."""
  272. class Company(Base, fixtures.ComparableEntity):
  273. __tablename__ = 'companies'
  274. id = Column('id', Integer, primary_key=True,
  275. test_needs_autoincrement=True)
  276. name = Column('name', String(50))
  277. employees = relationship('Person')
  278. class Person(Base, fixtures.ComparableEntity):
  279. __tablename__ = 'people'
  280. id = Column('id', Integer, primary_key=True,
  281. test_needs_autoincrement=True)
  282. company_id = Column('company_id', Integer,
  283. ForeignKey('companies.id'))
  284. name = Column('name', String(50))
  285. discriminator = Column('type', String(50))
  286. primary_language = Column('primary_language', String(50))
  287. golf_swing = Column('golf_swing', String(50))
  288. __mapper_args__ = {'polymorphic_on': discriminator}
  289. class Engineer(Person):
  290. __mapper_args__ = {'polymorphic_identity': 'engineer'}
  291. class Manager(Person):
  292. __mapper_args__ = {'polymorphic_identity': 'manager'}
  293. Base.metadata.create_all()
  294. sess = create_session()
  295. c1 = Company(name='MegaCorp, Inc.',
  296. employees=[Engineer(name='dilbert',
  297. primary_language='java'), Engineer(name='wally',
  298. primary_language='c++'), Manager(name='dogbert',
  299. golf_swing='fore!')])
  300. c2 = Company(name='Elbonia, Inc.',
  301. employees=[Engineer(name='vlad',
  302. primary_language='cobol')])
  303. sess.add(c1)
  304. sess.add(c2)
  305. sess.flush()
  306. sess.expunge_all()
  307. eq_(sess.query(Person).filter(Engineer.primary_language
  308. == 'cobol').first(), Engineer(name='vlad'))
  309. eq_(sess.query(Company).filter(Company.employees.of_type(Engineer).
  310. any(Engineer.primary_language
  311. == 'cobol')).first(), c2)
  312. def test_single_colsonsub(self):
  313. """test single inheritance where the columns are local to their
  314. class.
  315. this is a newer usage.
  316. """
  317. class Company(Base, fixtures.ComparableEntity):
  318. __tablename__ = 'companies'
  319. id = Column('id', Integer, primary_key=True,
  320. test_needs_autoincrement=True)
  321. name = Column('name', String(50))
  322. employees = relationship('Person')
  323. class Person(Base, fixtures.ComparableEntity):
  324. __tablename__ = 'people'
  325. id = Column(Integer, primary_key=True,
  326. test_needs_autoincrement=True)
  327. company_id = Column(Integer, ForeignKey('companies.id'))
  328. name = Column(String(50))
  329. discriminator = Column('type', String(50))
  330. __mapper_args__ = {'polymorphic_on': discriminator}
  331. class Engineer(Person):
  332. __mapper_args__ = {'polymorphic_identity': 'engineer'}
  333. primary_language = Column(String(50))
  334. class Manager(Person):
  335. __mapper_args__ = {'polymorphic_identity': 'manager'}
  336. golf_swing = Column(String(50))
  337. # we have here a situation that is somewhat unique. the Person
  338. # class is mapped to the "people" table, but it was mapped when
  339. # the table did not include the "primary_language" or
  340. # "golf_swing" columns. declarative will also manipulate the
  341. # exclude_properties collection so that sibling classes don't
  342. # cross-pollinate.
  343. assert Person.__table__.c.company_id is not None
  344. assert Person.__table__.c.golf_swing is not None
  345. assert Person.__table__.c.primary_language is not None
  346. assert Engineer.primary_language is not None
  347. assert Manager.golf_swing is not None
  348. assert not hasattr(Person, 'primary_language')
  349. assert not hasattr(Person, 'golf_swing')
  350. assert not hasattr(Engineer, 'golf_swing')
  351. assert not hasattr(Manager, 'primary_language')
  352. Base.metadata.create_all()
  353. sess = create_session()
  354. e1 = Engineer(name='dilbert', primary_language='java')
  355. e2 = Engineer(name='wally', primary_language='c++')
  356. m1 = Manager(name='dogbert', golf_swing='fore!')
  357. c1 = Company(name='MegaCorp, Inc.', employees=[e1, e2, m1])
  358. e3 = Engineer(name='vlad', primary_language='cobol')
  359. c2 = Company(name='Elbonia, Inc.', employees=[e3])
  360. sess.add(c1)
  361. sess.add(c2)
  362. sess.flush()
  363. sess.expunge_all()
  364. eq_(sess.query(Person).filter(Engineer.primary_language
  365. == 'cobol').first(), Engineer(name='vlad'))
  366. eq_(sess.query(Company).filter(Company.employees.of_type(Engineer).
  367. any(Engineer.primary_language
  368. == 'cobol')).first(), c2)
  369. eq_(sess.query(Engineer).filter_by(primary_language='cobol'
  370. ).one(), Engineer(name='vlad', primary_language='cobol'))
  371. def test_columns_single_inheritance_conflict_resolution(self):
  372. """Test that a declared_attr can return the existing column and it will
  373. be ignored. this allows conditional columns to be added.
  374. See [ticket:2472].
  375. """
  376. class Person(Base):
  377. __tablename__ = 'person'
  378. id = Column(Integer, primary_key=True)
  379. class Engineer(Person):
  380. """single table inheritance"""
  381. @declared_attr
  382. def target_id(cls):
  383. return cls.__table__.c.get('target_id',
  384. Column(Integer, ForeignKey('other.id'))
  385. )
  386. @declared_attr
  387. def target(cls):
  388. return relationship("Other")
  389. class Manager(Person):
  390. """single table inheritance"""
  391. @declared_attr
  392. def target_id(cls):
  393. return cls.__table__.c.get('target_id',
  394. Column(Integer, ForeignKey('other.id'))
  395. )
  396. @declared_attr
  397. def target(cls):
  398. return relationship("Other")
  399. class Other(Base):
  400. __tablename__ = 'other'
  401. id = Column(Integer, primary_key=True)
  402. is_(
  403. Engineer.target_id.property.columns[0],
  404. Person.__table__.c.target_id
  405. )
  406. is_(
  407. Manager.target_id.property.columns[0],
  408. Person.__table__.c.target_id
  409. )
  410. # do a brief round trip on this
  411. Base.metadata.create_all()
  412. session = Session()
  413. o1, o2 = Other(), Other()
  414. session.add_all([
  415. Engineer(target=o1),
  416. Manager(target=o2),
  417. Manager(target=o1)
  418. ])
  419. session.commit()
  420. eq_(session.query(Engineer).first().target, o1)
  421. def test_joined_from_single(self):
  422. class Company(Base, fixtures.ComparableEntity):
  423. __tablename__ = 'companies'
  424. id = Column('id', Integer, primary_key=True,
  425. test_needs_autoincrement=True)
  426. name = Column('name', String(50))
  427. employees = relationship('Person')
  428. class Person(Base, fixtures.ComparableEntity):
  429. __tablename__ = 'people'
  430. id = Column(Integer, primary_key=True,
  431. test_needs_autoincrement=True)
  432. company_id = Column(Integer, ForeignKey('companies.id'))
  433. name = Column(String(50))
  434. discriminator = Column('type', String(50))
  435. __mapper_args__ = {'polymorphic_on': discriminator}
  436. class Manager(Person):
  437. __mapper_args__ = {'polymorphic_identity': 'manager'}
  438. golf_swing = Column(String(50))
  439. class Engineer(Person):
  440. __tablename__ = 'engineers'
  441. __mapper_args__ = {'polymorphic_identity': 'engineer'}
  442. id = Column(Integer, ForeignKey('people.id'),
  443. primary_key=True)
  444. primary_language = Column(String(50))
  445. assert Person.__table__.c.golf_swing is not None
  446. assert not Person.__table__.c.has_key('primary_language')
  447. assert Engineer.__table__.c.primary_language is not None
  448. assert Engineer.primary_language is not None
  449. assert Manager.golf_swing is not None
  450. assert not hasattr(Person, 'primary_language')
  451. assert not hasattr(Person, 'golf_swing')
  452. assert not hasattr(Engineer, 'golf_swing')
  453. assert not hasattr(Manager, 'primary_language')
  454. Base.metadata.create_all()
  455. sess = create_session()
  456. e1 = Engineer(name='dilbert', primary_language='java')
  457. e2 = Engineer(name='wally', primary_language='c++')
  458. m1 = Manager(name='dogbert', golf_swing='fore!')
  459. c1 = Company(name='MegaCorp, Inc.', employees=[e1, e2, m1])
  460. e3 = Engineer(name='vlad', primary_language='cobol')
  461. c2 = Company(name='Elbonia, Inc.', employees=[e3])
  462. sess.add(c1)
  463. sess.add(c2)
  464. sess.flush()
  465. sess.expunge_all()
  466. eq_(sess.query(Person).with_polymorphic(Engineer).
  467. filter(Engineer.primary_language
  468. == 'cobol').first(), Engineer(name='vlad'))
  469. eq_(sess.query(Company).filter(Company.employees.of_type(Engineer).
  470. any(Engineer.primary_language
  471. == 'cobol')).first(), c2)
  472. eq_(sess.query(Engineer).filter_by(primary_language='cobol'
  473. ).one(), Engineer(name='vlad', primary_language='cobol'))
  474. def test_single_from_joined_colsonsub(self):
  475. class Person(Base, fixtures.ComparableEntity):
  476. __tablename__ = 'people'
  477. id = Column(Integer, primary_key=True,
  478. test_needs_autoincrement=True)
  479. name = Column(String(50))
  480. discriminator = Column('type', String(50))
  481. __mapper_args__ = {'polymorphic_on': discriminator}
  482. class Manager(Person):
  483. __tablename__ = 'manager'
  484. __mapper_args__ = {'polymorphic_identity': 'manager'}
  485. id = Column(Integer, ForeignKey('people.id'), primary_key=True)
  486. golf_swing = Column(String(50))
  487. class Boss(Manager):
  488. boss_name = Column(String(50))
  489. is_(
  490. Boss.__mapper__.column_attrs['boss_name'].columns[0],
  491. Manager.__table__.c.boss_name
  492. )
  493. def test_polymorphic_on_converted_from_inst(self):
  494. class A(Base):
  495. __tablename__ = 'A'
  496. id = Column(Integer, primary_key=True)
  497. discriminator = Column(String)
  498. @declared_attr
  499. def __mapper_args__(cls):
  500. return {
  501. 'polymorphic_identity': cls.__name__,
  502. 'polymorphic_on': cls.discriminator
  503. }
  504. class B(A):
  505. pass
  506. is_(B.__mapper__.polymorphic_on, A.__table__.c.discriminator)
  507. def test_add_deferred(self):
  508. class Person(Base, fixtures.ComparableEntity):
  509. __tablename__ = 'people'
  510. id = Column('id', Integer, primary_key=True,
  511. test_needs_autoincrement=True)
  512. Person.name = deferred(Column(String(10)))
  513. Base.metadata.create_all()
  514. sess = create_session()
  515. p = Person(name='ratbert')
  516. sess.add(p)
  517. sess.flush()
  518. sess.expunge_all()
  519. eq_(sess.query(Person).all(), [Person(name='ratbert')])
  520. sess.expunge_all()
  521. person = sess.query(Person).filter(Person.name == 'ratbert'
  522. ).one()
  523. assert 'name' not in person.__dict__
  524. def test_single_fksonsub(self):
  525. """test single inheritance with a foreign key-holding column on
  526. a subclass.
  527. """
  528. class Person(Base, fixtures.ComparableEntity):
  529. __tablename__ = 'people'
  530. id = Column(Integer, primary_key=True,
  531. test_needs_autoincrement=True)
  532. name = Column(String(50))
  533. discriminator = Column('type', String(50))
  534. __mapper_args__ = {'polymorphic_on': discriminator}
  535. class Engineer(Person):
  536. __mapper_args__ = {'polymorphic_identity': 'engineer'}
  537. primary_language_id = Column(Integer,
  538. ForeignKey('languages.id'))
  539. primary_language = relationship('Language')
  540. class Language(Base, fixtures.ComparableEntity):
  541. __tablename__ = 'languages'
  542. id = Column(Integer, primary_key=True,
  543. test_needs_autoincrement=True)
  544. name = Column(String(50))
  545. assert not hasattr(Person, 'primary_language_id')
  546. Base.metadata.create_all()
  547. sess = create_session()
  548. java, cpp, cobol = Language(name='java'), Language(name='cpp'), \
  549. Language(name='cobol')
  550. e1 = Engineer(name='dilbert', primary_language=java)
  551. e2 = Engineer(name='wally', primary_language=cpp)
  552. e3 = Engineer(name='vlad', primary_language=cobol)
  553. sess.add_all([e1, e2, e3])
  554. sess.flush()
  555. sess.expunge_all()
  556. eq_(sess.query(Person).filter(Engineer.primary_language.has(
  557. Language.name
  558. == 'cobol')).first(), Engineer(name='vlad',
  559. primary_language=Language(name='cobol')))
  560. eq_(sess.query(Engineer).filter(Engineer.primary_language.has(
  561. Language.name
  562. == 'cobol')).one(), Engineer(name='vlad',
  563. primary_language=Language(name='cobol')))
  564. eq_(sess.query(Person).join(Engineer.primary_language).order_by(
  565. Language.name).all(),
  566. [Engineer(name='vlad',
  567. primary_language=Language(name='cobol')),
  568. Engineer(name='wally', primary_language=Language(name='cpp'
  569. )), Engineer(name='dilbert',
  570. primary_language=Language(name='java'))])
  571. def test_single_three_levels(self):
  572. class Person(Base, fixtures.ComparableEntity):
  573. __tablename__ = 'people'
  574. id = Column(Integer, primary_key=True)
  575. name = Column(String(50))
  576. discriminator = Column('type', String(50))
  577. __mapper_args__ = {'polymorphic_on': discriminator}
  578. class Engineer(Person):
  579. __mapper_args__ = {'polymorphic_identity': 'engineer'}
  580. primary_language = Column(String(50))
  581. class JuniorEngineer(Engineer):
  582. __mapper_args__ = \
  583. {'polymorphic_identity': 'junior_engineer'}
  584. nerf_gun = Column(String(50))
  585. class Manager(Person):
  586. __mapper_args__ = {'polymorphic_identity': 'manager'}
  587. golf_swing = Column(String(50))
  588. assert JuniorEngineer.nerf_gun
  589. assert JuniorEngineer.primary_language
  590. assert JuniorEngineer.name
  591. assert Manager.golf_swing
  592. assert Engineer.primary_language
  593. assert not hasattr(Engineer, 'golf_swing')
  594. assert not hasattr(Engineer, 'nerf_gun')
  595. assert not hasattr(Manager, 'nerf_gun')
  596. assert not hasattr(Manager, 'primary_language')
  597. def test_single_detects_conflict(self):
  598. class Person(Base):
  599. __tablename__ = 'people'
  600. id = Column(Integer, primary_key=True)
  601. name = Column(String(50))
  602. discriminator = Column('type', String(50))
  603. __mapper_args__ = {'polymorphic_on': discriminator}
  604. class Engineer(Person):
  605. __mapper_args__ = {'polymorphic_identity': 'engineer'}
  606. primary_language = Column(String(50))
  607. # test sibling col conflict
  608. def go():
  609. class Manager(Person):
  610. __mapper_args__ = {'polymorphic_identity': 'manager'}
  611. golf_swing = Column(String(50))
  612. primary_language = Column(String(50))
  613. assert_raises(sa.exc.ArgumentError, go)
  614. # test parent col conflict
  615. def go():
  616. class Salesman(Person):
  617. __mapper_args__ = {'polymorphic_identity': 'manager'}
  618. name = Column(String(50))
  619. assert_raises(sa.exc.ArgumentError, go)
  620. def test_single_no_special_cols(self):
  621. class Person(Base, fixtures.ComparableEntity):
  622. __tablename__ = 'people'
  623. id = Column('id', Integer, primary_key=True)
  624. name = Column('name', String(50))
  625. discriminator = Column('type', String(50))
  626. __mapper_args__ = {'polymorphic_on': discriminator}
  627. def go():
  628. class Engineer(Person):
  629. __mapper_args__ = {'polymorphic_identity': 'engineer'}
  630. primary_language = Column('primary_language',
  631. String(50))
  632. foo_bar = Column(Integer, primary_key=True)
  633. assert_raises_message(sa.exc.ArgumentError, 'place primary key'
  634. , go)
  635. def test_single_no_table_args(self):
  636. class Person(Base, fixtures.ComparableEntity):
  637. __tablename__ = 'people'
  638. id = Column('id', Integer, primary_key=True)
  639. name = Column('name', String(50))
  640. discriminator = Column('type', String(50))
  641. __mapper_args__ = {'polymorphic_on': discriminator}
  642. def go():
  643. class Engineer(Person):
  644. __mapper_args__ = {'polymorphic_identity': 'engineer'}
  645. primary_language = Column('primary_language',
  646. String(50))
  647. # this should be on the Person class, as this is single
  648. # table inheritance, which is why we test that this
  649. # throws an exception!
  650. __table_args__ = {'mysql_engine': 'InnoDB'}
  651. assert_raises_message(sa.exc.ArgumentError,
  652. 'place __table_args__', go)
  653. @testing.emits_warning("This declarative")
  654. def test_dupe_name_in_hierarchy(self):
  655. class A(Base):
  656. __tablename__ = "a"
  657. id = Column( Integer, primary_key=True)
  658. a_1 = A
  659. class A(a_1):
  660. __tablename__ = 'b'
  661. id = Column(Integer(),ForeignKey(a_1.id), primary_key = True)
  662. assert A.__mapper__.inherits is a_1.__mapper__
  663. class OverlapColPrecedenceTest(DeclarativeTestBase):
  664. """test #1892 cases when declarative does column precedence."""
  665. def _run_test(self, Engineer, e_id, p_id):
  666. p_table = Base.metadata.tables['person']
  667. e_table = Base.metadata.tables['engineer']
  668. assert Engineer.id.property.columns[0] is e_table.c[e_id]
  669. assert Engineer.id.property.columns[1] is p_table.c[p_id]
  670. def test_basic(self):
  671. class Person(Base):
  672. __tablename__ = 'person'
  673. id = Column(Integer, primary_key=True)
  674. class Engineer(Person):
  675. __tablename__ = 'engineer'
  676. id = Column(Integer, ForeignKey('person.id'), primary_key=True)
  677. self._run_test(Engineer, "id", "id")
  678. def test_alt_name_base(self):
  679. class Person(Base):
  680. __tablename__ = 'person'
  681. id = Column("pid", Integer, primary_key=True)
  682. class Engineer(Person):
  683. __tablename__ = 'engineer'
  684. id = Column(Integer, ForeignKey('person.pid'), primary_key=True)
  685. self._run_test(Engineer, "id", "pid")
  686. def test_alt_name_sub(self):
  687. class Person(Base):
  688. __tablename__ = 'person'
  689. id = Column(Integer, primary_key=True)
  690. class Engineer(Person):
  691. __tablename__ = 'engineer'
  692. id = Column("eid", Integer, ForeignKey('person.id'), primary_key=True)
  693. self._run_test(Engineer, "eid", "id")
  694. def test_alt_name_both(self):
  695. class Person(Base):
  696. __tablename__ = 'person'
  697. id = Column("pid", Integer, primary_key=True)
  698. class Engineer(Person):
  699. __tablename__ = 'engineer'
  700. id = Column("eid", Integer, ForeignKey('person.pid'), primary_key=True)
  701. self._run_test(Engineer, "eid", "pid")
  702. from test.orm.test_events import _RemoveListeners
  703. class ConcreteInhTest(_RemoveListeners, DeclarativeTestBase):
  704. def _roundtrip(self, Employee, Manager, Engineer, Boss,
  705. polymorphic=True, explicit_type=False):
  706. Base.metadata.create_all()
  707. sess = create_session()
  708. e1 = Engineer(name='dilbert', primary_language='java')
  709. e2 = Engineer(name='wally', primary_language='c++')
  710. m1 = Manager(name='dogbert', golf_swing='fore!')
  711. e3 = Engineer(name='vlad', primary_language='cobol')
  712. b1 = Boss(name="pointy haired")
  713. if polymorphic:
  714. for obj in [e1, e2, m1, e3, b1]:
  715. if explicit_type:
  716. eq_(obj.type, obj.__mapper__.polymorphic_identity)
  717. else:
  718. assert_raises_message(
  719. AttributeError,
  720. "does not implement attribute .?'type' "
  721. "at the instance level.",
  722. getattr, obj, "type"
  723. )
  724. else:
  725. assert "type" not in Engineer.__dict__
  726. assert "type" not in Manager.__dict__
  727. assert "type" not in Boss.__dict__
  728. sess.add_all([e1, e2, m1, e3, b1])
  729. sess.flush()
  730. sess.expunge_all()
  731. if polymorphic:
  732. eq_(sess.query(Employee).order_by(Employee.name).all(),
  733. [Engineer(name='dilbert'), Manager(name='dogbert'),
  734. Boss(name='pointy haired'), Engineer(name='vlad'), Engineer(name='wally')])
  735. else:
  736. eq_(sess.query(Engineer).order_by(Engineer.name).all(),
  737. [Engineer(name='dilbert'), Engineer(name='vlad'),
  738. Engineer(name='wally')])
  739. eq_(sess.query(Manager).all(), [Manager(name='dogbert')])
  740. eq_(sess.query(Boss).all(), [Boss(name='pointy haired')])
  741. def test_explicit(self):
  742. engineers = Table('engineers', Base.metadata, Column('id',
  743. Integer, primary_key=True,
  744. test_needs_autoincrement=True), Column('name'
  745. , String(50)), Column('primary_language',
  746. String(50)))
  747. managers = Table('managers', Base.metadata,
  748. Column('id',Integer, primary_key=True, test_needs_autoincrement=True),
  749. Column('name', String(50)),
  750. Column('golf_swing', String(50))
  751. )
  752. boss = Table('boss', Base.metadata,
  753. Column('id',Integer, primary_key=True, test_needs_autoincrement=True),
  754. Column('name', String(50)),
  755. Column('golf_swing', String(50))
  756. )
  757. punion = polymorphic_union({
  758. 'engineer': engineers,
  759. 'manager' : managers,
  760. 'boss': boss}, 'type', 'punion')
  761. class Employee(Base, fixtures.ComparableEntity):
  762. __table__ = punion
  763. __mapper_args__ = {'polymorphic_on': punion.c.type}
  764. class Engineer(Employee):
  765. __table__ = engineers
  766. __mapper_args__ = {'polymorphic_identity': 'engineer',
  767. 'concrete': True}
  768. class Manager(Employee):
  769. __table__ = managers
  770. __mapper_args__ = {'polymorphic_identity': 'manager',
  771. 'concrete': True}
  772. class Boss(Manager):
  773. __table__ = boss
  774. __mapper_args__ = {'polymorphic_identity': 'boss',
  775. 'concrete': True}
  776. self._roundtrip(Employee, Manager, Engineer, Boss)
  777. def test_concrete_inline_non_polymorphic(self):
  778. """test the example from the declarative docs."""
  779. class Employee(Base, fixtures.ComparableEntity):
  780. __tablename__ = 'people'
  781. id = Column(Integer, primary_key=True,
  782. test_needs_autoincrement=True)
  783. name = Column(String(50))
  784. class Engineer(Employee):
  785. __tablename__ = 'engineers'
  786. __mapper_args__ = {'concrete': True}
  787. id = Column(Integer, primary_key=True,
  788. test_needs_autoincrement=True)
  789. primary_language = Column(String(50))
  790. name = Column(String(50))
  791. class Manager(Employee):
  792. __tablename__ = 'manager'
  793. __mapper_args__ = {'concrete': True}
  794. id = Column(Integer, primary_key=True,
  795. test_needs_autoincrement=True)
  796. golf_swing = Column(String(50))
  797. name = Column(String(50))
  798. class Boss(Manager):
  799. __tablename__ = 'boss'
  800. __mapper_args__ = {'concrete': True}
  801. id = Column(Integer, primary_key=True,
  802. test_needs_autoincrement=True)
  803. golf_swing = Column(String(50))
  804. name = Column(String(50))
  805. self._roundtrip(Employee, Manager, Engineer, Boss, polymorphic=False)
  806. def test_abstract_concrete_extension(self):
  807. class Employee(AbstractConcreteBase, Base, fixtures.ComparableEntity):
  808. pass
  809. class Manager(Employee):
  810. __tablename__ = 'manager'
  811. employee_id = Column(Integer, primary_key=True,
  812. test_needs_autoincrement=True)
  813. name = Column(String(50))
  814. golf_swing = Column(String(40))
  815. __mapper_args__ = {
  816. 'polymorphic_identity':'manager',
  817. 'concrete':True}
  818. class Boss(Manager):
  819. __tablename__ = 'boss'
  820. employee_id = Column(Integer, primary_key=True,
  821. test_needs_autoincrement=True)
  822. name = Column(String(50))
  823. golf_swing = Column(String(40))
  824. __mapper_args__ = {
  825. 'polymorphic_identity':'boss',
  826. 'concrete':True}
  827. class Engineer(Employee):
  828. __tablename__ = 'engineer'
  829. employee_id = Column(Integer, primary_key=True,
  830. test_needs_autoincrement=True)
  831. name = Column(String(50))
  832. primary_language = Column(String(40))
  833. __mapper_args__ = {'polymorphic_identity':'engineer',
  834. 'concrete':True}
  835. self._roundtrip(Employee, Manager, Engineer, Boss)
  836. def test_concrete_extension(self):
  837. class Employee(ConcreteBase, Base, fixtures.ComparableEntity):
  838. __tablename__ = 'employee'
  839. employee_id = Column(Integer, primary_key=True,
  840. test_needs_autoincrement=True)
  841. name = Column(String(50))
  842. __mapper_args__ = {
  843. 'polymorphic_identity':'employee',
  844. 'concrete':True}
  845. class Manager(Employee):
  846. __tablename__ = 'manager'
  847. employee_id = Column(Integer, primary_key=True,
  848. test_needs_autoincrement=True)
  849. name = Column(String(50))
  850. golf_swing = Column(String(40))
  851. __mapper_args__ = {
  852. 'polymorphic_identity':'manager',
  853. 'concrete':True}
  854. class Boss(Manager):
  855. __tablename__ = 'boss'
  856. employee_id = Column(Integer, primary_key=True,
  857. test_needs_autoincrement=True)
  858. name = Column(String(50))
  859. golf_swing = Column(String(40))
  860. __mapper_args__ = {
  861. 'polymorphic_identity':'boss',
  862. 'concrete':True}
  863. class Engineer(Employee):
  864. __tablename__ = 'engineer'
  865. employee_id = Column(Integer, primary_key=True,
  866. test_needs_autoincrement=True)
  867. name = Column(String(50))
  868. primary_language = Column(String(40))
  869. __mapper_args__ = {'polymorphic_identity':'engineer',
  870. 'concrete':True}
  871. self._roundtrip(Employee, Manager, Engineer, Boss)
  872. def test_ok_to_override_type_from_abstract(self):
  873. class Employee(AbstractConcreteBase, Base, fixtures.ComparableEntity):
  874. pass
  875. class Manager(Employee):
  876. __tablename__ = 'manager'
  877. employee_id = Column(Integer, primary_key=True,
  878. test_needs_autoincrement=True)
  879. name = Column(String(50))
  880. golf_swing = Column(String(40))
  881. @property
  882. def type(self):
  883. return "manager"
  884. __mapper_args__ = {
  885. 'polymorphic_identity': "manager",
  886. 'concrete':True}
  887. class Boss(Manager):
  888. __tablename__ = 'boss'
  889. employee_id = Column(Integer, primary_key=True,
  890. test_needs_autoincrement=True)
  891. name = Column(String(50))
  892. golf_swing = Column(String(40))
  893. @property
  894. def type(self):
  895. return "boss"
  896. __mapper_args__ = {
  897. 'polymorphic_identity': "boss",
  898. 'concrete':True}
  899. class Engineer(Employee):
  900. __tablename__ = 'engineer'
  901. employee_id = Column(Integer, primary_key=True,
  902. test_needs_autoincrement=True)
  903. name = Column(String(50))
  904. primary_language = Column(String(40))
  905. @property
  906. def type(self):
  907. return "engineer"
  908. __mapper_args__ = {'polymorphic_identity': "engineer",
  909. 'concrete':True}
  910. self._roundtrip(Employee, Manager, Engineer, Boss, explicit_type=True)