PageRenderTime 69ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/webserver/dependencies/SQLAlchemy-0.5.6/test/ext/test_declarative.py

https://github.com/hughperkins/springgrid
Python | 1606 lines | 1239 code | 314 blank | 53 comment | 21 complexity | c929a54b248f2aef8e5fb7d6f5ec52c9 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
  2. from sqlalchemy.ext import declarative as decl
  3. from sqlalchemy import exc
  4. import sqlalchemy as sa
  5. from sqlalchemy.test import testing
  6. from sqlalchemy import MetaData, Integer, String, ForeignKey, ForeignKeyConstraint, asc, Index
  7. from sqlalchemy.test.schema import Table
  8. from sqlalchemy.test.schema import Column
  9. from sqlalchemy.orm import relation, create_session, class_mapper, eagerload, compile_mappers, backref, clear_mappers, polymorphic_union, deferred
  10. from sqlalchemy.test.testing import eq_
  11. from test.orm._base import ComparableEntity, MappedTest
  12. class DeclarativeTestBase(testing.TestBase, testing.AssertsExecutionResults):
  13. def setup(self):
  14. global Base
  15. Base = decl.declarative_base(testing.db)
  16. def teardown(self):
  17. clear_mappers()
  18. Base.metadata.drop_all()
  19. class DeclarativeTest(DeclarativeTestBase):
  20. def test_basic(self):
  21. class User(Base, ComparableEntity):
  22. __tablename__ = 'users'
  23. id = Column('id', Integer, primary_key=True)
  24. name = Column('name', String(50))
  25. addresses = relation("Address", backref="user")
  26. class Address(Base, ComparableEntity):
  27. __tablename__ = 'addresses'
  28. id = Column(Integer, primary_key=True)
  29. email = Column(String(50), key='_email')
  30. user_id = Column('user_id', Integer, ForeignKey('users.id'),
  31. key='_user_id')
  32. Base.metadata.create_all()
  33. eq_(Address.__table__.c['id'].name, 'id')
  34. eq_(Address.__table__.c['_email'].name, 'email')
  35. eq_(Address.__table__.c['_user_id'].name, 'user_id')
  36. u1 = User(name='u1', addresses=[
  37. Address(email='one'),
  38. Address(email='two'),
  39. ])
  40. sess = create_session()
  41. sess.add(u1)
  42. sess.flush()
  43. sess.expunge_all()
  44. eq_(sess.query(User).all(), [User(name='u1', addresses=[
  45. Address(email='one'),
  46. Address(email='two'),
  47. ])])
  48. a1 = sess.query(Address).filter(Address.email == 'two').one()
  49. eq_(a1, Address(email='two'))
  50. eq_(a1.user, User(name='u1'))
  51. def test_no_table(self):
  52. def go():
  53. class User(Base):
  54. id = Column('id', Integer, primary_key=True)
  55. assert_raises_message(sa.exc.InvalidRequestError, "does not have a __table__", go)
  56. def test_cant_add_columns(self):
  57. t = Table('t', Base.metadata, Column('id', Integer, primary_key=True), Column('data', String))
  58. def go():
  59. class User(Base):
  60. __table__ = t
  61. foo = Column(Integer, primary_key=True)
  62. # can't specify new columns not already in the table
  63. assert_raises_message(sa.exc.ArgumentError, "Can't add additional column 'foo' when specifying __table__", go)
  64. # regular re-mapping works tho
  65. class Bar(Base):
  66. __table__ = t
  67. some_data = t.c.data
  68. assert class_mapper(Bar).get_property('some_data').columns[0] is t.c.data
  69. def test_undefer_column_name(self):
  70. # TODO: not sure if there was an explicit
  71. # test for this elsewhere
  72. foo = Column(Integer)
  73. eq_(str(foo), '(no name)')
  74. eq_(foo.key, None)
  75. eq_(foo.name, None)
  76. decl._undefer_column_name('foo', foo)
  77. eq_(str(foo), 'foo')
  78. eq_(foo.key, 'foo')
  79. eq_(foo.name, 'foo')
  80. def test_recompile_on_othermapper(self):
  81. """declarative version of the same test in mappers.py"""
  82. from sqlalchemy.orm import mapperlib
  83. class User(Base):
  84. __tablename__ = 'users'
  85. id = Column('id', Integer, primary_key=True)
  86. name = Column('name', String(50))
  87. class Address(Base):
  88. __tablename__ = 'addresses'
  89. id = Column('id', Integer, primary_key=True)
  90. email = Column('email', String(50))
  91. user_id = Column('user_id', Integer, ForeignKey('users.id'))
  92. user = relation("User", primaryjoin=user_id == User.id,
  93. backref="addresses")
  94. assert mapperlib._new_mappers is True
  95. u = User()
  96. assert User.addresses
  97. assert mapperlib._new_mappers is False
  98. def test_string_dependency_resolution(self):
  99. from sqlalchemy.sql import desc
  100. class User(Base, ComparableEntity):
  101. __tablename__ = 'users'
  102. id = Column(Integer, primary_key=True)
  103. name = Column(String(50))
  104. addresses = relation("Address", order_by="desc(Address.email)",
  105. primaryjoin="User.id==Address.user_id", foreign_keys="[Address.user_id]",
  106. backref=backref('user', primaryjoin="User.id==Address.user_id", foreign_keys="[Address.user_id]")
  107. )
  108. class Address(Base, ComparableEntity):
  109. __tablename__ = 'addresses'
  110. id = Column(Integer, primary_key=True)
  111. email = Column(String(50))
  112. user_id = Column(Integer) # note no foreign key
  113. Base.metadata.create_all()
  114. sess = create_session()
  115. u1 = User(name='ed', addresses=[Address(email='abc'), Address(email='def'), Address(email='xyz')])
  116. sess.add(u1)
  117. sess.flush()
  118. sess.expunge_all()
  119. eq_(sess.query(User).filter(User.name == 'ed').one(),
  120. User(name='ed', addresses=[Address(email='xyz'), Address(email='def'), Address(email='abc')])
  121. )
  122. class Foo(Base, ComparableEntity):
  123. __tablename__ = 'foo'
  124. id = Column(Integer, primary_key=True)
  125. rel = relation("User", primaryjoin="User.addresses==Foo.id")
  126. assert_raises_message(exc.InvalidRequestError, "'addresses' is not an instance of ColumnProperty", compile_mappers)
  127. def test_string_dependency_resolution_in_backref(self):
  128. class User(Base, ComparableEntity):
  129. __tablename__ = 'users'
  130. id = Column(Integer, primary_key=True)
  131. name = Column(String(50))
  132. addresses = relation("Address",
  133. primaryjoin="User.id==Address.user_id",
  134. backref="user"
  135. )
  136. class Address(Base, ComparableEntity):
  137. __tablename__ = 'addresses'
  138. id = Column(Integer, primary_key=True)
  139. email = Column(String(50))
  140. user_id = Column(Integer, ForeignKey('users.id'))
  141. compile_mappers()
  142. eq_(str(User.addresses.property.primaryjoin), str(Address.user.property.primaryjoin))
  143. def test_string_dependency_resolution_tables(self):
  144. class User(Base, ComparableEntity):
  145. __tablename__ = 'users'
  146. id = Column(Integer, primary_key=True)
  147. name = Column(String(50))
  148. props = relation("Prop",
  149. secondary="user_to_prop",
  150. primaryjoin="User.id==user_to_prop.c.user_id",
  151. secondaryjoin="user_to_prop.c.prop_id==Prop.id",
  152. backref="users")
  153. class Prop(Base, ComparableEntity):
  154. __tablename__ = 'props'
  155. id = Column(Integer, primary_key=True)
  156. name = Column(String(50))
  157. user_to_prop = Table('user_to_prop', Base.metadata,
  158. Column('user_id', Integer, ForeignKey('users.id')),
  159. Column('prop_id', Integer, ForeignKey('props.id')),
  160. )
  161. compile_mappers()
  162. assert class_mapper(User).get_property("props").secondary is user_to_prop
  163. def test_uncompiled_attributes_in_relation(self):
  164. class Address(Base, ComparableEntity):
  165. __tablename__ = 'addresses'
  166. id = Column(Integer, primary_key=True)
  167. email = Column(String(50))
  168. user_id = Column(Integer, ForeignKey('users.id'))
  169. class User(Base, ComparableEntity):
  170. __tablename__ = 'users'
  171. id = Column(Integer, primary_key=True)
  172. name = Column(String(50))
  173. addresses = relation("Address", order_by=Address.email,
  174. foreign_keys=Address.user_id,
  175. remote_side=Address.user_id,
  176. )
  177. # get the mapper for User. User mapper will compile,
  178. # "addresses" relation will call upon Address.user_id for
  179. # its clause element. Address.user_id is a _CompileOnAttr,
  180. # which then calls class_mapper(Address). But ! We're already
  181. # "in compilation", but class_mapper(Address) needs to initialize
  182. # regardless, or COA's assertion fails
  183. # and things generally go downhill from there.
  184. class_mapper(User)
  185. Base.metadata.create_all()
  186. sess = create_session()
  187. u1 = User(name='ed', addresses=[Address(email='abc'), Address(email='xyz'), Address(email='def')])
  188. sess.add(u1)
  189. sess.flush()
  190. sess.expunge_all()
  191. eq_(sess.query(User).filter(User.name == 'ed').one(),
  192. User(name='ed', addresses=[Address(email='abc'), Address(email='def'), Address(email='xyz')])
  193. )
  194. def test_nice_dependency_error(self):
  195. class User(Base):
  196. __tablename__ = 'users'
  197. id = Column('id', Integer, primary_key=True)
  198. addresses = relation("Address")
  199. class Address(Base):
  200. __tablename__ = 'addresses'
  201. id = Column(Integer, primary_key=True)
  202. foo = sa.orm.column_property(User.id == 5)
  203. # this used to raise an error when accessing User.id but that's no longer the case
  204. # since we got rid of _CompileOnAttr.
  205. assert_raises(sa.exc.ArgumentError, compile_mappers)
  206. def test_nice_dependency_error_works_with_hasattr(self):
  207. class User(Base):
  208. __tablename__ = 'users'
  209. id = Column('id', Integer, primary_key=True)
  210. addresses = relation("Addresss")
  211. # hasattr() on a compile-loaded attribute
  212. hasattr(User.addresses, 'property')
  213. # the exeption is preserved
  214. assert_raises_message(sa.exc.InvalidRequestError, r"suppressed within a hasattr\(\)", compile_mappers)
  215. def test_custom_base(self):
  216. class MyBase(object):
  217. def foobar(self):
  218. return "foobar"
  219. Base = decl.declarative_base(cls=MyBase)
  220. assert hasattr(Base, 'metadata')
  221. assert Base().foobar() == "foobar"
  222. def test_index_doesnt_compile(self):
  223. class User(Base):
  224. __tablename__ = 'users'
  225. id = Column('id', Integer, primary_key=True)
  226. name = Column('name', String(50))
  227. error = relation("Address")
  228. i = Index('my_index', User.name)
  229. # compile fails due to the nonexistent Addresses relation
  230. assert_raises(sa.exc.InvalidRequestError, compile_mappers)
  231. # index configured
  232. assert i in User.__table__.indexes
  233. assert User.__table__.c.id not in set(i.columns)
  234. assert User.__table__.c.name in set(i.columns)
  235. # tables create fine
  236. Base.metadata.create_all()
  237. def test_add_prop(self):
  238. class User(Base, ComparableEntity):
  239. __tablename__ = 'users'
  240. id = Column('id', Integer, primary_key=True)
  241. User.name = Column('name', String(50))
  242. User.addresses = relation("Address", backref="user")
  243. class Address(Base, ComparableEntity):
  244. __tablename__ = 'addresses'
  245. id = Column(Integer, primary_key=True)
  246. Address.email = Column(String(50), key='_email')
  247. Address.user_id = Column('user_id', Integer, ForeignKey('users.id'),
  248. key='_user_id')
  249. Base.metadata.create_all()
  250. eq_(Address.__table__.c['id'].name, 'id')
  251. eq_(Address.__table__.c['_email'].name, 'email')
  252. eq_(Address.__table__.c['_user_id'].name, 'user_id')
  253. u1 = User(name='u1', addresses=[
  254. Address(email='one'),
  255. Address(email='two'),
  256. ])
  257. sess = create_session()
  258. sess.add(u1)
  259. sess.flush()
  260. sess.expunge_all()
  261. eq_(sess.query(User).all(), [User(name='u1', addresses=[
  262. Address(email='one'),
  263. Address(email='two'),
  264. ])])
  265. a1 = sess.query(Address).filter(Address.email == 'two').one()
  266. eq_(a1, Address(email='two'))
  267. eq_(a1.user, User(name='u1'))
  268. def test_eager_order_by(self):
  269. class Address(Base, ComparableEntity):
  270. __tablename__ = 'addresses'
  271. id = Column('id', Integer, primary_key=True)
  272. email = Column('email', String(50))
  273. user_id = Column('user_id', Integer, ForeignKey('users.id'))
  274. class User(Base, ComparableEntity):
  275. __tablename__ = 'users'
  276. id = Column('id', Integer, primary_key=True)
  277. name = Column('name', String(50))
  278. addresses = relation("Address", order_by=Address.email)
  279. Base.metadata.create_all()
  280. u1 = User(name='u1', addresses=[
  281. Address(email='two'),
  282. Address(email='one'),
  283. ])
  284. sess = create_session()
  285. sess.add(u1)
  286. sess.flush()
  287. sess.expunge_all()
  288. eq_(sess.query(User).options(eagerload(User.addresses)).all(), [User(name='u1', addresses=[
  289. Address(email='one'),
  290. Address(email='two'),
  291. ])])
  292. def test_order_by_multi(self):
  293. class Address(Base, ComparableEntity):
  294. __tablename__ = 'addresses'
  295. id = Column('id', Integer, primary_key=True)
  296. email = Column('email', String(50))
  297. user_id = Column('user_id', Integer, ForeignKey('users.id'))
  298. class User(Base, ComparableEntity):
  299. __tablename__ = 'users'
  300. id = Column('id', Integer, primary_key=True)
  301. name = Column('name', String(50))
  302. addresses = relation("Address", order_by=(Address.email, Address.id))
  303. Base.metadata.create_all()
  304. u1 = User(name='u1', addresses=[
  305. Address(email='two'),
  306. Address(email='one'),
  307. ])
  308. sess = create_session()
  309. sess.add(u1)
  310. sess.flush()
  311. sess.expunge_all()
  312. u = sess.query(User).filter(User.name == 'u1').one()
  313. a = u.addresses
  314. def test_as_declarative(self):
  315. class User(ComparableEntity):
  316. __tablename__ = 'users'
  317. id = Column('id', Integer, primary_key=True)
  318. name = Column('name', String(50))
  319. addresses = relation("Address", backref="user")
  320. class Address(ComparableEntity):
  321. __tablename__ = 'addresses'
  322. id = Column('id', Integer, primary_key=True)
  323. email = Column('email', String(50))
  324. user_id = Column('user_id', Integer, ForeignKey('users.id'))
  325. reg = {}
  326. decl.instrument_declarative(User, reg, Base.metadata)
  327. decl.instrument_declarative(Address, reg, Base.metadata)
  328. Base.metadata.create_all()
  329. u1 = User(name='u1', addresses=[
  330. Address(email='one'),
  331. Address(email='two'),
  332. ])
  333. sess = create_session()
  334. sess.add(u1)
  335. sess.flush()
  336. sess.expunge_all()
  337. eq_(sess.query(User).all(), [User(name='u1', addresses=[
  338. Address(email='one'),
  339. Address(email='two'),
  340. ])])
  341. @testing.uses_deprecated()
  342. def test_custom_mapper(self):
  343. class MyExt(sa.orm.MapperExtension):
  344. def create_instance(self):
  345. return "CHECK"
  346. def mymapper(cls, tbl, **kwargs):
  347. kwargs['extension'] = MyExt()
  348. return sa.orm.mapper(cls, tbl, **kwargs)
  349. from sqlalchemy.orm.mapper import Mapper
  350. class MyMapper(Mapper):
  351. def __init__(self, *args, **kwargs):
  352. kwargs['extension'] = MyExt()
  353. Mapper.__init__(self, *args, **kwargs)
  354. from sqlalchemy.orm import scoping
  355. ss = scoping.ScopedSession(create_session)
  356. ss.extension = MyExt()
  357. ss_mapper = ss.mapper
  358. for mapperfunc in (mymapper, MyMapper, ss_mapper):
  359. base = decl.declarative_base()
  360. class Foo(base):
  361. __tablename__ = 'foo'
  362. __mapper_cls__ = mapperfunc
  363. id = Column(Integer, primary_key=True)
  364. eq_(Foo.__mapper__.compile().extension.create_instance(), 'CHECK')
  365. base = decl.declarative_base(mapper=mapperfunc)
  366. class Foo(base):
  367. __tablename__ = 'foo'
  368. id = Column(Integer, primary_key=True)
  369. eq_(Foo.__mapper__.compile().extension.create_instance(), 'CHECK')
  370. @testing.emits_warning('Ignoring declarative-like tuple value of '
  371. 'attribute id')
  372. def test_oops(self):
  373. def define():
  374. class User(Base, ComparableEntity):
  375. __tablename__ = 'users'
  376. id = Column('id', Integer, primary_key=True),
  377. name = Column('name', String(50))
  378. assert False
  379. assert_raises_message(
  380. sa.exc.ArgumentError,
  381. "Mapper Mapper|User|users could not assemble any primary key",
  382. define)
  383. def test_table_args(self):
  384. def err():
  385. class Foo(Base):
  386. __tablename__ = 'foo'
  387. __table_args__ = (ForeignKeyConstraint(['id'], ['foo.id']),)
  388. id = Column('id', Integer, primary_key=True)
  389. assert_raises_message(sa.exc.ArgumentError, "Tuple form of __table_args__ is ", err)
  390. class Foo(Base):
  391. __tablename__ = 'foo'
  392. __table_args__ = {'mysql_engine':'InnoDB'}
  393. id = Column('id', Integer, primary_key=True)
  394. assert Foo.__table__.kwargs['mysql_engine'] == 'InnoDB'
  395. class Bar(Base):
  396. __tablename__ = 'bar'
  397. __table_args__ = (ForeignKeyConstraint(['id'], ['foo.id']), {'mysql_engine':'InnoDB'})
  398. id = Column('id', Integer, primary_key=True)
  399. assert Bar.__table__.c.id.references(Foo.__table__.c.id)
  400. assert Bar.__table__.kwargs['mysql_engine'] == 'InnoDB'
  401. def test_expression(self):
  402. class User(Base, ComparableEntity):
  403. __tablename__ = 'users'
  404. id = Column('id', Integer, primary_key=True)
  405. name = Column('name', String(50))
  406. addresses = relation("Address", backref="user")
  407. class Address(Base, ComparableEntity):
  408. __tablename__ = 'addresses'
  409. id = Column('id', Integer, primary_key=True)
  410. email = Column('email', String(50))
  411. user_id = Column('user_id', Integer, ForeignKey('users.id'))
  412. User.address_count = sa.orm.column_property(
  413. sa.select([sa.func.count(Address.id)]).
  414. where(Address.user_id == User.id).as_scalar())
  415. Base.metadata.create_all()
  416. u1 = User(name='u1', addresses=[
  417. Address(email='one'),
  418. Address(email='two'),
  419. ])
  420. sess = create_session()
  421. sess.add(u1)
  422. sess.flush()
  423. sess.expunge_all()
  424. eq_(sess.query(User).all(),
  425. [User(name='u1', address_count=2, addresses=[
  426. Address(email='one'),
  427. Address(email='two')])])
  428. def test_column(self):
  429. class User(Base, ComparableEntity):
  430. __tablename__ = 'users'
  431. id = Column('id', Integer, primary_key=True)
  432. name = Column('name', String(50))
  433. User.a = Column('a', String(10))
  434. User.b = Column(String(10))
  435. Base.metadata.create_all()
  436. u1 = User(name='u1', a='a', b='b')
  437. eq_(u1.a, 'a')
  438. eq_(User.a.get_history(u1), (['a'], (), ()))
  439. sess = create_session()
  440. sess.add(u1)
  441. sess.flush()
  442. sess.expunge_all()
  443. eq_(sess.query(User).all(),
  444. [User(name='u1', a='a', b='b')])
  445. def test_column_properties(self):
  446. class Address(Base, ComparableEntity):
  447. __tablename__ = 'addresses'
  448. id = Column(Integer, primary_key=True)
  449. email = Column(String(50))
  450. user_id = Column(Integer, ForeignKey('users.id'))
  451. class User(Base, ComparableEntity):
  452. __tablename__ = 'users'
  453. id = Column('id', Integer, primary_key=True)
  454. name = Column('name', String(50))
  455. adr_count = sa.orm.column_property(
  456. sa.select([sa.func.count(Address.id)], Address.user_id == id).
  457. as_scalar())
  458. addresses = relation(Address)
  459. Base.metadata.create_all()
  460. u1 = User(name='u1', addresses=[
  461. Address(email='one'),
  462. Address(email='two'),
  463. ])
  464. sess = create_session()
  465. sess.add(u1)
  466. sess.flush()
  467. sess.expunge_all()
  468. eq_(sess.query(User).all(),
  469. [User(name='u1', adr_count=2, addresses=[
  470. Address(email='one'),
  471. Address(email='two')])])
  472. def test_column_properties_2(self):
  473. class Address(Base, ComparableEntity):
  474. __tablename__ = 'addresses'
  475. id = Column(Integer, primary_key=True)
  476. email = Column(String(50))
  477. user_id = Column(Integer, ForeignKey('users.id'))
  478. class User(Base, ComparableEntity):
  479. __tablename__ = 'users'
  480. id = Column('id', Integer, primary_key=True)
  481. name = Column('name', String(50))
  482. # this is not "valid" but we want to test that Address.id doesnt
  483. # get stuck into user's table
  484. adr_count = Address.id
  485. eq_(set(User.__table__.c.keys()), set(['id', 'name']))
  486. eq_(set(Address.__table__.c.keys()), set(['id', 'email', 'user_id']))
  487. def test_deferred(self):
  488. class User(Base, ComparableEntity):
  489. __tablename__ = 'users'
  490. id = Column(Integer, primary_key=True)
  491. name = sa.orm.deferred(Column(String(50)))
  492. Base.metadata.create_all()
  493. sess = create_session()
  494. sess.add(User(name='u1'))
  495. sess.flush()
  496. sess.expunge_all()
  497. u1 = sess.query(User).filter(User.name == 'u1').one()
  498. assert 'name' not in u1.__dict__
  499. def go():
  500. eq_(u1.name, 'u1')
  501. self.assert_sql_count(testing.db, go, 1)
  502. def test_synonym_inline(self):
  503. class User(Base, ComparableEntity):
  504. __tablename__ = 'users'
  505. id = Column('id', Integer, primary_key=True)
  506. _name = Column('name', String(50))
  507. def _set_name(self, name):
  508. self._name = "SOMENAME " + name
  509. def _get_name(self):
  510. return self._name
  511. name = sa.orm.synonym('_name',
  512. descriptor=property(_get_name, _set_name))
  513. Base.metadata.create_all()
  514. sess = create_session()
  515. u1 = User(name='someuser')
  516. eq_(u1.name, "SOMENAME someuser")
  517. sess.add(u1)
  518. sess.flush()
  519. eq_(sess.query(User).filter(User.name == "SOMENAME someuser").one(), u1)
  520. def test_synonym_no_descriptor(self):
  521. from sqlalchemy.orm.properties import ColumnProperty
  522. class CustomCompare(ColumnProperty.Comparator):
  523. __hash__ = None
  524. def __eq__(self, other):
  525. return self.__clause_element__() == other + ' FOO'
  526. class User(Base, ComparableEntity):
  527. __tablename__ = 'users'
  528. id = Column('id', Integer, primary_key=True)
  529. _name = Column('name', String(50))
  530. name = sa.orm.synonym('_name', comparator_factory=CustomCompare)
  531. Base.metadata.create_all()
  532. sess = create_session()
  533. u1 = User(name='someuser FOO')
  534. sess.add(u1)
  535. sess.flush()
  536. eq_(sess.query(User).filter(User.name == "someuser").one(), u1)
  537. def test_synonym_added(self):
  538. class User(Base, ComparableEntity):
  539. __tablename__ = 'users'
  540. id = Column('id', Integer, primary_key=True)
  541. _name = Column('name', String(50))
  542. def _set_name(self, name):
  543. self._name = "SOMENAME " + name
  544. def _get_name(self):
  545. return self._name
  546. name = property(_get_name, _set_name)
  547. User.name = sa.orm.synonym('_name', descriptor=User.name)
  548. Base.metadata.create_all()
  549. sess = create_session()
  550. u1 = User(name='someuser')
  551. eq_(u1.name, "SOMENAME someuser")
  552. sess.add(u1)
  553. sess.flush()
  554. eq_(sess.query(User).filter(User.name == "SOMENAME someuser").one(), u1)
  555. def test_reentrant_compile_via_foreignkey(self):
  556. class User(Base, ComparableEntity):
  557. __tablename__ = 'users'
  558. id = Column('id', Integer, primary_key=True)
  559. name = Column('name', String(50))
  560. addresses = relation("Address", backref="user")
  561. class Address(Base, ComparableEntity):
  562. __tablename__ = 'addresses'
  563. id = Column('id', Integer, primary_key=True)
  564. email = Column('email', String(50))
  565. user_id = Column('user_id', Integer, ForeignKey(User.id))
  566. # previous versions would force a re-entrant mapper compile
  567. # via the User.id inside the ForeignKey but this is no
  568. # longer the case
  569. sa.orm.compile_mappers()
  570. eq_(str(Address.user_id.property.columns[0].foreign_keys[0]), "ForeignKey('users.id')")
  571. Base.metadata.create_all()
  572. u1 = User(name='u1', addresses=[
  573. Address(email='one'),
  574. Address(email='two'),
  575. ])
  576. sess = create_session()
  577. sess.add(u1)
  578. sess.flush()
  579. sess.expunge_all()
  580. eq_(sess.query(User).all(), [User(name='u1', addresses=[
  581. Address(email='one'),
  582. Address(email='two'),
  583. ])])
  584. def test_relation_reference(self):
  585. class Address(Base, ComparableEntity):
  586. __tablename__ = 'addresses'
  587. id = Column('id', Integer, primary_key=True)
  588. email = Column('email', String(50))
  589. user_id = Column('user_id', Integer, ForeignKey('users.id'))
  590. class User(Base, ComparableEntity):
  591. __tablename__ = 'users'
  592. id = Column('id', Integer, primary_key=True)
  593. name = Column('name', String(50))
  594. addresses = relation("Address", backref="user",
  595. primaryjoin=id == Address.user_id)
  596. User.address_count = sa.orm.column_property(
  597. sa.select([sa.func.count(Address.id)]).
  598. where(Address.user_id == User.id).as_scalar())
  599. Base.metadata.create_all()
  600. u1 = User(name='u1', addresses=[
  601. Address(email='one'),
  602. Address(email='two'),
  603. ])
  604. sess = create_session()
  605. sess.add(u1)
  606. sess.flush()
  607. sess.expunge_all()
  608. eq_(sess.query(User).all(),
  609. [User(name='u1', address_count=2, addresses=[
  610. Address(email='one'),
  611. Address(email='two')])])
  612. def test_pk_with_fk_init(self):
  613. class Bar(Base):
  614. __tablename__ = 'bar'
  615. id = sa.Column(sa.Integer, sa.ForeignKey("foo.id"), primary_key=True)
  616. ex = sa.Column(sa.Integer, primary_key=True)
  617. class Foo(Base):
  618. __tablename__ = 'foo'
  619. id = sa.Column(sa.Integer, primary_key=True)
  620. bars = sa.orm.relation(Bar)
  621. assert Bar.__mapper__.primary_key[0] is Bar.__table__.c.id
  622. assert Bar.__mapper__.primary_key[1] is Bar.__table__.c.ex
  623. def test_with_explicit_autoloaded(self):
  624. meta = MetaData(testing.db)
  625. t1 = Table('t1', meta,
  626. Column('id', String(50), primary_key=True),
  627. Column('data', String(50)))
  628. meta.create_all()
  629. try:
  630. class MyObj(Base):
  631. __table__ = Table('t1', Base.metadata, autoload=True)
  632. sess = create_session()
  633. m = MyObj(id="someid", data="somedata")
  634. sess.add(m)
  635. sess.flush()
  636. eq_(t1.select().execute().fetchall(), [('someid', 'somedata')])
  637. finally:
  638. meta.drop_all()
  639. class DeclarativeInheritanceTest(DeclarativeTestBase):
  640. def test_custom_join_condition(self):
  641. class Foo(Base):
  642. __tablename__ = 'foo'
  643. id = Column('id', Integer, primary_key=True)
  644. class Bar(Foo):
  645. __tablename__ = 'bar'
  646. id = Column('id', Integer, primary_key=True)
  647. foo_id = Column('foo_id', Integer)
  648. __mapper_args__ = {'inherit_condition':foo_id==Foo.id}
  649. # compile succeeds because inherit_condition is honored
  650. compile_mappers()
  651. def test_joined(self):
  652. class Company(Base, ComparableEntity):
  653. __tablename__ = 'companies'
  654. id = Column('id', Integer, primary_key=True)
  655. name = Column('name', String(50))
  656. employees = relation("Person")
  657. class Person(Base, ComparableEntity):
  658. __tablename__ = 'people'
  659. id = Column('id', Integer, primary_key=True)
  660. company_id = Column('company_id', Integer,
  661. ForeignKey('companies.id'))
  662. name = Column('name', String(50))
  663. discriminator = Column('type', String(50))
  664. __mapper_args__ = {'polymorphic_on':discriminator}
  665. class Engineer(Person):
  666. __tablename__ = 'engineers'
  667. __mapper_args__ = {'polymorphic_identity':'engineer'}
  668. id = Column('id', Integer, ForeignKey('people.id'), primary_key=True)
  669. primary_language = Column('primary_language', String(50))
  670. class Manager(Person):
  671. __tablename__ = 'managers'
  672. __mapper_args__ = {'polymorphic_identity':'manager'}
  673. id = Column('id', Integer, ForeignKey('people.id'), primary_key=True)
  674. golf_swing = Column('golf_swing', String(50))
  675. Base.metadata.create_all()
  676. sess = create_session()
  677. c1 = Company(name="MegaCorp, Inc.", employees=[
  678. Engineer(name="dilbert", primary_language="java"),
  679. Engineer(name="wally", primary_language="c++"),
  680. Manager(name="dogbert", golf_swing="fore!")
  681. ])
  682. c2 = Company(name="Elbonia, Inc.", employees=[
  683. Engineer(name="vlad", primary_language="cobol")
  684. ])
  685. sess.add(c1)
  686. sess.add(c2)
  687. sess.flush()
  688. sess.expunge_all()
  689. eq_((sess.query(Company).
  690. filter(Company.employees.of_type(Engineer).
  691. any(Engineer.primary_language == 'cobol')).first()),
  692. c2)
  693. # ensure that the Manager mapper was compiled
  694. # with the Person id column as higher priority.
  695. # this ensures that "id" will get loaded from the Person row
  696. # and not the possibly non-present Manager row
  697. assert Manager.id.property.columns == [Person.__table__.c.id, Manager.__table__.c.id]
  698. # assert that the "id" column is available without a second load.
  699. # this would be the symptom of the previous step not being correct.
  700. sess.expunge_all()
  701. def go():
  702. assert sess.query(Manager).filter(Manager.name=='dogbert').one().id
  703. self.assert_sql_count(testing.db, go, 1)
  704. sess.expunge_all()
  705. def go():
  706. assert sess.query(Person).filter(Manager.name=='dogbert').one().id
  707. self.assert_sql_count(testing.db, go, 1)
  708. def test_add_subcol_after_the_fact(self):
  709. class Person(Base, ComparableEntity):
  710. __tablename__ = 'people'
  711. id = Column('id', Integer, primary_key=True, test_needs_autoincrement=True)
  712. name = Column('name', String(50))
  713. discriminator = Column('type', String(50))
  714. __mapper_args__ = {'polymorphic_on':discriminator}
  715. class Engineer(Person):
  716. __tablename__ = 'engineers'
  717. __mapper_args__ = {'polymorphic_identity':'engineer'}
  718. id = Column('id', Integer, ForeignKey('people.id'), primary_key=True)
  719. Engineer.primary_language = Column('primary_language', String(50))
  720. Base.metadata.create_all()
  721. sess = create_session()
  722. e1 = Engineer(primary_language='java', name='dilbert')
  723. sess.add(e1)
  724. sess.flush()
  725. sess.expunge_all()
  726. eq_(sess.query(Person).first(),
  727. Engineer(primary_language='java', name='dilbert')
  728. )
  729. def test_subclass_mixin(self):
  730. class Person(Base, ComparableEntity):
  731. __tablename__ = 'people'
  732. id = Column('id', Integer, primary_key=True)
  733. name = Column('name', String(50))
  734. discriminator = Column('type', String(50))
  735. __mapper_args__ = {'polymorphic_on':discriminator}
  736. class MyMixin(object):
  737. pass
  738. class Engineer(MyMixin, Person):
  739. __tablename__ = 'engineers'
  740. __mapper_args__ = {'polymorphic_identity':'engineer'}
  741. id = Column('id', Integer, ForeignKey('people.id'), primary_key=True)
  742. primary_language = Column('primary_language', String(50))
  743. assert class_mapper(Engineer).inherits is class_mapper(Person)
  744. def test_with_undefined_foreignkey(self):
  745. class Parent(Base):
  746. __tablename__ = 'parent'
  747. id = Column('id', Integer, primary_key=True)
  748. tp = Column('type', String(50))
  749. __mapper_args__ = dict(polymorphic_on = tp)
  750. class Child1(Parent):
  751. __tablename__ = 'child1'
  752. id = Column('id', Integer, ForeignKey('parent.id'), primary_key=True)
  753. related_child2 = Column('c2', Integer, ForeignKey('child2.id'))
  754. __mapper_args__ = dict(polymorphic_identity = 'child1')
  755. # no exception is raised by the ForeignKey to "child2" even though
  756. # child2 doesn't exist yet
  757. class Child2(Parent):
  758. __tablename__ = 'child2'
  759. id = Column('id', Integer, ForeignKey('parent.id'), primary_key=True)
  760. related_child1 = Column('c1', Integer)
  761. __mapper_args__ = dict(polymorphic_identity = 'child2')
  762. sa.orm.compile_mappers() # no exceptions here
  763. def test_single_colsonbase(self):
  764. """test single inheritance where all the columns are on the base class."""
  765. class Company(Base, ComparableEntity):
  766. __tablename__ = 'companies'
  767. id = Column('id', Integer, primary_key=True)
  768. name = Column('name', String(50))
  769. employees = relation("Person")
  770. class Person(Base, ComparableEntity):
  771. __tablename__ = 'people'
  772. id = Column('id', Integer, primary_key=True)
  773. company_id = Column('company_id', Integer,
  774. ForeignKey('companies.id'))
  775. name = Column('name', String(50))
  776. discriminator = Column('type', String(50))
  777. primary_language = Column('primary_language', String(50))
  778. golf_swing = Column('golf_swing', String(50))
  779. __mapper_args__ = {'polymorphic_on':discriminator}
  780. class Engineer(Person):
  781. __mapper_args__ = {'polymorphic_identity':'engineer'}
  782. class Manager(Person):
  783. __mapper_args__ = {'polymorphic_identity':'manager'}
  784. Base.metadata.create_all()
  785. sess = create_session()
  786. c1 = Company(name="MegaCorp, Inc.", employees=[
  787. Engineer(name="dilbert", primary_language="java"),
  788. Engineer(name="wally", primary_language="c++"),
  789. Manager(name="dogbert", golf_swing="fore!")
  790. ])
  791. c2 = Company(name="Elbonia, Inc.", employees=[
  792. Engineer(name="vlad", primary_language="cobol")
  793. ])
  794. sess.add(c1)
  795. sess.add(c2)
  796. sess.flush()
  797. sess.expunge_all()
  798. eq_((sess.query(Person).
  799. filter(Engineer.primary_language == 'cobol').first()),
  800. Engineer(name='vlad'))
  801. eq_((sess.query(Company).
  802. filter(Company.employees.of_type(Engineer).
  803. any(Engineer.primary_language == 'cobol')).first()),
  804. c2)
  805. def test_single_colsonsub(self):
  806. """test single inheritance where the columns are local to their class.
  807. this is a newer usage.
  808. """
  809. class Company(Base, ComparableEntity):
  810. __tablename__ = 'companies'
  811. id = Column('id', Integer, primary_key=True)
  812. name = Column('name', String(50))
  813. employees = relation("Person")
  814. class Person(Base, ComparableEntity):
  815. __tablename__ = 'people'
  816. id = Column(Integer, primary_key=True)
  817. company_id = Column(Integer,
  818. ForeignKey('companies.id'))
  819. name = Column(String(50))
  820. discriminator = Column('type', String(50))
  821. __mapper_args__ = {'polymorphic_on':discriminator}
  822. class Engineer(Person):
  823. __mapper_args__ = {'polymorphic_identity':'engineer'}
  824. primary_language = Column(String(50))
  825. class Manager(Person):
  826. __mapper_args__ = {'polymorphic_identity':'manager'}
  827. golf_swing = Column(String(50))
  828. # we have here a situation that is somewhat unique.
  829. # the Person class is mapped to the "people" table, but it
  830. # was mapped when the table did not include the "primary_language"
  831. # or "golf_swing" columns. declarative will also manipulate
  832. # the exclude_properties collection so that sibling classes
  833. # don't cross-pollinate.
  834. assert Person.__table__.c.company_id
  835. assert Person.__table__.c.golf_swing
  836. assert Person.__table__.c.primary_language
  837. assert Engineer.primary_language
  838. assert Manager.golf_swing
  839. assert not hasattr(Person, 'primary_language')
  840. assert not hasattr(Person, 'golf_swing')
  841. assert not hasattr(Engineer, 'golf_swing')
  842. assert not hasattr(Manager, 'primary_language')
  843. Base.metadata.create_all()
  844. sess = create_session()
  845. e1 = Engineer(name="dilbert", primary_language="java")
  846. e2 = Engineer(name="wally", primary_language="c++")
  847. m1 = Manager(name="dogbert", golf_swing="fore!")
  848. c1 = Company(name="MegaCorp, Inc.", employees=[e1, e2, m1])
  849. e3 =Engineer(name="vlad", primary_language="cobol")
  850. c2 = Company(name="Elbonia, Inc.", employees=[e3])
  851. sess.add(c1)
  852. sess.add(c2)
  853. sess.flush()
  854. sess.expunge_all()
  855. eq_((sess.query(Person).
  856. filter(Engineer.primary_language == 'cobol').first()),
  857. Engineer(name='vlad'))
  858. eq_((sess.query(Company).
  859. filter(Company.employees.of_type(Engineer).
  860. any(Engineer.primary_language == 'cobol')).first()),
  861. c2)
  862. eq_(
  863. sess.query(Engineer).filter_by(primary_language='cobol').one(),
  864. Engineer(name="vlad", primary_language="cobol")
  865. )
  866. def test_joined_from_single(self):
  867. class Company(Base, ComparableEntity):
  868. __tablename__ = 'companies'
  869. id = Column('id', Integer, primary_key=True)
  870. name = Column('name', String(50))
  871. employees = relation("Person")
  872. class Person(Base, ComparableEntity):
  873. __tablename__ = 'people'
  874. id = Column(Integer, primary_key=True)
  875. company_id = Column(Integer, ForeignKey('companies.id'))
  876. name = Column(String(50))
  877. discriminator = Column('type', String(50))
  878. __mapper_args__ = {'polymorphic_on':discriminator}
  879. class Manager(Person):
  880. __mapper_args__ = {'polymorphic_identity':'manager'}
  881. golf_swing = Column(String(50))
  882. class Engineer(Person):
  883. __tablename__ = 'engineers'
  884. __mapper_args__ = {'polymorphic_identity':'engineer'}
  885. id = Column(Integer, ForeignKey('people.id'), primary_key=True)
  886. primary_language = Column(String(50))
  887. assert Person.__table__.c.golf_swing
  888. assert not Person.__table__.c.has_key('primary_language')
  889. assert Engineer.__table__.c.primary_language
  890. assert Engineer.primary_language
  891. assert Manager.golf_swing
  892. assert not hasattr(Person, 'primary_language')
  893. assert not hasattr(Person, 'golf_swing')
  894. assert not hasattr(Engineer, 'golf_swing')
  895. assert not hasattr(Manager, 'primary_language')
  896. Base.metadata.create_all()
  897. sess = create_session()
  898. e1 = Engineer(name="dilbert", primary_language="java")
  899. e2 = Engineer(name="wally", primary_language="c++")
  900. m1 = Manager(name="dogbert", golf_swing="fore!")
  901. c1 = Company(name="MegaCorp, Inc.", employees=[e1, e2, m1])
  902. e3 =Engineer(name="vlad", primary_language="cobol")
  903. c2 = Company(name="Elbonia, Inc.", employees=[e3])
  904. sess.add(c1)
  905. sess.add(c2)
  906. sess.flush()
  907. sess.expunge_all()
  908. eq_((sess.query(Person).with_polymorphic(Engineer).
  909. filter(Engineer.primary_language == 'cobol').first()),
  910. Engineer(name='vlad'))
  911. eq_((sess.query(Company).
  912. filter(Company.employees.of_type(Engineer).
  913. any(Engineer.primary_language == 'cobol')).first()),
  914. c2)
  915. eq_(
  916. sess.query(Engineer).filter_by(primary_language='cobol').one(),
  917. Engineer(name="vlad", primary_language="cobol")
  918. )
  919. def test_add_deferred(self):
  920. class Person(Base, ComparableEntity):
  921. __tablename__ = 'people'
  922. id = Column('id', Integer, primary_key=True)
  923. Person.name = deferred(Column(String(10)))
  924. Base.metadata.create_all()
  925. sess = create_session()
  926. p = Person(name='ratbert')
  927. sess.add(p)
  928. sess.flush()
  929. sess.expunge_all()
  930. eq_(
  931. sess.query(Person).all(),
  932. [
  933. Person(name='ratbert')
  934. ]
  935. )
  936. person = sess.query(Person).filter(Person.name == 'ratbert').one()
  937. assert 'name' not in person.__dict__
  938. def test_single_fksonsub(self):
  939. """test single inheritance with a foreign key-holding column on a subclass.
  940. """
  941. class Person(Base, ComparableEntity):
  942. __tablename__ = 'people'
  943. id = Column(Integer, primary_key=True)
  944. name = Column(String(50))
  945. discriminator = Column('type', String(50))
  946. __mapper_args__ = {'polymorphic_on':discriminator}
  947. class Engineer(Person):
  948. __mapper_args__ = {'polymorphic_identity':'engineer'}
  949. primary_language_id = Column(Integer, ForeignKey('languages.id'))
  950. primary_language = relation("Language")
  951. class Language(Base, ComparableEntity):
  952. __tablename__ = 'languages'
  953. id = Column(Integer, primary_key=True)
  954. name = Column(String(50))
  955. assert not hasattr(Person, 'primary_language_id')
  956. Base.metadata.create_all()
  957. sess = create_session()
  958. java, cpp, cobol = Language(name='java'),Language(name='cpp'), Language(name='cobol')
  959. e1 = Engineer(name="dilbert", primary_language=java)
  960. e2 = Engineer(name="wally", primary_language=cpp)
  961. e3 =Engineer(name="vlad", primary_language=cobol)
  962. sess.add_all([e1, e2, e3])
  963. sess.flush()
  964. sess.expunge_all()
  965. eq_((sess.query(Person).
  966. filter(Engineer.primary_language.has(Language.name=='cobol')).first()),
  967. Engineer(name='vlad', primary_language=Language(name='cobol')))
  968. eq_(
  969. sess.query(Engineer).filter(Engineer.primary_language.has(Language.name=='cobol')).one(),
  970. Engineer(name="vlad", primary_language=Language(name='cobol'))
  971. )
  972. eq_(
  973. sess.query(Person).join(Engineer.primary_language).order_by(Language.name).all(),
  974. [
  975. Engineer(name='vlad', primary_language=Language(name='cobol')),
  976. Engineer(name='wally', primary_language=Language(name='cpp')),
  977. Engineer(name='dilbert', primary_language=Language(name='java')),
  978. ]
  979. )
  980. def test_single_three_levels(self):
  981. class Person(Base, ComparableEntity):
  982. __tablename__ = 'people'
  983. id = Column(Integer, primary_key=True)
  984. name = Column(String(50))
  985. discriminator = Column('type', String(50))
  986. __mapper_args__ = {'polymorphic_on':discriminator}
  987. class Engineer(Person):
  988. __mapper_args__ = {'polymorphic_identity':'engineer'}
  989. primary_language = Column(String(50))
  990. class JuniorEngineer(Engineer):
  991. __mapper_args__ = {'polymorphic_identity':'junior_engineer'}
  992. nerf_gun = Column(String(50))
  993. class Manager(Person):
  994. __mapper_args__ = {'polymorphic_identity':'manager'}
  995. golf_swing = Column(String(50))
  996. assert JuniorEngineer.nerf_gun
  997. assert JuniorEngineer.primary_language
  998. assert JuniorEngineer.name
  999. assert Manager.golf_swing
  1000. assert Engineer.primary_language
  1001. assert not hasattr(Engineer, 'golf_swing')
  1002. assert not hasattr(Engineer, 'nerf_gun')
  1003. assert not hasattr(Manager, 'nerf_gun')
  1004. assert not hasattr(Manager, 'primary_language')
  1005. def test_single_no_special_cols(self):
  1006. class Person(Base, ComparableEntity):
  1007. __tablename__ = 'people'
  1008. id = Column('id', Integer, primary_key=True)
  1009. name = Column('name', String(50))
  1010. discriminator = Column('type', String(50))
  1011. __mapper_args__ = {'polymorphic_on':discriminator}
  1012. def go():
  1013. class Engineer(Person):
  1014. __mapper_args__ = {'polymorphic_identity':'engineer'}
  1015. primary_language = Column('primary_language', String(50))
  1016. foo_bar = Column(Integer, primary_key=True)
  1017. assert_raises_message(sa.exc.ArgumentError, "place primary key", go)
  1018. def test_single_no_table_args(self):
  1019. class Person(Base, ComparableEntity):
  1020. __tablename__ = 'people'
  1021. id = Column('id', Integer, primary_key=True)
  1022. name = Column('name', String(50))
  1023. discriminator = Column('type', String(50))
  1024. __mapper_args__ = {'polymorphic_on':discriminator}
  1025. def go():
  1026. class Engineer(Person):
  1027. __mapper_args__ = {'polymorphic_identity':'engineer'}
  1028. primary_language = Column('primary_language', String(50))
  1029. __table_args__ = ()
  1030. assert_raises_message(sa.exc.ArgumentError, "place __table_args__", go)
  1031. def test_concrete(self):
  1032. engineers = Table('engineers', Base.metadata,
  1033. Column('id', Integer, primary_key=True),
  1034. Column('name', String(50)),
  1035. Column('primary_language', String(50))
  1036. )
  1037. managers = Table('managers', Base.metadata,
  1038. Column('id', Integer, primary_key=True),
  1039. Column('name', String(50)),
  1040. Column('golf_swing', String(50))
  1041. )
  1042. punion = polymorphic_union({
  1043. 'engineer':engineers,
  1044. 'manager':managers
  1045. }, 'type', 'punion')
  1046. class Person(Base, ComparableEntity):
  1047. __table__ = punion
  1048. __mapper_args__ = {'polymorphic_on':punion.c.type}
  1049. class Engineer(Person):
  1050. __table__ = engineers
  1051. __mapper_args__ = {'polymorphic_identity':'engineer', 'concrete':True}
  1052. class Manager(Person):
  1053. __table__ = managers
  1054. __mapper_args__ = {'polymorphic_identity':'manager', 'concrete':True}
  1055. Base.metadata.create_all()
  1056. sess = create_session()
  1057. e1 = Engineer(name="dilbert", primary_language="java")
  1058. e2 = Engineer(name="wally", primary_language="c++")
  1059. m1 = Manager(name="dogbert", golf_swing="fore!")
  1060. e3 = Engineer(name="vlad", primary_language="cobol")
  1061. sess.add_all([e1, e2, m1, e3])
  1062. sess.flush()
  1063. sess.expunge_all()
  1064. eq_(
  1065. sess.query(Person).order_by(Person.name).all(),
  1066. [
  1067. Engineer(name='dilbert'), Manager(name='dogbert'),
  1068. Engineer(name='vlad'), Engineer(name='wally')
  1069. ]
  1070. )
  1071. def _produce_test(inline, stringbased):
  1072. class ExplicitJoinTest(MappedTest):
  1073. @classmethod
  1074. def define_tables(cls, metadata):
  1075. global User, Address
  1076. Base = decl.declarative_base(metadata=metadata)
  1077. class User(Base, ComparableEntity):
  1078. __tablename__ = 'users'
  1079. id = Column(Integer, primary_key=True)
  1080. name = Column(String(50))
  1081. class Address(Base, ComparableEntity):
  1082. __tablename__ = 'addresses'
  1083. id = Column(Integer, primary_key=True)

Large files files are truncated, but you can click here to view the full file