inheritance attributes which were based on
column_property() or similar would fail to evaluate.
[ticket:1480]
+
+ - Improved support for MapperProperty objects overriding
+ that of an inherited mapper for non-concrete
+ inheritance setups - attribute extensions won't randomly
+ collide with each other. [ticket:1488]
+
+ - UPDATE and DELETE do not support ORDER BY, LIMIT, OFFSET,
+ etc. in standard SQL. Query.update() and Query.delete()
+ now raise an exception if any of limit(), offset(),
+ order_by(), group_by(), or distinct() have been
+ called. [ticket:1487]
+
+ - Added AttributeExtension to sqlalchemy.orm.__all__
- Improved error message when query() is called with
a non-SQL /entity expression. [ticket:1476]
[ticket:1424]. See
http://www.sqlalchemy.org/trac/wiki/UsageRecipes/PreFilteredQuery
for an example.
+
+ - Fixed a somewhat hypothetical issue which would result
+ in the wrong primary key being calculated for a mapper
+ using the old polymorphic_union function - but this
+ is old stuff. [ticket:1486]
- sql
- Fixed a bug in extract() introduced in 0.5.4 whereby
- Fixed bug in Table and Column whereby passing empty
dict for "info" argument would raise an exception.
[ticket:1482]
+
+- oracle
+ - Backported 0.6 fix for Oracle alias names not getting
+ truncated. [ticket:1309]
- ext
- The collection proxies produced by associationproxy are now
- Declarative will raise an informative exception if
__table_args__ is passed as a tuple with no dict argument.
Improved documentation. [ticket:1468]
+
+- test
+ - Added examples into the test suite so they get exercised
+ regularly and cleaned up a couple deprecation warnings.
+
0.5.5
=======
engine = create_engine('sqlite://')
meta = MetaData(engine)
+# setup a comparator for the PickleType since it's a mutable
+# element.
+def are_elements_equal(x, y):
+ return x == y
+
# stores a top level record of an XML document.
# the "element" column will store the ElementTree document as a BLOB.
documents = Table('documents', meta,
Column('document_id', Integer, primary_key=True),
Column('filename', String(30), unique=True),
- Column('element', PickleType)
+ Column('element', PickleType(comparator=are_elements_equal))
)
meta.create_all()
class Bar(object):
def __init__(self, value):
self.data = value
-
+
+ def __eq__(self, other):
+ if not other is None:
+ return self.data == other.data
+ return NotImplemented
+
+
mapper(Foo, foo_table, extension=MyExt())
mapper(Bar, bar_table)
'employees': relation(Person, lazy=False, backref='company', cascade="all, delete-orphan")
})
-session = create_session(echo_uow=False)
+session = create_session()
c = Company(name='company1')
c.employees.append(Manager(name='pointy haired boss', status='AAB', manager_name='manager1'))
c.employees.append(Engineer(name='dilbert', status='BBA', engineer_name='engineer1', primary_language='java'))
MapperExtension,
PropComparator,
SessionExtension,
+ AttributeExtension,
)
from sqlalchemy.orm.util import (
AliasedClass as aliased,
'EXT_STOP',
'InstrumentationManager',
'MapperExtension',
+ 'AttributeExtension',
'Validator',
'PropComparator',
'Query',
return self
# initialize properties on all mappers
+ # note that _mapper_registry is unordered, which
+ # may randomly conceal/reveal issues related to
+ # the order of mapper compilation
for mapper in list(_mapper_registry):
if getattr(mapper, '_compile_failed', False):
raise sa_exc.InvalidRequestError("One or more mappers failed to compile. Exception was probably "
"Otherwise, call %s() before limit() or offset() are applied." % (meth, meth)
)
+ def _no_select_modifiers(self, meth):
+ if not self._enable_assertions:
+ return
+ for attr, methname, notset in (
+ ('_limit', 'limit()', None),
+ ('_offset', 'offset()', None),
+ ('_order_by', 'order_by()', False),
+ ('_group_by', 'group_by()', False),
+ ('_distinct', 'distinct()', False),
+ ):
+ if getattr(self, attr) is not notset:
+ raise sa_exc.InvalidRequestError(
+ "Can't call Query.%s() when %s has been called" % (meth, methname)
+ )
+
def _get_options(self, populate_existing=None,
version_check=None,
only_load_props=None,
if synchronize_session not in [False, 'evaluate', 'fetch']:
raise sa_exc.ArgumentError("Valid strategies for session synchronization are False, 'evaluate' and 'fetch'")
+ self._no_select_modifiers("delete")
self = self.enable_eagerloads(False)
if synchronize_session not in [False, 'evaluate', 'fetch']:
raise sa_exc.ArgumentError("Valid strategies for session synchronization are False, 'evaluate' and 'fetch'")
+ self._no_select_modifiers("update")
self = self.enable_eagerloads(False)
if useobject:
attribute_ext.append(sessionlib.UOWEventHandler(prop.key))
+
for m in mapper.polymorphic_iterator():
- if (m is prop.parent or not m.concrete) and m.has_property(prop.key):
+ if prop is m._props.get(prop.key):
+
attributes.register_attribute_impl(
m.class_,
prop.key,
tag_svn_revision = true
[nosetests]
-with-sqlalchemy = true
\ No newline at end of file
+with-sqlalchemy = true
--- /dev/null
+from sqlalchemy.test import *
+import os, re
+
+
+def find_py_files(dirs):
+ for dn in dirs:
+ dn = os.path.abspath(dn)
+ for root, dirs, files in os.walk(dn):
+ for r in '.svn', 'CVS', '.git', '.hg':
+ try:
+ dirs.remove(r)
+ except ValueError:
+ pass
+
+ pyfiles = [fn for fn in files if fn.endswith('.py')]
+ if not pyfiles:
+ continue
+
+ # Find the root of the packages.
+ packroot = root
+ while 1:
+ if not os.path.exists(os.path.join(packroot, '__init__.py')):
+ break
+ packroot = os.path.dirname(packroot)
+
+ for fn in pyfiles:
+ yield os.path.join(root[len(packroot)+1:], fn)
+
+def filename_to_module_name(fn):
+ if os.path.basename(fn) == '__init__.py':
+ fn = os.path.dirname(fn)
+ return re.sub('\.py$', '', fn.replace(os.sep, '.'))
+
+def find_modules(*args):
+ for fn in find_py_files(args or ('./examples',)):
+ yield filename_to_module_name(fn)
+
+def check_import(module):
+ __import__(module)
+
+
+class ExamplesTest(TestBase):
+ def test_examples(self):
+ for module in find_modules():
+ check_import.description = module
+ yield check_import, module
if testing.against('oracle', 'firebird'):
id = Column('id', Integer, primary_key=True, test_needs_autoincrement=True)
-
+
u1 = User(name='u1', addresses=[
Address(email='one'),
Address(email='two'),
)
+class AttributeExtensionTest(_base.MappedTest):
+ @classmethod
+ def define_tables(cls, metadata):
+ Table('t1',
+ metadata,
+ Column('id', Integer, primary_key=True),
+ Column('type', String(40)),
+ Column('data', String(50))
+
+ )
+
+ @testing.resolve_artifact_names
+ def test_cascading_extensions(self):
+ ext_msg = []
+
+ class Ex1(sa.orm.AttributeExtension):
+ def set(self, state, value, oldvalue, initiator):
+ ext_msg.append("Ex1 %r" % value)
+ return "ex1" + value
+
+ class Ex2(sa.orm.AttributeExtension):
+ def set(self, state, value, oldvalue, initiator):
+ ext_msg.append("Ex2 %r" % value)
+ return "ex2" + value
+
+ class A(_base.BasicEntity):
+ pass
+ class B(A):
+ pass
+ class C(B):
+ pass
+
+ mapper(A, t1, polymorphic_on=t1.c.type, polymorphic_identity='a', properties={
+ 'data':column_property(t1.c.data, extension=Ex1())
+ })
+ mapper(B, polymorphic_identity='b', inherits=A)
+ mc = mapper(C, polymorphic_identity='c', inherits=B, properties={
+ 'data':column_property(t1.c.data, extension=Ex2())
+ })
+
+ a1 = A(data='a1')
+ b1 = B(data='b1')
+ c1 = C(data='c1')
+
+ eq_(a1.data, 'ex1a1')
+ eq_(b1.data, 'ex1b1')
+ eq_(c1.data, 'ex2c1')
+
+ a1.data = 'a2'
+ b1.data='b2'
+ c1.data = 'c2'
+ eq_(a1.data, 'ex1a2')
+ eq_(b1.data, 'ex1b2')
+ eq_(c1.data, 'ex2c2')
+
+ eq_(ext_msg, ["Ex1 'a1'", "Ex1 'b1'", "Ex2 'c1'", "Ex1 'a2'", "Ex1 'b2'", "Ex2 'c2'"])
+
+
class MapperExtensionTest(_fixtures.FixtureTest):
run_inserts = None
'user': relation(User, lazy=False, backref=backref('documents', lazy=True))
})
+ @testing.resolve_artifact_names
+ def test_illegal_operations(self):
+ s = create_session()
+
+ for q, mname in (
+ (s.query(User).limit(2), "limit"),
+ (s.query(User).offset(2), "offset"),
+ (s.query(User).limit(2).offset(2), "limit"),
+ (s.query(User).order_by(User.id), "order_by"),
+ (s.query(User).group_by(User.id), "group_by"),
+ (s.query(User).distinct(), "distinct")
+ ):
+ assert_raises_message(sa_exc.InvalidRequestError, r"Can't call Query.update\(\) when %s\(\) has been called" % mname, q.update, {'name':'ed'})
+ assert_raises_message(sa_exc.InvalidRequestError, r"Can't call Query.delete\(\) when %s\(\) has been called" % mname, q.delete)
+
+
@testing.resolve_artifact_names
def test_delete(self):
sess = create_session(bind=testing.db, autocommit=False)
magazine_page_table.c.page_id,
cast(null(), Integer).label('magazine_page_id')
]).select_from(page_table.join(magazine_page_table)),
-
).alias('pjoin')
eq_(