def test_orphan(self): """test that an entity can have two parent delete-orphan cascades, and is detected as an orphan when saved without a parent.""" class Address(_fixtures.Base): pass class Home(_fixtures.Base): pass class Business(_fixtures.Base): pass mapper(Address, addresses) mapper(Home, homes, properties={"address": relation(Address, cascade="all,delete-orphan", single_parent=True)}) mapper( Business, businesses, properties={"address": relation(Address, cascade="all,delete-orphan", single_parent=True)}, ) session = create_session() a1 = Address() session.add(a1) try: session.flush() assert False except orm_exc.FlushError, e: assert True
def test_delete_orphan_cascades(self): mapper( A, a, properties={ # if no backref here, delete-orphan failed until [ticket:427] was # fixed "bs": relation(B, secondary=atob, cascade="all, delete-orphan", single_parent=True) }, ) mapper(B, b, properties={"cs": relation(C, cascade="all, delete-orphan")}) mapper(C, c) sess = create_session() b1 = B(data="b1", cs=[C(data="c1")]) a1 = A(data="a1", bs=[b1]) sess.add(a1) sess.flush() a1.bs.remove(b1) sess.flush() assert atob.count().scalar() == 0 assert b.count().scalar() == 0 assert a.count().scalar() == 1 assert c.count().scalar() == 0
def test_one(self): p_m = mapper(Part, parts) mapper(InheritedPart, inherited_part, properties=dict( part=relation(Part, lazy=False))) d_m = mapper(Design, design, properties=dict( inheritedParts=relation(InheritedPart, cascade="all, delete-orphan", backref="design"))) mapper(DesignType, design_types) d_m.add_property( "type", relation(DesignType, lazy=False, backref="designs")) p_m.add_property( "design", relation( Design, lazy=False, backref=backref("parts", cascade="all, delete-orphan"))) d = Design() sess = create_session() sess.add(d) sess.flush() sess.expunge_all() x = sess.query(Design).get(1) x.inheritedParts
def test_pending_expunge(self): class Order(_fixtures.Base): pass class Item(_fixtures.Base): pass class Attribute(_fixtures.Base): pass mapper(Attribute, attributes) mapper( Item, items, properties=dict(attributes=relation(Attribute, cascade="all,delete-orphan", backref="item")) ) mapper(Order, orders, properties=dict(items=relation(Item, cascade="all,delete-orphan", backref="order"))) s = create_session() order = Order(name="order1") s.add(order) attr = Attribute(name="attr1") item = Item(name="item1", attributes=[attr]) order.items.append(item) order.items.remove(item) assert item not in s assert attr not in s s.flush() assert orders.count().scalar() == 1 assert items.count().scalar() == 0 assert attributes.count().scalar() == 0
def test_bidirectional(self): """tests a many-to-many backrefs""" Place.mapper = mapper(Place, place) Transition.mapper = mapper(Transition, transition, properties = dict( inputs = relation(Place.mapper, place_output, lazy=True, backref='inputs'), outputs = relation(Place.mapper, place_input, lazy=True, backref='outputs'), ) ) t1 = Transition('transition1') t2 = Transition('transition2') t3 = Transition('transition3') p1 = Place('place1') p2 = Place('place2') p3 = Place('place3') t1.inputs.append(p1) t1.inputs.append(p2) t1.outputs.append(p3) t2.inputs.append(p1) p2.inputs.append(t2) p3.inputs.append(t2) p1.outputs.append(t1) sess = create_session() sess.add_all((t1, t2, t3,p1, p2, p3)) sess.flush() self.assert_result([t1], Transition, {'outputs': (Place, [{'name':'place3'}, {'name':'place1'}])}) self.assert_result([p2], Place, {'inputs': (Transition, [{'name':'transition1'},{'name':'transition2'}])})
def testone(self): """ Tests eager load of a many-to-one attached to a one-to-many. this testcase illustrated the bug, which is that when the single Company is loaded, no further processing of the rows occurred in order to load the Company's second Address object. """ mapper(Address, addresses) mapper(Company, companies, properties={"addresses": relation(Address, lazy=False)}) mapper(Invoice, invoices, properties={"company": relation(Company, lazy=False)}) a1 = Address(address="a1 address") a2 = Address(address="a2 address") c1 = Company(company_name="company 1", addresses=[a1, a2]) i1 = Invoice(date=datetime.datetime.now(), company=c1) session = create_session() session.add(i1) session.flush() company_id = c1.company_id invoice_id = i1.invoice_id session.clear() c = session.query(Company).get(company_id) session.clear() i = session.query(Invoice).get(invoice_id) eq_(c, i.company)
def define_tables(self, metadata): global User, Address Base = decl.declarative_base(metadata=metadata) class User(Base, ComparableEntity): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(50)) class Address(Base, ComparableEntity): __tablename__ = 'addresses' id = Column(Integer, primary_key=True) email = Column(String(50)) user_id = Column(Integer, ForeignKey('users.id')) if inline: if stringbased: user = relation("User", primaryjoin="User.id==Address.user_id", backref="addresses") else: user = relation(User, primaryjoin=User.id==user_id, backref="addresses") if not inline: compile_mappers() if stringbased: Address.user = relation("User", primaryjoin="User.id==Address.user_id", backref="addresses") else: Address.user = relation(User, primaryjoin=User.id==Address.user_id, backref="addresses")
def test_double(self): """test that a mapper can have two eager relations to the same table, via two different association tables. aliases are required.""" Place.mapper = mapper(Place, place, properties = { 'thingies':relation(mapper(PlaceThingy, place_thingy), lazy=False) }) Transition.mapper = mapper(Transition, transition, properties = dict( inputs = relation(Place.mapper, place_output, lazy=False), outputs = relation(Place.mapper, place_input, lazy=False), ) ) tran = Transition('transition1') tran.inputs.append(Place('place1')) tran.outputs.append(Place('place2')) tran.outputs.append(Place('place3')) sess = create_session() sess.add(tran) sess.flush() sess.expunge_all() r = sess.query(Transition).all() self.assert_unordered_result(r, Transition, {'name': 'transition1', 'inputs': (Place, [{'name':'place1'}]), 'outputs': (Place, [{'name':'place2'}, {'name':'place3'}]) })
def test_non_orphan(self): """test that an entity can have two parent delete-orphan cascades, and persists normally.""" class Address(_fixtures.Base): pass class Home(_fixtures.Base): pass class Business(_fixtures.Base): pass mapper(Address, addresses) mapper(Home, homes, properties={"address": relation(Address, cascade="all,delete-orphan", single_parent=True)}) mapper( Business, businesses, properties={"address": relation(Address, cascade="all,delete-orphan", single_parent=True)}, ) session = create_session() h1 = Home(description="home1", address=Address(street="address1")) b1 = Business(description="business1", address=Address(street="address2")) session.add_all((h1, b1)) session.flush() session.expunge_all() eq_(session.query(Home).get(h1.id), Home(description="home1", address=Address(street="address1"))) eq_(session.query(Business).get(b1.id), Business(description="business1", address=Address(street="address2")))
def setup_mappers(self): mapper(T1, t1, properties={ 't2':relation(T2, primaryjoin=t1.c.t2id == t2.c.id)}) mapper(T2, t2, properties={ 't1':relation(T1, primaryjoin=t2.c.t1id == t1.c.id)}) mapper(T3, t3, properties={ 't1':relation(T1), 't2':relation(T2)})
def setup_mappers(self): mapper(Extra, extra) mapper(Pref, prefs, properties=dict(extra=relation(Extra, cascade="all, delete"))) mapper( User, users, properties=dict(pref=relation(Pref, lazy=False, cascade="all, delete-orphan", single_parent=True)), )
def test_error(self): mapper(Place, place, properties={ 'transitions':relation(Transition, secondary=place_input, backref='places') }) mapper(Transition, transition, properties={ 'places':relation(Place, secondary=place_input, backref='transitions') }) self.assertRaisesMessage(sa.exc.ArgumentError, "Error creating backref", sa.orm.compile_mappers)
def testtwo(self): """The original testcase that includes various complicating factors""" mapper(Phone, phone_numbers) mapper(Address, addresses, properties={ 'phones': relation(Phone, lazy=False, backref='address', order_by=phone_numbers.c.phone_id)}) mapper(Company, companies, properties={ 'addresses': relation(Address, lazy=False, backref='company', order_by=addresses.c.address_id)}) mapper(Item, items) mapper(Invoice, invoices, properties={ 'items': relation(Item, lazy=False, backref='invoice', order_by=items.c.item_id), 'company': relation(Company, lazy=False, backref='invoices')}) c1 = Company(company_name='company 1', addresses=[ Address(address='a1 address', phones=[Phone(type='home', number='1111'), Phone(type='work', number='22222')]), Address(address='a2 address', phones=[Phone(type='home', number='3333'), Phone(type='work', number='44444')]) ]) session = create_session() session.add(c1) session.flush() company_id = c1.company_id session.expunge_all() a = session.query(Company).get(company_id) # set up an invoice i1 = Invoice(date=datetime.datetime.now(), company=a) item1 = Item(code='aaaa', qty=1, invoice=i1) item2 = Item(code='bbbb', qty=2, invoice=i1) item3 = Item(code='cccc', qty=3, invoice=i1) session.flush() invoice_id = i1.invoice_id session.expunge_all() c = session.query(Company).get(company_id) session.expunge_all() i = session.query(Invoice).get(invoice_id) eq_(c, i.company)
def setup_mappers(self): mapper(C2, t2, properties={ 'c1s': relation(C1, primaryjoin=t2.c.c1 == t1.c.c2, uselist=True)}) mapper(C1, t1, properties={ 'c2s': relation(C2, primaryjoin=t1.c.c1 == t2.c.c2, uselist=True), 'data': relation(mapper(C1Data, t1_data))})
def setup_mappers(self): mapper(Address, addresses) mapper(User, users, properties = dict( addresses = relation(Address, cascade="all, delete-orphan", backref="user"), orders = relation( mapper(Order, orders), cascade="all, delete-orphan") )) mapper(Dingaling,dingalings, properties={ 'address':relation(Address) })
def setup_mappers(self): mapper(T1, t1, properties=dict(t2=relation(T2, cascade="all, delete-orphan", single_parent=True))) mapper( T2, t2, properties=dict( t3=relation(T3, cascade="all, delete-orphan", single_parent=True, backref=backref("t2", uselist=False)) ), ) mapper(T3, t3)
def test_nesting_with_functions(self): mapper(Data, datas) mapper(Foo, foo, properties={"data": relation(Data, backref=backref("foo", uselist=False))}) mapper(Stat, stats, properties={"data": relation(Data)}) session = create_session() data = [Data(a=x) for x in range(5)] session.add_all(data) session.add_all( ( Stat(data=data[0], somedata=1), Stat(data=data[1], somedata=2), Stat(data=data[2], somedata=3), Stat(data=data[3], somedata=4), Stat(data=data[4], somedata=5), Stat(data=data[0], somedata=6), Stat(data=data[1], somedata=7), Stat(data=data[2], somedata=8), Stat(data=data[3], somedata=9), Stat(data=data[4], somedata=10), ) ) session.flush() arb_data = sa.select( [stats.c.data_id, sa.func.max(stats.c.somedata).label("max")], stats.c.data_id <= 5, group_by=[stats.c.data_id], ).alias("arb") arb_result = arb_data.execute().fetchall() # order the result list descending based on 'max' arb_result.sort(key=lambda a: a["max"], reverse=True) # extract just the "data_id" from it arb_result = [row["data_id"] for row in arb_result] # now query for Data objects using that above select, adding the # "order by max desc" separately q = ( session.query(Data) .options(sa.orm.eagerload("foo")) .select_from(datas.join(arb_data, arb_data.c.data_id == datas.c.id)) .order_by(sa.desc(arb_data.c.max)) .limit(10) ) # extract "data_id" from the list of result objects verify_result = [d.id for d in q] eq_(verify_result, arb_result)
def setup_mappers(self): # set up bi-directional eager loads mapper(Left, left) mapper(Right, right) mapper(Middle, middle, properties=dict( left=relation(Left, lazy=False, backref=backref('middle',lazy=False)), right=relation(Right, lazy=False, backref=backref('middle', lazy=False)))),
def setup_mappers(self): mapper(Account, accounts) mapper(Transaction, transactions) mapper(Entry, entries, properties=dict( account=relation(Account, uselist=False, backref=backref('entries', lazy=True, order_by=entries.c.entry_id)), transaction=relation(Transaction, uselist=False, backref=backref('entries', lazy=False, order_by=entries.c.entry_id))))
def setup_mappers(self): items, item_keywords, keywords = self.tables.get_all( 'items', 'item_keywords', 'keywords') mapper(Keyword, keywords) mapper(KeywordAssociation, item_keywords, properties={ 'keyword':relation(Keyword, lazy=False)}, primary_key=[item_keywords.c.item_id, item_keywords.c.keyword_id], order_by=[item_keywords.c.data]) mapper(Item, items, properties={ 'keywords' : relation(KeywordAssociation, cascade="all, delete-orphan") })
def testpostupdate_m2o(self): """A cycle between two rows, with a post_update on the many-to-one""" mapper(Ball, ball) mapper(Person, person, properties=dict( balls=relation(Ball, primaryjoin=ball.c.person_id == person.c.id, remote_side=ball.c.person_id, post_update=False, cascade="all, delete-orphan"), favorite=relation(Ball, primaryjoin=person.c.favorite_ball_id == ball.c.id, remote_side=person.c.favorite_ball_id, post_update=True))) b = Ball(data='some data') p = Person(data='some data') p.balls.append(b) p.balls.append(Ball(data='some data')) p.balls.append(Ball(data='some data')) p.balls.append(Ball(data='some data')) p.favorite = b sess = create_session() sess.add(b) sess.add(p) self.assert_sql_execution( testing.db, sess.flush, RegexSQL("^INSERT INTO person", {'data':'some data'}), RegexSQL("^INSERT INTO ball", lambda c: {'person_id':p.id, 'data':'some data'}), RegexSQL("^INSERT INTO ball", lambda c: {'person_id':p.id, 'data':'some data'}), RegexSQL("^INSERT INTO ball", lambda c: {'person_id':p.id, 'data':'some data'}), RegexSQL("^INSERT INTO ball", lambda c: {'person_id':p.id, 'data':'some data'}), ExactSQL("UPDATE person SET favorite_ball_id=:favorite_ball_id " "WHERE person.id = :person_id", lambda ctx:{'favorite_ball_id':p.favorite.id, 'person_id':p.id} ), ) sess.delete(p) self.assert_sql_execution( testing.db, sess.flush, ExactSQL("UPDATE person SET favorite_ball_id=:favorite_ball_id " "WHERE person.id = :person_id", lambda ctx: {'person_id': p.id, 'favorite_ball_id': None}), ExactSQL("DELETE FROM ball WHERE ball.id = :id", None), # lambda ctx:[{'id': 1L}, {'id': 4L}, {'id': 3L}, {'id': 2L}]) ExactSQL("DELETE FROM person WHERE person.id = :id", lambda ctx:[{'id': p.id}]) )
def test_flush(self): mapper(A, a, properties={ 'cs':relation(C, primaryjoin=a.c.cid==c.c.id)}) mapper(B, b, inherits=A, inherit_condition=b.c.id == a.c.id) mapper(C, c, properties={ 'arel':relation(A, primaryjoin=a.c.id == c.c.aid)}) sess = create_session() bobj = B() sess.add(bobj) cobj = C() sess.add(cobj) sess.flush()
def test_refresh2(self): """test a hang condition that was occuring on expire/refresh""" s = create_session() mapper(Address, addresses) mapper(User, users, properties=dict(addresses=relation( Address, cascade="all, delete-orphan", lazy=False))) u = User() u.name = 'Justin' a = Address(id=10, email_address='lala') u.addresses.append(a) s.add(u) s.flush() s.clear() u = s.query(User).filter(User.name == 'Justin').one() s.expire(u) assert u.name == 'Justin' s.refresh(u)
def test_onetomany(self): mapper(Node, nodes, properties={ 'children': relation(Node, backref=sa.orm.backref('parentnode', remote_side=nodes.c.name, passive_updates=False), passive_updates=False) }) sess = create_session() n1 = Node(name='n1') n1.children.append(Node(name='n11')) n1.children.append(Node(name='n12')) n1.children.append(Node(name='n13')) sess.add(n1) sess.flush() n1.name = 'new n1' sess.flush() eq_(n1.children[1].parent, 'new n1') eq_(['new n1', 'new n1', 'new n1'], [ n.parent for n in sess.query(Node).filter( Node.name.in_(['n11', 'n12', 'n13'])) ])
def test_nested_joins(self): # this is testing some subtle column resolution stuff, # concerning corresponding_column() being extremely accurate # as well as how mapper sets up its column properties mapper(Task_Type, task_type) tsk_cnt_join = sa.outerjoin(prj, task, task.c.prj_id == prj.c.id) j = sa.outerjoin(task, msg, task.c.id == msg.c.task_id) jj = sa.select([ task.c.id.label('task_id'), sa.func.count(msg.c.id).label('props_cnt') ], from_obj=[j], group_by=[task.c.id]).alias('prop_c_s') jjj = sa.join(task, jj, task.c.id == jj.c.task_id) mapper(Joined, jjj, properties=dict(type=relation(Task_Type, lazy=False))) session = create_session() eq_( session.query(Joined).limit(10).offset(0).one(), Joined(id=1, title=u'task 1', props_cnt=0))
def test_basic(self): mapper(Employee, employees) mapper(Department, departments, properties=dict(employees=relation( Employee, lazy=False, backref='department'))) d1 = Department(name='One') for e in 'Jim', 'Jack', 'John', 'Susan': d1.employees.append(Employee(name=e)) d2 = Department(name='Two') for e in 'Joe', 'Bob', 'Mary', 'Wally': d2.employees.append(Employee(name=e)) sess = create_session() sess.add_all((d1, d2)) sess.flush() q = (sess.query(Department).join('employees').filter( Employee.name.startswith('J')).distinct().order_by( [sa.desc(Department.name)])) eq_(q.count(), 2) assert q[0] is d2
def test_one_to_one_cascade(self): mapper(User, users, properties={ 'address':relation(mapper(Address, addresses),uselist = False) }) on_load = self.on_load_tracker(User) self.on_load_tracker(Address, on_load) sess = create_session() u = User() u.id = 7 u.name = "fred" a1 = Address() a1.email_address='*****@*****.**' u.address = a1 sess.add(u) sess.flush() eq_(on_load.called, 0) sess2 = create_session() u2 = sess2.query(User).get(7) eq_(on_load.called, 1) u2.name = 'fred2' u2.address.email_address = '*****@*****.**' eq_(on_load.called, 2) u3 = sess.merge(u2) eq_(on_load.called, 2) assert u3 is u
def test_basic(self): class A(_fixtures.Base): pass class B(_fixtures.Base): pass mapper(A, table_a, properties={ 'bs':relation(B, cascade="all, delete-orphan") }) mapper(B, table_b) a1 = A(name='a1', bs=[B(name='b1'), B(name='b2'), B(name='b3')]) sess = create_session() sess.add(a1) sess.flush() sess.clear() eq_(sess.query(A).get(a1.id), A(name='a1', bs=[B(name='b1'), B(name='b2'), B(name='b3')])) a1 = sess.query(A).get(a1.id) assert not class_mapper(B)._is_orphan( attributes.instance_state(a1.bs[0])) a1.bs[0].foo='b2modified' a1.bs[1].foo='b3modified' sess.flush() sess.clear() eq_(sess.query(A).get(a1.id), A(name='a1', bs=[B(name='b1'), B(name='b2'), B(name='b3')]))
def test_dontload_with_eager(self): """ This test illustrates that with dont_load=True, we can't just copy the committed_state of the merged instance over; since it references collection objects which themselves are to be merged. This committed_state would instead need to be piecemeal 'converted' to represent the correct objects. However, at the moment I'd rather not support this use case; if you are merging with dont_load=True, you're typically dealing with caching and the merged objects shouldnt be 'dirty'. """ mapper(User, users, properties={ 'addresses':relation(mapper(Address, addresses)) }) sess = create_session() u = User() u.id = 7 u.name = "fred" a1 = Address() a1.email_address='*****@*****.**' u.addresses.append(a1) sess.add(u) sess.flush() sess2 = create_session() u2 = sess2.query(User).options(sa.orm.eagerload('addresses')).get(7) sess3 = create_session() u3 = sess3.merge(u2, dont_load=True) def go(): sess3.flush() self.assert_sql_count(testing.db, go, 0)
def test_is_modified(self): s = create_session() mapper(User, users, properties={'addresses':relation(Address)}) mapper(Address, addresses) # save user u = User(name='fred') s.add(u) s.flush() s.clear() user = s.query(User).one() assert user not in s.dirty assert not s.is_modified(user) user.name = 'fred' assert user in s.dirty assert not s.is_modified(user) user.name = 'ed' assert user in s.dirty assert s.is_modified(user) s.flush() assert user not in s.dirty assert not s.is_modified(user) a = Address() user.addresses.append(a) assert user in s.dirty assert s.is_modified(user) assert not s.is_modified(user, include_collections=False)
def test_basic(self): Session = scoped_session(sa.orm.sessionmaker()) class CustomQuery(query.Query): pass class SomeObject(_base.ComparableEntity): query = Session.query_property() class SomeOtherObject(_base.ComparableEntity): query = Session.query_property() custom_query = Session.query_property(query_cls=CustomQuery) mapper(SomeObject, table1, properties={ 'options':relation(SomeOtherObject)}) mapper(SomeOtherObject, table2) s = SomeObject(id=1, data="hello") sso = SomeOtherObject() s.options.append(sso) Session.add(s) Session.commit() Session.refresh(sso) Session.remove() eq_(SomeObject(id=1, data="hello", options=[SomeOtherObject(someid=1)]), Session.query(SomeObject).one()) eq_(SomeObject(id=1, data="hello", options=[SomeOtherObject(someid=1)]), SomeObject.query.one()) eq_(SomeOtherObject(someid=1), SomeOtherObject.query.filter( SomeOtherObject.someid == sso.someid).one()) assert isinstance(SomeOtherObject.query, query.Query) assert not isinstance(SomeOtherObject.query, CustomQuery) assert isinstance(SomeOtherObject.custom_query, query.Query)
def test_class_deferred_cols(self): mapper(User, users, properties={ 'name':sa.orm.deferred(users.c.name), 'addresses':relation(Address, backref="user") }) mapper(Address, addresses, properties={ 'email_address':sa.orm.deferred(addresses.c.email_address) }) sess = create_session() u1 = User(name='ed') u1.addresses.append(Address(email_address='*****@*****.**')) sess.add(u1) sess.flush() sess.clear() u1 = sess.query(User).get(u1.id) assert 'name' not in u1.__dict__ assert 'addresses' not in u1.__dict__ u2 = pickle.loads(pickle.dumps(u1)) sess2 = create_session() sess2.update(u2) self.assertEquals(u2.name, 'ed') self.assertEquals(u2, User(name='ed', addresses=[Address(email_address='*****@*****.**')])) u2 = pickle.loads(pickle.dumps(u1)) sess2 = create_session() u2 = sess2.merge(u2, dont_load=True) self.assertEquals(u2.name, 'ed') self.assertEquals(u2, User(name='ed', addresses=[Address(email_address='*****@*****.**')]))
def test_detached_to_persistent_collection(self): mapper(User, users, properties={ 'addresses':relation(Address, backref='user', collection_class=OrderedSet)}) mapper(Address, addresses) on_load = self.on_load_tracker(User) self.on_load_tracker(Address, on_load) a = Address(id=1, email_address='fred1') u = User(id=7, name='fred', addresses=OrderedSet([ a, Address(id=2, email_address='fred2'), ])) sess = create_session() sess.add(u) sess.flush() sess.clear() u.name='fred jones' u.addresses.add(Address(id=3, email_address='fred3')) u.addresses.remove(a) eq_(on_load.called, 0) u = sess.merge(u) eq_(on_load.called, 4) sess.flush() sess.clear() eq_(sess.query(User).first(), User(id=7, name='fred jones', addresses=OrderedSet([ Address(id=2, email_address='fred2'), Address(id=3, email_address='fred3')])))
def test_compileonattr_rel_backref_a(self): m = MetaData() t1 = Table('t1', m, Column('id', Integer, primary_key=True), Column('x', Integer)) t2 = Table('t2', m, Column('id', Integer, primary_key=True), Column('t1_id', Integer, ForeignKey('t1.id'))) class Base(object): def __init__(self, *args, **kwargs): pass for base in object, Base: class A(base): pass class B(base): pass mapper(A, t1, properties=dict(bs=relation(B, backref='a'))) mapper(B, t2) b = B() assert b.a is None a = A() b.a = a session = create_session() session.add(b) assert a in session, "base is %s" % base
def test_compileonattr_rel_backref_b(self): m = MetaData() t1 = Table("t1", m, Column("id", Integer, primary_key=True), Column("x", Integer)) t2 = Table("t2", m, Column("id", Integer, primary_key=True), Column("t1_id", Integer, ForeignKey("t1.id"))) class Base(object): def __init__(self): pass class Base_AKW(object): def __init__(self, *args, **kwargs): pass for base in object, Base, Base_AKW: class A(base): pass class B(base): pass mapper(A, t1) mapper(B, t2, properties=dict(a=relation(A, backref="bs"))) a = A() b = B() b.a = a session = create_session() session.add(a) assert b in session, "base: %s" % base
def test_expired_eager(self): mapper(User, users, properties={ 'addresses':relation(Address, backref='user', lazy=False), }) mapper(Address, addresses) sess = create_session() u = sess.query(User).get(7) sess.expire(u) assert 'name' not in u.__dict__ assert 'addresses' not in u.__dict__ def go(): assert u.addresses[0].email_address == '*****@*****.**' assert u.name == 'jack' # two loads, since relation() + scalar are # separate right now on per-attribute load self.assert_sql_count(testing.db, go, 2) assert 'name' in u.__dict__ assert 'addresses' in u.__dict__ sess.expire(u, ['name', 'addresses']) assert 'name' not in u.__dict__ assert 'addresses' not in u.__dict__ def go(): sess.query(User).filter_by(id=7).one() assert u.addresses[0].email_address == '*****@*****.**' assert u.name == 'jack' # one load, since relation() + scalar are # together when eager load used with Query self.assert_sql_count(testing.db, go, 1)
def test_basic(self): mapper(Employee, employees) mapper(Department, departments, properties=dict(employees=relation(Employee, lazy=False, backref="department"))) d1 = Department(name="One") for e in "Jim", "Jack", "John", "Susan": d1.employees.append(Employee(name=e)) d2 = Department(name="Two") for e in "Joe", "Bob", "Mary", "Wally": d2.employees.append(Employee(name=e)) sess = create_session() sess.add_all((d1, d2)) sess.flush() q = ( sess.query(Department) .join("employees") .filter(Employee.name.startswith("J")) .distinct() .order_by([sa.desc(Department.name)]) ) eq_(q.count(), 2) assert q[0] is d2
def test_transient_to_pending_collection(self): mapper(User, users, properties={ 'addresses': relation(Address, backref='user', collection_class=OrderedSet)}) mapper(Address, addresses) on_load = self.on_load_tracker(User) self.on_load_tracker(Address, on_load) u = User(id=7, name='fred', addresses=OrderedSet([ Address(id=1, email_address='fred1'), Address(id=2, email_address='fred2'), ])) eq_(on_load.called, 0) sess = create_session() sess.merge(u) eq_(on_load.called, 3) merged_users = [e for e in sess if isinstance(e, User)] eq_(len(merged_users), 1) assert merged_users[0] is not u sess.flush() sess.clear() eq_(sess.query(User).one(), User(id=7, name='fred', addresses=OrderedSet([ Address(id=1, email_address='fred1'), Address(id=2, email_address='fred2'), ])) )
def test_correlated_lazyload(self): class User(_base.ComparableEntity): pass class Stuff(_base.ComparableEntity): pass mapper(Stuff, stuff) stuff_view = sa.select([ stuff.c.id ]).where(stuff.c.user_id == user_t.c.id).correlate(user_t).order_by( sa.desc(stuff.c.date)).limit(1) mapper(User, user_t, properties={ 'stuff': relation(Stuff, primaryjoin=sa.and_( user_t.c.id == stuff.c.user_id, stuff.c.id == (stuff_view.as_scalar()))) }) sess = create_session() eq_( sess.query(User).all(), [ User(name='user1', stuff=[Stuff(date=datetime.date(2007, 12, 15), id=2)]), User(name='user2', stuff=[Stuff(id=4, date=datetime.date(2008, 1, 15))]), User(name='user3', stuff=[Stuff(id=5, date=datetime.date(2007, 6, 15))]) ])
def test_uses_get(self): """test that a simple many-to-one lazyload optimizes to use query.get().""" for pj in (None, users.c.id == addresses.c.user_id, addresses.c.user_id == users.c.id): mapper(Address, addresses, properties=dict(user=relation( mapper(User, users), lazy=True, primaryjoin=pj))) sess = create_session() # load address a1 = sess.query(Address).filter_by( email_address="*****@*****.**").one() # load user that is attached to the address u1 = sess.query(User).get(8) def go(): # lazy load of a1.user should get it from the session assert a1.user is u1 self.assert_sql_count(testing.db, go, 0) sa.orm.clear_mappers()
def test_dontload_with_backrefs(self): """dontload populates relations in both directions without requiring a load""" mapper(User, users, properties={ 'addresses':relation(mapper(Address, addresses), backref='user') }) u = User(id=7, name='fred', addresses=[ Address(email_address='ad1'), Address(email_address='ad2')]) sess = create_session() sess.add(u) sess.flush() sess.close() assert 'user' in u.addresses[1].__dict__ sess = create_session() u2 = sess.merge(u, dont_load=True) assert 'user' in u2.addresses[1].__dict__ eq_(u2.addresses[1].user, User(id=7, name='fred')) sess.expire(u2.addresses[1], ['user']) assert 'user' not in u2.addresses[1].__dict__ sess.close() sess = create_session() u = sess.merge(u2, dont_load=True) assert 'user' not in u.addresses[1].__dict__ eq_(u.addresses[1].user, User(id=7, name='fred'))
def _test_manytomany(self, passive_updates): mapper(User, users, properties={ 'items': relation(Item, secondary=users_to_items, backref='users', passive_updates=passive_updates) }) mapper(Item, items) sess = create_session() u1 = User(username='******') u2 = User(username='******') i1 = Item(itemname='item1') i2 = Item(itemname='item2') u1.items.append(i1) u1.items.append(i2) i2.users.append(u2) sess.add(u1) sess.add(u2) sess.flush() r = sess.query(Item).all() # ComparableEntity can't handle a comparison with the backrefs # involved.... self.assertEquals(Item(itemname='item1'), r[0]) self.assertEquals(['jack'], [u.username for u in r[0].users]) self.assertEquals(Item(itemname='item2'), r[1]) self.assertEquals(['jack', 'fred'], [u.username for u in r[1].users]) u2.username = '******' def go(): sess.flush() go() def go(): sess.flush() self.assert_sql_count(testing.db, go, 0) sess.clear() r = sess.query(Item).all() self.assertEquals(Item(itemname='item1'), r[0]) self.assertEquals(['jack'], [u.username for u in r[0].users]) self.assertEquals(Item(itemname='item2'), r[1]) self.assertEquals(['ed', 'jack'], sorted([u.username for u in r[1].users])) sess.clear() u2 = sess.query(User).get(u2.username) u2.username = '******' sess.flush() r = sess.query(Item).with_parent(u2).all() self.assertEquals(Item(itemname='item2'), r[0])
def setup_mappers(self): Session = scoped_session(sa.orm.create_session) Session.mapper(SomeObject, table1, properties={ 'options':relation(SomeOtherObject) }) Session.mapper(SomeOtherObject, table2) self.scoping['Session'] = Session
def test_one_to_many_scalar(self): mapper(User, users, properties=dict(address=relation( mapper(Address, addresses), lazy=True, uselist=False))) q = create_session().query(User) l = q.filter(users.c.id == 7).all() assert [User(id=7, address=Address(id=1))] == l
def setup_mappers(self): mapper(Parent, parent) mapper(Child1, child1, inherits=Parent) mapper(Child2, child2, inherits=Parent, properties=dict(child1=relation( Child1, primaryjoin=child2.c.child1_id == child1.c.id)))
def test_flush(self): mapper(A, a, properties={'cs': relation(C, primaryjoin=a.c.cid == c.c.id)}) mapper(B, b, inherits=A, inherit_condition=b.c.id == a.c.id) mapper(C, c, properties={'arel': relation(A, primaryjoin=a.c.id == c.c.aid)}) sess = create_session() bobj = B() sess.add(bobj) cobj = C() sess.add(cobj) sess.flush()
class Address(Base): __tablename__ = 'addresses' id = Column('id', Integer, primary_key=True) email = Column('email', String(50)) user_id = Column('user_id', Integer, ForeignKey('users.id')) user = relation("User", primaryjoin=user_id == User.id, backref="addresses")
class User(Base, ComparableEntity): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(50)) addresses = relation("Address", order_by=Address.email, foreign_keys=Address.user_id, remote_side=Address.user_id, )
class User(Base, ComparableEntity): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(50)) addresses = relation("Address", primaryjoin="User.id==Address.user_id", backref="user" )
def _test_bidirectional(self, passive_updates): mapper(User, users) mapper(Address, addresses, properties={ 'user': relation(User, passive_updates=passive_updates, backref='addresses') }) sess = create_session() a1 = Address(email='jack1') a2 = Address(email='jack2') u1 = User(username='******', fullname='jack') a1.user = u1 a2.user = u1 sess.add(a1) sess.add(a2) sess.flush() u1.username = '******' (ad1, ad2) = sess.query(Address).all() self.assertEquals([Address(username='******'), Address(username='******')], [ad1, ad2]) def go(): sess.flush() if passive_updates: self.assert_sql_count(testing.db, go, 1) else: self.assert_sql_count(testing.db, go, 3) self.assertEquals([Address(username='******'), Address(username='******')], [ad1, ad2]) sess.clear() self.assertEquals([Address(username='******'), Address(username='******')], sess.query(Address).all()) u1 = sess.query(User).get('ed') assert len(u1.addresses) == 2 # load addresses u1.username = '******' print "--------------------------------" def go(): sess.flush() # check that the passive_updates is on on the other side if passive_updates: self.assert_sql_count(testing.db, go, 1) else: self.assert_sql_count(testing.db, go, 3) sess.clear() self.assertEquals([Address(username='******'), Address(username='******')], sess.query(Address).all())
def test_double(self): """test that a mapper can have two eager relations to the same table, via two different association tables. aliases are required.""" Place.mapper = mapper(Place, place, properties={ 'thingies': relation(mapper(PlaceThingy, place_thingy), lazy=False) }) Transition.mapper = mapper(Transition, transition, properties=dict( inputs=relation(Place.mapper, place_output, lazy=False), outputs=relation(Place.mapper, place_input, lazy=False), )) tran = Transition('transition1') tran.inputs.append(Place('place1')) tran.outputs.append(Place('place2')) tran.outputs.append(Place('place3')) sess = create_session() sess.add(tran) sess.flush() sess.clear() r = sess.query(Transition).all() self.assert_unordered_result( r, Transition, { 'name': 'transition1', 'inputs': (Place, [{ 'name': 'place1' }]), 'outputs': (Place, [{ 'name': 'place2' }, { 'name': 'place3' }]) })
def test_cascades_dont_autoflush(self): sess = create_session(autoflush=True) m = mapper(User, users, properties={ 'addresses':relation(mapper(Address, addresses),backref='user')}) user = User(id=8, name='fred', addresses=[Address(email_address='user')]) merged_user = sess.merge(user) assert merged_user in sess.new sess.flush() assert merged_user not in sess.new
def setup_mappers(self): items, item_keywords, keywords = self.tables.get_all( 'items', 'item_keywords', 'keywords') mapper(Keyword, keywords) mapper( KeywordAssociation, item_keywords, properties={'keyword': relation(Keyword, lazy=False)}, primary_key=[item_keywords.c.item_id, item_keywords.c.keyword_id], order_by=[item_keywords.c.data]) mapper(Item, items, properties={ 'keywords': relation(KeywordAssociation, cascade="all, delete-orphan") })
class User(Base, ComparableEntity): __tablename__ = 'users' id = Column('id', Integer, primary_key=True) name = Column('name', String(50)) adr_count = sa.orm.column_property( sa.select([sa.func.count(Address.id)], Address.user_id == id). as_scalar()) addresses = relation(Address)
def setup_mappers(self): mapper(TT, item, properties={ 'children': relation(TT, remote_side=[item.c.parent_uuid], backref=backref('parent', remote_side=[item.c.uuid])) })
def _test_onetomany(self, passive_updates): mapper(User, users, properties={ 'addresses': relation(Address, passive_updates=passive_updates) }) mapper(Address, addresses) sess = create_session() u1 = User(username='******', fullname='jack') u1.addresses.append(Address(email='jack1')) u1.addresses.append(Address(email='jack2')) sess.add(u1) sess.flush() assert sess.query(Address).get('jack1') is u1.addresses[0] u1.username = '******' sess.flush() assert u1.addresses[0].username == 'ed' sess.clear() self.assertEquals([Address(username='******'), Address(username='******')], sess.query(Address).all()) u1 = sess.query(User).get('ed') u1.username = '******' def go(): sess.flush() if not passive_updates: self.assert_sql_count( testing.db, go, 4 ) # test passive_updates=False; load addresses, update user, update 2 addresses else: self.assert_sql_count(testing.db, go, 1) # test passive_updates=True; update user sess.clear() assert User(username='******', addresses=[ Address(username='******'), Address(username='******') ]) == sess.query(User).get('jack') u1 = sess.query(User).get('jack') u1.addresses = [] u1.username = '******' sess.flush() sess.clear() assert sess.query(Address).get('jack1').username is None u1 = sess.query(User).get('fred') self.assertEquals(User(username='******', fullname='jack'), u1)
def setup_mappers(self): mapper(Account, accounts) mapper(Transaction, transactions) mapper(Entry, entries, properties=dict(account=relation( Account, uselist=False, backref=backref('entries', lazy=True, order_by=entries.c.entry_id)), transaction=relation( Transaction, uselist=False, backref=backref( 'entries', lazy=False, order_by=entries.c.entry_id))))
def test_error(self): mapper(Place, place, properties={ 'transitions': relation(Transition, secondary=place_input, backref='places') }) mapper(Transition, transition, properties={ 'places': relation(Place, secondary=place_input, backref='transitions') }) self.assertRaisesMessage(sa.exc.ArgumentError, "Error creating backref", sa.orm.compile_mappers)
def test_save_update_delete(self): s = create_session() mapper(User, users, properties={ 'addresses':relation(Address, cascade="all, delete") }) mapper(Address, addresses) user = User(name='u1') self.assertRaisesMessage(sa.exc.InvalidRequestError, "is not persisted", s.update, user) self.assertRaisesMessage(sa.exc.InvalidRequestError, "is not persisted", s.delete, user) s.add(user) s.flush() user = s.query(User).one() s.expunge(user) assert user not in s # modify outside of session, assert changes remain/get saved user.name = "fred" s.update(user) assert user in s assert user in s.dirty s.flush() s.clear() assert s.query(User).count() == 1 user = s.query(User).one() assert user.name == 'fred' # ensure its not dirty if no changes occur s.clear() assert user not in s s.update(user) assert user in s assert user not in s.dirty self.assertRaisesMessage(sa.exc.InvalidRequestError, "is already persistent", s.save, user) s2 = create_session() self.assertRaisesMessage(sa.exc.InvalidRequestError, "is already attached to session", s2.delete, user) u2 = s2.query(User).get(user.id) self.assertRaisesMessage(sa.exc.InvalidRequestError, "another instance with key", s.delete, u2) s.expire(user) s.expunge(user) assert user not in s s.delete(user) assert user in s s.flush() assert user not in s assert s.query(User).count() == 0
def testsingle(self): mapper(C1, t1, properties={ 'c1s': relation(C1, cascade="all"), 'parent': relation(C1, primaryjoin=t1.c.parent_c1 == t1.c.c1, remote_side=t1.c.c1, lazy=True, uselist=False) }) a = C1('head c1') a.c1s.append(C1('another c1')) sess = create_session() sess.add(a) sess.flush() sess.delete(a) sess.flush()