if Python unicodes are passed under
certain circumstances.
+- examples
+ - Repaired the examples/versioning test runner
+ to not rely upon SQLAlchemy test libs,
+ nosetests must be run from within
+ examples/versioning to get around setup.cfg
+ breaking it.
+
+ - Tweak to examples/versioning to pick the
+ correct foreign key in a multi-level
+ inheritance situation.
+
0.7.1
=====
- general
self-referential relationships - this since
SQLA 0.7 no longer enforces "parent with no
child" at the ORM level; this check is left
- up to foreign key nullability.
+ up to foreign key nullability.
Related to [ticket:1912]
- Repaired new "mutable" extension to propagate
- Adjusted the __contains__() method of
a RowProxy result row such that no exception
- throw is generated internally;
+ throw is generated internally;
NoSuchColumnError() also will generate its
message regardless of whether or not the column
- construct can be coerced to a string.
+ construct can be coerced to a string.
[ticket:2178]. Also in 0.6.8.
- sqlite
MATCH operator. A potential floating-point
inaccuracy issue was fixed, and certain tests
of the MATCH operator only execute within an
- EN-oriented locale for now. [ticket:2175].
+ EN-oriented locale for now. [ticket:2175].
Also in 0.6.8.
- mysql
- It is an error to call query.get() when the
given entity is not a single, full class
entity or mapper (i.e. a column). This is
- a deprecation warning in 0.6.8.
+ a deprecation warning in 0.6.8.
[ticket:2144]
- Fixed a potential KeyError which under some
- The changelog for 0.6.7 and subsequent within
the 0.6 series is now listed only in the
- CHANGES file within the 0.6 branch.
+ CHANGES file within the 0.6 branch.
In the 0.7 CHANGES file (i.e. this file), all the
0.6 changes are listed inline within the 0.7
section in which they were also applied
the given objects don't have any inter-attribute
references in memory, which was the behavior in
0.5 and earlier, so a flush of Parent/Child with
- only foreign key/primary key set will succeed.
+ only foreign key/primary key set will succeed.
This while still maintaining 0.6 and above's not
generating a ton of useless internal dependency
structures within the flush that don't correspond
- Improvements to the error messages emitted when
querying against column-only entities in conjunction
with (typically incorrectly) using loader options,
- where the parent entity is not fully present.
+ where the parent entity is not fully present.
[ticket:2069]
- Fixed bug in query.options() whereby a path
a Sequence object as its argument and renders the
appropriate "next value" generation string on the
target platform, if supported. Also provides
- ".next_value()" method on Sequence itself.
+ ".next_value()" method on Sequence itself.
[ticket:2085]
- func.next_value() or other SQL expression can
- Established consistency when server_default is present
on an Integer PK column. SQLA doesn't pre-fetch these,
- nor do they come back in cursor.lastrowid (DBAPI).
+ nor do they come back in cursor.lastrowid (DBAPI).
Ensured all backends consistently return None
in result.inserted_primary_key for these. Regarding
reflection for this case, reflection of an int PK col
Usage is illustrated via a unit test module ``test_versioning.py``, which can
be run via nose::
- nosetests -w examples/versioning/
+ cd examples/versioning
+ nosetests -v
A fragment of example usage, using declarative::
--- /dev/null
+"""copy of ComparableEntity and eq_() from test.lib.
+
+This is just to support running the example outside of
+the SQLA testing environment which is no longer part of
+SQLAlchemy as of 0.7.
+
+"""
+
+import sqlalchemy as sa
+from sqlalchemy import exc as sa_exc
+
+
+def eq_(a, b, msg=None):
+ """Assert a == b, with repr messaging on failure."""
+ assert a == b, msg or "%r != %r" % (a, b)
+
+_repr_stack = set()
+class BasicEntity(object):
+ def __init__(self, **kw):
+ for key, value in kw.iteritems():
+ setattr(self, key, value)
+
+ def __repr__(self):
+ if id(self) in _repr_stack:
+ return object.__repr__(self)
+ _repr_stack.add(id(self))
+ try:
+ return "%s(%s)" % (
+ (self.__class__.__name__),
+ ', '.join(["%s=%r" % (key, getattr(self, key))
+ for key in sorted(self.__dict__.keys())
+ if not key.startswith('_')]))
+ finally:
+ _repr_stack.remove(id(self))
+
+_recursion_stack = set()
+class ComparableEntity(BasicEntity):
+ def __hash__(self):
+ return hash(self.__class__)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __eq__(self, other):
+ """'Deep, sparse compare.
+
+ Deeply compare two entities, following the non-None attributes of the
+ non-persisted object, if possible.
+
+ """
+ if other is self:
+ return True
+ elif not self.__class__ == other.__class__:
+ return False
+
+ if id(self) in _recursion_stack:
+ return True
+ _recursion_stack.add(id(self))
+
+ try:
+ # pick the entity thats not SA persisted as the source
+ try:
+ self_key = sa.orm.attributes.instance_state(self).key
+ except sa.orm.exc.NO_STATE:
+ self_key = None
+
+ if other is None:
+ a = self
+ b = other
+ elif self_key is not None:
+ a = other
+ b = self
+ else:
+ a = self
+ b = other
+
+ for attr in a.__dict__.keys():
+ if attr.startswith('_'):
+ continue
+ value = getattr(a, attr)
+
+ try:
+ # handle lazy loader errors
+ battr = getattr(b, attr)
+ except (AttributeError, sa_exc.UnboundExecutionError):
+ return False
+
+ if hasattr(value, '__iter__'):
+ if list(value) != list(battr):
+ return False
+ else:
+ if value is not None and value != battr:
+ return False
+ return True
+ finally:
+ _recursion_stack.remove(id(self))
col.unique = False
if super_mapper and col_references_table(column, super_mapper.local_table):
- super_fks.append((col.key, list(super_history_mapper.base_mapper.local_table.primary_key)[0]))
+ super_fks.append((col.key, list(super_history_mapper.local_table.primary_key)[0]))
cols.append(col)
+from unittest import TestCase
from sqlalchemy.ext.declarative import declarative_base
from history_meta import VersionedMeta, VersionedListener
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import clear_mappers, sessionmaker, deferred, relationship
-from test.lib.testing import TestBase, eq_
-from test.lib.entities import ComparableEntity
+from _lib import ComparableEntity, eq_
def setup():
global engine
engine = create_engine('sqlite://', echo=True)
-class TestVersioning(fixtures.TestBase):
- def setup(self):
+class TestVersioning(TestCase):
+ def setUp(self):
global Base, Session, Versioned
Base = declarative_base(bind=engine)
class Versioned(object):
_decl_class_registry = Base._decl_class_registry
Session = sessionmaker(extension=VersionedListener())
- def teardown(self):
+ def tearDown(self):
clear_mappers()
Base.metadata.drop_all()