Files
sqlalchemy/test/ext/test_associationproxy.py
T
Mike Bayer 1cf80dc5b2 - Changed the update() method on association proxy
dictionary to use a duck typing approach, i.e.
    checks for "keys", to discern between update({})
    and update((a, b)).   Previously, passing a
    dictionary that had tuples as keys would be misinterpreted
    as a sequence. [ticket:2275]
2011-09-14 11:31:33 -04:00

1370 lines
45 KiB
Python

from test.lib.testing import eq_, assert_raises
import copy
import pickle
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.orm.collections import collection, attribute_mapped_collection
from sqlalchemy.ext.associationproxy import *
from sqlalchemy.ext.associationproxy import _AssociationList
from test.lib import *
from test.lib.testing import assert_raises_message
from test.lib.util import gc_collect
from sqlalchemy.sql import not_
from test.lib import fixtures
class DictCollection(dict):
@collection.appender
def append(self, obj):
self[obj.foo] = obj
@collection.remover
def remove(self, obj):
del self[obj.foo]
class SetCollection(set):
pass
class ListCollection(list):
pass
class ObjectCollection(object):
def __init__(self):
self.values = list()
@collection.appender
def append(self, obj):
self.values.append(obj)
@collection.remover
def remove(self, obj):
self.values.remove(obj)
def __iter__(self):
return iter(self.values)
class _CollectionOperations(fixtures.TestBase):
def setup(self):
collection_class = self.collection_class
metadata = MetaData(testing.db)
parents_table = Table('Parent', metadata,
Column('id', Integer, primary_key=True,
test_needs_autoincrement=True),
Column('name', String(128)))
children_table = Table('Children', metadata,
Column('id', Integer, primary_key=True,
test_needs_autoincrement=True),
Column('parent_id', Integer,
ForeignKey('Parent.id')),
Column('foo', String(128)),
Column('name', String(128)))
class Parent(object):
children = association_proxy('_children', 'name')
def __init__(self, name):
self.name = name
class Child(object):
if collection_class and issubclass(collection_class, dict):
def __init__(self, foo, name):
self.foo = foo
self.name = name
else:
def __init__(self, name):
self.name = name
mapper(Parent, parents_table, properties={
'_children': relationship(Child, lazy='joined',
collection_class=collection_class)})
mapper(Child, children_table)
metadata.create_all()
self.metadata = metadata
self.session = create_session()
self.Parent, self.Child = Parent, Child
def teardown(self):
self.metadata.drop_all()
def roundtrip(self, obj):
if obj not in self.session:
self.session.add(obj)
self.session.flush()
id, type_ = obj.id, type(obj)
self.session.expunge_all()
return self.session.query(type_).get(id)
def _test_sequence_ops(self):
Parent, Child = self.Parent, self.Child
p1 = Parent('P1')
self.assert_(not p1._children)
self.assert_(not p1.children)
ch = Child('regular')
p1._children.append(ch)
self.assert_(ch in p1._children)
self.assert_(len(p1._children) == 1)
self.assert_(p1.children)
self.assert_(len(p1.children) == 1)
self.assert_(ch not in p1.children)
self.assert_('regular' in p1.children)
p1.children.append('proxied')
self.assert_('proxied' in p1.children)
self.assert_('proxied' not in p1._children)
self.assert_(len(p1.children) == 2)
self.assert_(len(p1._children) == 2)
self.assert_(p1._children[0].name == 'regular')
self.assert_(p1._children[1].name == 'proxied')
del p1._children[1]
self.assert_(len(p1._children) == 1)
self.assert_(len(p1.children) == 1)
self.assert_(p1._children[0] == ch)
del p1.children[0]
self.assert_(len(p1._children) == 0)
self.assert_(len(p1.children) == 0)
p1.children = ['a','b','c']
self.assert_(len(p1._children) == 3)
self.assert_(len(p1.children) == 3)
del ch
p1 = self.roundtrip(p1)
self.assert_(len(p1._children) == 3)
self.assert_(len(p1.children) == 3)
popped = p1.children.pop()
self.assert_(len(p1.children) == 2)
self.assert_(popped not in p1.children)
p1 = self.roundtrip(p1)
self.assert_(len(p1.children) == 2)
self.assert_(popped not in p1.children)
p1.children[1] = 'changed-in-place'
self.assert_(p1.children[1] == 'changed-in-place')
inplace_id = p1._children[1].id
p1 = self.roundtrip(p1)
self.assert_(p1.children[1] == 'changed-in-place')
assert p1._children[1].id == inplace_id
p1.children.append('changed-in-place')
self.assert_(p1.children.count('changed-in-place') == 2)
p1.children.remove('changed-in-place')
self.assert_(p1.children.count('changed-in-place') == 1)
p1 = self.roundtrip(p1)
self.assert_(p1.children.count('changed-in-place') == 1)
p1._children = []
self.assert_(len(p1.children) == 0)
after = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
p1.children = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
self.assert_(len(p1.children) == 10)
self.assert_([c.name for c in p1._children] == after)
p1.children[2:6] = ['x'] * 4
after = ['a', 'b', 'x', 'x', 'x', 'x', 'g', 'h', 'i', 'j']
self.assert_(p1.children == after)
self.assert_([c.name for c in p1._children] == after)
p1.children[2:6] = ['y']
after = ['a', 'b', 'y', 'g', 'h', 'i', 'j']
self.assert_(p1.children == after)
self.assert_([c.name for c in p1._children] == after)
p1.children[2:3] = ['z'] * 4
after = ['a', 'b', 'z', 'z', 'z', 'z', 'g', 'h', 'i', 'j']
self.assert_(p1.children == after)
self.assert_([c.name for c in p1._children] == after)
p1.children[2::2] = ['O'] * 4
after = ['a', 'b', 'O', 'z', 'O', 'z', 'O', 'h', 'O', 'j']
self.assert_(p1.children == after)
self.assert_([c.name for c in p1._children] == after)
assert_raises(TypeError, set, [p1.children])
p1.children *= 0
after = []
self.assert_(p1.children == after)
self.assert_([c.name for c in p1._children] == after)
p1.children += ['a', 'b']
after = ['a', 'b']
self.assert_(p1.children == after)
self.assert_([c.name for c in p1._children] == after)
p1.children += ['c']
after = ['a', 'b', 'c']
self.assert_(p1.children == after)
self.assert_([c.name for c in p1._children] == after)
p1.children *= 1
after = ['a', 'b', 'c']
self.assert_(p1.children == after)
self.assert_([c.name for c in p1._children] == after)
p1.children *= 2
after = ['a', 'b', 'c', 'a', 'b', 'c']
self.assert_(p1.children == after)
self.assert_([c.name for c in p1._children] == after)
p1.children = ['a']
after = ['a']
self.assert_(p1.children == after)
self.assert_([c.name for c in p1._children] == after)
self.assert_((p1.children * 2) == ['a', 'a'])
self.assert_((2 * p1.children) == ['a', 'a'])
self.assert_((p1.children * 0) == [])
self.assert_((0 * p1.children) == [])
self.assert_((p1.children + ['b']) == ['a', 'b'])
self.assert_((['b'] + p1.children) == ['b', 'a'])
try:
p1.children + 123
assert False
except TypeError:
assert True
class DefaultTest(_CollectionOperations):
def __init__(self, *args, **kw):
super(DefaultTest, self).__init__(*args, **kw)
self.collection_class = None
def test_sequence_ops(self):
self._test_sequence_ops()
class ListTest(_CollectionOperations):
def __init__(self, *args, **kw):
super(ListTest, self).__init__(*args, **kw)
self.collection_class = list
def test_sequence_ops(self):
self._test_sequence_ops()
class CustomListTest(ListTest):
def __init__(self, *args, **kw):
super(CustomListTest, self).__init__(*args, **kw)
self.collection_class = list
# No-can-do until ticket #213
class DictTest(_CollectionOperations):
pass
class CustomDictTest(DictTest):
def __init__(self, *args, **kw):
super(DictTest, self).__init__(*args, **kw)
self.collection_class = DictCollection
def test_mapping_ops(self):
Parent, Child = self.Parent, self.Child
p1 = Parent('P1')
self.assert_(not p1._children)
self.assert_(not p1.children)
ch = Child('a', 'regular')
p1._children.append(ch)
self.assert_(ch in p1._children.values())
self.assert_(len(p1._children) == 1)
self.assert_(p1.children)
self.assert_(len(p1.children) == 1)
self.assert_(ch not in p1.children)
self.assert_('a' in p1.children)
self.assert_(p1.children['a'] == 'regular')
self.assert_(p1._children['a'] == ch)
p1.children['b'] = 'proxied'
self.assert_('proxied' in p1.children.values())
self.assert_('b' in p1.children)
self.assert_('proxied' not in p1._children)
self.assert_(len(p1.children) == 2)
self.assert_(len(p1._children) == 2)
self.assert_(p1._children['a'].name == 'regular')
self.assert_(p1._children['b'].name == 'proxied')
del p1._children['b']
self.assert_(len(p1._children) == 1)
self.assert_(len(p1.children) == 1)
self.assert_(p1._children['a'] == ch)
del p1.children['a']
self.assert_(len(p1._children) == 0)
self.assert_(len(p1.children) == 0)
p1.children = {'d': 'v d', 'e': 'v e', 'f': 'v f'}
self.assert_(len(p1._children) == 3)
self.assert_(len(p1.children) == 3)
self.assert_(set(p1.children) == set(['d','e','f']))
del ch
p1 = self.roundtrip(p1)
self.assert_(len(p1._children) == 3)
self.assert_(len(p1.children) == 3)
p1.children['e'] = 'changed-in-place'
self.assert_(p1.children['e'] == 'changed-in-place')
inplace_id = p1._children['e'].id
p1 = self.roundtrip(p1)
self.assert_(p1.children['e'] == 'changed-in-place')
self.assert_(p1._children['e'].id == inplace_id)
p1._children = {}
self.assert_(len(p1.children) == 0)
try:
p1._children = []
self.assert_(False)
except TypeError:
self.assert_(True)
try:
p1._children = None
self.assert_(False)
except TypeError:
self.assert_(True)
assert_raises(TypeError, set, [p1.children])
class SetTest(_CollectionOperations):
def __init__(self, *args, **kw):
super(SetTest, self).__init__(*args, **kw)
self.collection_class = set
def test_set_operations(self):
Parent, Child = self.Parent, self.Child
p1 = Parent('P1')
self.assert_(not p1._children)
self.assert_(not p1.children)
ch1 = Child('regular')
p1._children.add(ch1)
self.assert_(ch1 in p1._children)
self.assert_(len(p1._children) == 1)
self.assert_(p1.children)
self.assert_(len(p1.children) == 1)
self.assert_(ch1 not in p1.children)
self.assert_('regular' in p1.children)
p1.children.add('proxied')
self.assert_('proxied' in p1.children)
self.assert_('proxied' not in p1._children)
self.assert_(len(p1.children) == 2)
self.assert_(len(p1._children) == 2)
self.assert_(set([o.name for o in p1._children]) ==
set(['regular', 'proxied']))
ch2 = None
for o in p1._children:
if o.name == 'proxied':
ch2 = o
break
p1._children.remove(ch2)
self.assert_(len(p1._children) == 1)
self.assert_(len(p1.children) == 1)
self.assert_(p1._children == set([ch1]))
p1.children.remove('regular')
self.assert_(len(p1._children) == 0)
self.assert_(len(p1.children) == 0)
p1.children = ['a','b','c']
self.assert_(len(p1._children) == 3)
self.assert_(len(p1.children) == 3)
del ch1
p1 = self.roundtrip(p1)
self.assert_(len(p1._children) == 3)
self.assert_(len(p1.children) == 3)
self.assert_('a' in p1.children)
self.assert_('b' in p1.children)
self.assert_('d' not in p1.children)
self.assert_(p1.children == set(['a','b','c']))
try:
p1.children.remove('d')
self.fail()
except KeyError:
pass
self.assert_(len(p1.children) == 3)
p1.children.discard('d')
self.assert_(len(p1.children) == 3)
p1 = self.roundtrip(p1)
self.assert_(len(p1.children) == 3)
popped = p1.children.pop()
self.assert_(len(p1.children) == 2)
self.assert_(popped not in p1.children)
p1 = self.roundtrip(p1)
self.assert_(len(p1.children) == 2)
self.assert_(popped not in p1.children)
p1.children = ['a','b','c']
p1 = self.roundtrip(p1)
self.assert_(p1.children == set(['a','b','c']))
p1.children.discard('b')
p1 = self.roundtrip(p1)
self.assert_(p1.children == set(['a', 'c']))
p1.children.remove('a')
p1 = self.roundtrip(p1)
self.assert_(p1.children == set(['c']))
p1._children = set()
self.assert_(len(p1.children) == 0)
try:
p1._children = []
self.assert_(False)
except TypeError:
self.assert_(True)
try:
p1._children = None
self.assert_(False)
except TypeError:
self.assert_(True)
assert_raises(TypeError, set, [p1.children])
def test_set_comparisons(self):
Parent, Child = self.Parent, self.Child
p1 = Parent('P1')
p1.children = ['a','b','c']
control = set(['a','b','c'])
for other in (set(['a','b','c']), set(['a','b','c','d']),
set(['a']), set(['a','b']),
set(['c','d']), set(['e', 'f', 'g']),
set()):
eq_(p1.children.union(other),
control.union(other))
eq_(p1.children.difference(other),
control.difference(other))
eq_((p1.children - other),
(control - other))
eq_(p1.children.intersection(other),
control.intersection(other))
eq_(p1.children.symmetric_difference(other),
control.symmetric_difference(other))
eq_(p1.children.issubset(other),
control.issubset(other))
eq_(p1.children.issuperset(other),
control.issuperset(other))
self.assert_((p1.children == other) == (control == other))
self.assert_((p1.children != other) == (control != other))
self.assert_((p1.children < other) == (control < other))
self.assert_((p1.children <= other) == (control <= other))
self.assert_((p1.children > other) == (control > other))
self.assert_((p1.children >= other) == (control >= other))
def test_set_mutation(self):
Parent, Child = self.Parent, self.Child
# mutations
for op in ('update', 'intersection_update',
'difference_update', 'symmetric_difference_update'):
for base in (['a', 'b', 'c'], []):
for other in (set(['a','b','c']), set(['a','b','c','d']),
set(['a']), set(['a','b']),
set(['c','d']), set(['e', 'f', 'g']),
set()):
p = Parent('p')
p.children = base[:]
control = set(base[:])
getattr(p.children, op)(other)
getattr(control, op)(other)
try:
self.assert_(p.children == control)
except:
print 'Test %s.%s(%s):' % (set(base), op, other)
print 'want', repr(control)
print 'got', repr(p.children)
raise
p = self.roundtrip(p)
try:
self.assert_(p.children == control)
except:
print 'Test %s.%s(%s):' % (base, op, other)
print 'want', repr(control)
print 'got', repr(p.children)
raise
# in-place mutations
for op in ('|=', '-=', '&=', '^='):
for base in (['a', 'b', 'c'], []):
for other in (set(['a','b','c']), set(['a','b','c','d']),
set(['a']), set(['a','b']),
set(['c','d']), set(['e', 'f', 'g']),
frozenset(['e', 'f', 'g']),
set()):
p = Parent('p')
p.children = base[:]
control = set(base[:])
exec "p.children %s other" % op
exec "control %s other" % op
try:
self.assert_(p.children == control)
except:
print 'Test %s %s %s:' % (set(base), op, other)
print 'want', repr(control)
print 'got', repr(p.children)
raise
p = self.roundtrip(p)
try:
self.assert_(p.children == control)
except:
print 'Test %s %s %s:' % (base, op, other)
print 'want', repr(control)
print 'got', repr(p.children)
raise
class CustomSetTest(SetTest):
def __init__(self, *args, **kw):
super(CustomSetTest, self).__init__(*args, **kw)
self.collection_class = SetCollection
class CustomObjectTest(_CollectionOperations):
def __init__(self, *args, **kw):
super(CustomObjectTest, self).__init__(*args, **kw)
self.collection_class = ObjectCollection
def test_basic(self):
Parent, Child = self.Parent, self.Child
p = Parent('p1')
self.assert_(len(list(p.children)) == 0)
p.children.append('child')
self.assert_(len(list(p.children)) == 1)
p = self.roundtrip(p)
self.assert_(len(list(p.children)) == 1)
# We didn't provide an alternate _AssociationList implementation
# for our ObjectCollection, so indexing will fail.
try:
v = p.children[1]
self.fail()
except TypeError:
pass
class ProxyFactoryTest(ListTest):
def setup(self):
metadata = MetaData(testing.db)
parents_table = Table('Parent', metadata,
Column('id', Integer, primary_key=True,
test_needs_autoincrement=True),
Column('name', String(128)))
children_table = Table('Children', metadata,
Column('id', Integer, primary_key=True,
test_needs_autoincrement=True),
Column('parent_id', Integer,
ForeignKey('Parent.id')),
Column('foo', String(128)),
Column('name', String(128)))
class CustomProxy(_AssociationList):
def __init__(
self,
lazy_collection,
creator,
value_attr,
parent,
):
getter, setter = parent._default_getset(lazy_collection)
_AssociationList.__init__(
self,
lazy_collection,
creator,
getter,
setter,
parent,
)
class Parent(object):
children = association_proxy('_children', 'name',
proxy_factory=CustomProxy,
proxy_bulk_set=CustomProxy.extend
)
def __init__(self, name):
self.name = name
class Child(object):
def __init__(self, name):
self.name = name
mapper(Parent, parents_table, properties={
'_children': relationship(Child, lazy='joined',
collection_class=list)})
mapper(Child, children_table)
metadata.create_all()
self.metadata = metadata
self.session = create_session()
self.Parent, self.Child = Parent, Child
def test_sequence_ops(self):
self._test_sequence_ops()
class ScalarTest(fixtures.TestBase):
def test_scalar_proxy(self):
metadata = MetaData(testing.db)
parents_table = Table('Parent', metadata,
Column('id', Integer, primary_key=True,
test_needs_autoincrement=True),
Column('name', String(128)))
children_table = Table('Children', metadata,
Column('id', Integer, primary_key=True,
test_needs_autoincrement=True),
Column('parent_id', Integer,
ForeignKey('Parent.id')),
Column('foo', String(128)),
Column('bar', String(128)),
Column('baz', String(128)))
class Parent(object):
foo = association_proxy('child', 'foo')
bar = association_proxy('child', 'bar',
creator=lambda v: Child(bar=v))
baz = association_proxy('child', 'baz',
creator=lambda v: Child(baz=v))
def __init__(self, name):
self.name = name
class Child(object):
def __init__(self, **kw):
for attr in kw:
setattr(self, attr, kw[attr])
mapper(Parent, parents_table, properties={
'child': relationship(Child, lazy='joined',
backref='parent', uselist=False)})
mapper(Child, children_table)
metadata.create_all()
session = create_session()
def roundtrip(obj):
if obj not in session:
session.add(obj)
session.flush()
id, type_ = obj.id, type(obj)
session.expunge_all()
return session.query(type_).get(id)
p = Parent('p')
# No child
try:
v = p.foo
self.fail()
except:
pass
p.child = Child(foo='a', bar='b', baz='c')
self.assert_(p.foo == 'a')
self.assert_(p.bar == 'b')
self.assert_(p.baz == 'c')
p.bar = 'x'
self.assert_(p.foo == 'a')
self.assert_(p.bar == 'x')
self.assert_(p.baz == 'c')
p = roundtrip(p)
self.assert_(p.foo == 'a')
self.assert_(p.bar == 'x')
self.assert_(p.baz == 'c')
p.child = None
# No child again
try:
v = p.foo
self.fail()
except:
pass
# Bogus creator for this scalar type
try:
p.foo = 'zzz'
self.fail()
except TypeError:
pass
p.bar = 'yyy'
self.assert_(p.foo is None)
self.assert_(p.bar == 'yyy')
self.assert_(p.baz is None)
del p.child
p = roundtrip(p)
self.assert_(p.child is None)
p.baz = 'xxx'
self.assert_(p.foo is None)
self.assert_(p.bar is None)
self.assert_(p.baz == 'xxx')
p = roundtrip(p)
self.assert_(p.foo is None)
self.assert_(p.bar is None)
self.assert_(p.baz == 'xxx')
# Ensure an immediate __set__ works.
p2 = Parent('p2')
p2.bar = 'quux'
class LazyLoadTest(fixtures.TestBase):
def setup(self):
metadata = MetaData(testing.db)
parents_table = Table('Parent', metadata,
Column('id', Integer, primary_key=True,
test_needs_autoincrement=True),
Column('name', String(128)))
children_table = Table('Children', metadata,
Column('id', Integer, primary_key=True,
test_needs_autoincrement=True),
Column('parent_id', Integer,
ForeignKey('Parent.id')),
Column('foo', String(128)),
Column('name', String(128)))
class Parent(object):
children = association_proxy('_children', 'name')
def __init__(self, name):
self.name = name
class Child(object):
def __init__(self, name):
self.name = name
mapper(Child, children_table)
metadata.create_all()
self.metadata = metadata
self.session = create_session()
self.Parent, self.Child = Parent, Child
self.table = parents_table
def teardown(self):
self.metadata.drop_all()
def roundtrip(self, obj):
self.session.add(obj)
self.session.flush()
id, type_ = obj.id, type(obj)
self.session.expunge_all()
return self.session.query(type_).get(id)
def test_lazy_list(self):
Parent, Child = self.Parent, self.Child
mapper(Parent, self.table, properties={
'_children': relationship(Child, lazy='select',
collection_class=list)})
p = Parent('p')
p.children = ['a','b','c']
p = self.roundtrip(p)
# Is there a better way to ensure that the association_proxy
# didn't convert a lazy load to an eager load? This does work though.
self.assert_('_children' not in p.__dict__)
self.assert_(len(p._children) == 3)
self.assert_('_children' in p.__dict__)
def test_eager_list(self):
Parent, Child = self.Parent, self.Child
mapper(Parent, self.table, properties={
'_children': relationship(Child, lazy='joined',
collection_class=list)})
p = Parent('p')
p.children = ['a','b','c']
p = self.roundtrip(p)
self.assert_('_children' in p.__dict__)
self.assert_(len(p._children) == 3)
def test_lazy_scalar(self):
Parent, Child = self.Parent, self.Child
mapper(Parent, self.table, properties={
'_children': relationship(Child, lazy='select', uselist=False)})
p = Parent('p')
p.children = 'value'
p = self.roundtrip(p)
self.assert_('_children' not in p.__dict__)
self.assert_(p._children is not None)
def test_eager_scalar(self):
Parent, Child = self.Parent, self.Child
mapper(Parent, self.table, properties={
'_children': relationship(Child, lazy='joined', uselist=False)})
p = Parent('p')
p.children = 'value'
p = self.roundtrip(p)
self.assert_('_children' in p.__dict__)
self.assert_(p._children is not None)
class Parent(object):
def __init__(self, name):
self.name = name
class Child(object):
def __init__(self, name):
self.name = name
class KVChild(object):
def __init__(self, name, value):
self.name = name
self.value = value
class ReconstitutionTest(fixtures.TestBase):
def setup(self):
metadata = MetaData(testing.db)
parents = Table('parents', metadata, Column('id', Integer,
primary_key=True,
test_needs_autoincrement=True), Column('name',
String(30)))
children = Table('children', metadata, Column('id', Integer,
primary_key=True,
test_needs_autoincrement=True),
Column('parent_id', Integer,
ForeignKey('parents.id')), Column('name',
String(30)))
metadata.create_all()
parents.insert().execute(name='p1')
self.metadata = metadata
self.parents = parents
self.children = children
Parent.kids = association_proxy('children', 'name')
def teardown(self):
self.metadata.drop_all()
clear_mappers()
def test_weak_identity_map(self):
mapper(Parent, self.parents,
properties=dict(children=relationship(Child)))
mapper(Child, self.children)
session = create_session(weak_identity_map=True)
def add_child(parent_name, child_name):
parent = \
session.query(Parent).filter_by(name=parent_name).one()
parent.kids.append(child_name)
add_child('p1', 'c1')
gc_collect()
add_child('p1', 'c2')
session.flush()
p = session.query(Parent).filter_by(name='p1').one()
assert set(p.kids) == set(['c1', 'c2']), p.kids
def test_copy(self):
mapper(Parent, self.parents,
properties=dict(children=relationship(Child)))
mapper(Child, self.children)
p = Parent('p1')
p.kids.extend(['c1', 'c2'])
p_copy = copy.copy(p)
del p
gc_collect()
assert set(p_copy.kids) == set(['c1', 'c2']), p.kids
def test_pickle_list(self):
mapper(Parent, self.parents,
properties=dict(children=relationship(Child)))
mapper(Child, self.children)
p = Parent('p1')
p.kids.extend(['c1', 'c2'])
r1 = pickle.loads(pickle.dumps(p))
assert r1.kids == ['c1', 'c2']
r2 = pickle.loads(pickle.dumps(p.kids))
assert r2 == ['c1', 'c2']
def test_pickle_set(self):
mapper(Parent, self.parents,
properties=dict(children=relationship(Child,
collection_class=set)))
mapper(Child, self.children)
p = Parent('p1')
p.kids.update(['c1', 'c2'])
r1 = pickle.loads(pickle.dumps(p))
assert r1.kids == set(['c1', 'c2'])
r2 = pickle.loads(pickle.dumps(p.kids))
assert r2 == set(['c1', 'c2'])
def test_pickle_dict(self):
mapper(Parent, self.parents,
properties=dict(children=relationship(KVChild,
collection_class=
collections.mapped_collection(PickleKeyFunc('name')))))
mapper(KVChild, self.children)
p = Parent('p1')
p.kids.update({'c1': 'v1', 'c2': 'v2'})
assert p.kids == {'c1': 'c1', 'c2': 'c2'}
r1 = pickle.loads(pickle.dumps(p))
assert r1.kids == {'c1': 'c1', 'c2': 'c2'}
r2 = pickle.loads(pickle.dumps(p.kids))
assert r2 == {'c1': 'c1', 'c2': 'c2'}
class PickleKeyFunc(object):
def __init__(self, name):
self.name = name
def __call__(self, obj):
return getattr(obj, self.name)
class ComparatorTest(fixtures.MappedTest, AssertsCompiledSQL):
__dialect__ = 'default'
run_inserts = 'once'
run_deletes = None
run_setup_mappers = 'once'
run_setup_classes = 'once'
@classmethod
def define_tables(cls, metadata):
Table('userkeywords', metadata,
Column('keyword_id', Integer,ForeignKey('keywords.id'), primary_key=True),
Column('user_id', Integer, ForeignKey('users.id'))
)
Table('users', metadata,
Column('id', Integer,
primary_key=True, test_needs_autoincrement=True),
Column('name', String(64)),
Column('singular_id', Integer, ForeignKey('singular.id'))
)
Table('keywords', metadata,
Column('id', Integer,
primary_key=True, test_needs_autoincrement=True),
Column('keyword', String(64)),
Column('singular_id', Integer, ForeignKey('singular.id'))
)
Table('singular', metadata,
Column('id', Integer,
primary_key=True, test_needs_autoincrement=True),
)
@classmethod
def setup_classes(cls):
class User(cls.Comparable):
def __init__(self, name):
self.name = name
# o2m -> m2o
# uselist -> nonuselist
keywords = association_proxy('user_keywords', 'keyword',
creator=lambda k: UserKeyword(keyword=k))
# m2o -> o2m
# nonuselist -> uselist
singular_keywords = association_proxy('singular', 'keywords')
class Keyword(cls.Comparable):
def __init__(self, keyword):
self.keyword = keyword
# o2o -> m2o
# nonuselist -> nonuselist
user = association_proxy('user_keyword', 'user')
class UserKeyword(cls.Comparable):
def __init__(self, user=None, keyword=None):
self.user = user
self.keyword = keyword
class Singular(cls.Comparable):
def __init__(self, value=None):
self.value = value
@classmethod
def setup_mappers(cls):
users, Keyword, UserKeyword, singular, \
userkeywords, User, keywords, Singular = (cls.tables.users,
cls.classes.Keyword,
cls.classes.UserKeyword,
cls.tables.singular,
cls.tables.userkeywords,
cls.classes.User,
cls.tables.keywords,
cls.classes.Singular)
mapper(User, users, properties={
'singular':relationship(Singular)
})
mapper(Keyword, keywords, properties={
'user_keyword':relationship(UserKeyword, uselist=False)
})
mapper(UserKeyword, userkeywords, properties={
'user' : relationship(User, backref='user_keywords'),
'keyword' : relationship(Keyword)
})
mapper(Singular, singular, properties={
'keywords': relationship(Keyword)
})
@classmethod
def insert_data(cls):
UserKeyword, User, Keyword, Singular = (cls.classes.UserKeyword,
cls.classes.User,
cls.classes.Keyword,
cls.classes.Singular)
session = sessionmaker()()
words = (
'quick', 'brown',
'fox', 'jumped', 'over',
'the', 'lazy',
)
for ii in range(4):
user = User('user%d' % ii)
user.singular = Singular()
session.add(user)
for jj in words[ii:ii + 3]:
k = Keyword(jj)
user.keywords.append(k)
user.singular.keywords.append(k)
orphan = Keyword('orphan')
orphan.user_keyword = UserKeyword(keyword=orphan, user=None)
session.add(orphan)
session.commit()
cls.u = user
cls.kw = user.keywords[0]
cls.session = session
def _equivalent(self, q_proxy, q_direct):
eq_(q_proxy.all(), q_direct.all())
def test_filter_any_kwarg_ul_nul(self):
UserKeyword, User = self.classes.UserKeyword, self.classes.User
self._equivalent(self.session.query(User).
filter(User.keywords.any(keyword='jumped'
)),
self.session.query(User).filter(
User.user_keywords.any(
UserKeyword.keyword.has(keyword='jumped'
))))
def test_filter_has_kwarg_nul_nul(self):
UserKeyword, Keyword = self.classes.UserKeyword, self.classes.Keyword
self._equivalent(self.session.query(Keyword).
filter(Keyword.user.has(name='user2'
)),
self.session.query(Keyword).
filter(Keyword.user_keyword.has(
UserKeyword.user.has(name='user2'
))))
def test_filter_has_kwarg_nul_ul(self):
User, Singular = self.classes.User, self.classes.Singular
self._equivalent(
self.session.query(User).\
filter(User.singular_keywords.any(keyword='jumped')),
self.session.query(User).\
filter(
User.singular.has(
Singular.keywords.any(keyword='jumped')
)
)
)
def test_filter_any_criterion_ul_nul(self):
UserKeyword, User, Keyword = (self.classes.UserKeyword,
self.classes.User,
self.classes.Keyword)
self._equivalent(self.session.query(User).
filter(User.keywords.any(Keyword.keyword
== 'jumped')),
self.session.query(User).
filter(User.user_keywords.any(
UserKeyword.keyword.has(Keyword.keyword
== 'jumped'))))
def test_filter_has_criterion_nul_nul(self):
UserKeyword, User, Keyword = (self.classes.UserKeyword,
self.classes.User,
self.classes.Keyword)
self._equivalent(self.session.query(Keyword).
filter(Keyword.user.has(User.name
== 'user2')),
self.session.query(Keyword).
filter(Keyword.user_keyword.has(
UserKeyword.user.has(User.name
== 'user2'))))
def test_filter_any_criterion_nul_ul(self):
User, Keyword, Singular = (self.classes.User,
self.classes.Keyword,
self.classes.Singular)
self._equivalent(
self.session.query(User).\
filter(User.singular_keywords.any(Keyword.keyword=='jumped')),
self.session.query(User).\
filter(
User.singular.has(
Singular.keywords.any(Keyword.keyword=='jumped')
)
)
)
def test_filter_contains_ul_nul(self):
User = self.classes.User
self._equivalent(self.session.query(User).
filter(User.keywords.contains(self.kw)),
self.session.query(User).
filter(User.user_keywords.any(keyword=self.kw)))
def test_filter_contains_nul_ul(self):
User, Singular = self.classes.User, self.classes.Singular
self._equivalent(
self.session.query(User).filter(
User.singular_keywords.contains(self.kw)
),
self.session.query(User).filter(
User.singular.has(
Singular.keywords.contains(self.kw)
)
),
)
def test_filter_eq_nul_nul(self):
Keyword = self.classes.Keyword
self._equivalent(self.session.query(Keyword).filter(Keyword.user
== self.u),
self.session.query(Keyword).
filter(Keyword.user_keyword.has(user=self.u)))
def test_filter_ne_nul_nul(self):
Keyword = self.classes.Keyword
self._equivalent(self.session.query(Keyword).filter(Keyword.user
!= self.u),
self.session.query(Keyword).
filter(not_(Keyword.user_keyword.has(user=self.u))))
def test_filter_eq_null_nul_nul(self):
UserKeyword, Keyword = self.classes.UserKeyword, self.classes.Keyword
self._equivalent(self.session.query(Keyword).filter(Keyword.user
== None),
self.session.query(Keyword).
filter(Keyword.user_keyword.has(UserKeyword.user
== None)))
def test_filter_scalar_contains_fails_nul_nul(self):
Keyword = self.classes.Keyword
assert_raises(exceptions.InvalidRequestError, lambda : \
Keyword.user.contains(self.u))
def test_filter_scalar_any_fails_nul_nul(self):
Keyword = self.classes.Keyword
assert_raises(exceptions.InvalidRequestError, lambda : \
Keyword.user.any(name='user2'))
def test_filter_collection_has_fails_ul_nul(self):
User = self.classes.User
assert_raises(exceptions.InvalidRequestError, lambda : \
User.keywords.has(keyword='quick'))
def test_filter_collection_eq_fails_ul_nul(self):
User = self.classes.User
assert_raises(exceptions.InvalidRequestError, lambda : \
User.keywords == self.kw)
def test_filter_collection_ne_fails_ul_nul(self):
User = self.classes.User
assert_raises(exceptions.InvalidRequestError, lambda : \
User.keywords != self.kw)
def test_join_separate_attr(self):
User = self.classes.User
self.assert_compile(
self.session.query(User).join(
User.keywords.local_attr,
User.keywords.remote_attr),
"SELECT users.id AS users_id, users.name AS users_name, "
"users.singular_id AS users_singular_id "
"FROM users JOIN userkeywords ON users.id = "
"userkeywords.user_id JOIN keywords ON keywords.id = "
"userkeywords.keyword_id"
)
def test_join_single_attr(self):
User = self.classes.User
self.assert_compile(
self.session.query(User).join(
*User.keywords.attr),
"SELECT users.id AS users_id, users.name AS users_name, "
"users.singular_id AS users_singular_id "
"FROM users JOIN userkeywords ON users.id = "
"userkeywords.user_id JOIN keywords ON keywords.id = "
"userkeywords.keyword_id"
)
class DictOfTupleUpdateTest(fixtures.TestBase):
def setup(self):
class B(object):
def __init__(self, key, elem):
self.key = key
self.elem = elem
class A(object):
elements = association_proxy("orig", "elem", creator=B)
m = MetaData()
a = Table('a', m, Column('id', Integer, primary_key=True))
b = Table('b', m, Column('id', Integer, primary_key=True),
Column('aid', Integer, ForeignKey('a.id')))
mapper(A, a, properties={
'orig':relationship(B, collection_class=attribute_mapped_collection('key'))
})
mapper(B, b)
self.A = A
self.B = B
def test_update_one_elem_dict(self):
a1 = self.A()
a1.elements.update({("B", 3): 'elem2'})
eq_(a1.elements, {("B",3):'elem2'})
def test_update_multi_elem_dict(self):
a1 = self.A()
a1.elements.update({("B", 3): 'elem2', ("C", 4): "elem3"})
eq_(a1.elements, {("B",3):'elem2', ("C", 4): "elem3"})
def test_update_one_elem_list(self):
a1 = self.A()
a1.elements.update([(("B", 3), 'elem2')])
eq_(a1.elements, {("B",3):'elem2'})
def test_update_multi_elem_list(self):
a1 = self.A()
a1.elements.update([(("B", 3), 'elem2'), (("C", 4), "elem3")])
eq_(a1.elements, {("B",3):'elem2', ("C", 4): "elem3"})
def test_update_one_elem_varg(self):
a1 = self.A()
assert_raises_message(
ValueError,
"dictionary update sequence requires "
"2-element tuples",
a1.elements.update, (("B", 3), 'elem2')
)
def test_update_multi_elem_varg(self):
a1 = self.A()
assert_raises_message(
TypeError,
"update expected at most 1 arguments, got 2",
a1.elements.update,
(("B", 3), 'elem2'), (("C", 4), "elem3")
)