diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-04-11 13:03:52 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-04-11 13:03:52 -0400 |
commit | 747f5edd2192caae4ab79ff7dc9d79045b5bad0c (patch) | |
tree | ae7c2655773ca0762a11a057a0c7cfffbe659902 /lib/sqlalchemy/orm/util.py | |
parent | 5280f26d45f6d79013d2da3bd5e0fe4a88117b20 (diff) | |
download | sqlalchemy-747f5edd2192caae4ab79ff7dc9d79045b5bad0c.tar.gz |
- [feature] Added new flag to @validates
include_removes. When True, collection
remove and attribute del events
will also be sent to the validation function,
which accepts an additional argument
"is_remove" when this flag is used.
Diffstat (limited to 'lib/sqlalchemy/orm/util.py')
-rw-r--r-- | lib/sqlalchemy/orm/util.py | 52 |
1 files changed, 32 insertions, 20 deletions
diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py index 0c5f203a7..197c0c4c1 100644 --- a/lib/sqlalchemy/orm/util.py +++ b/lib/sqlalchemy/orm/util.py @@ -68,24 +68,36 @@ class CascadeOptions(frozenset): ",".join([x for x in sorted(self)]) ) -def _validator_events(desc, key, validator): +def _validator_events(desc, key, validator, include_removes): """Runs a validation method on an attribute value to be set or appended.""" - def append(state, value, initiator): - return validator(state.obj(), key, value) + if include_removes: + def append(state, value, initiator): + return validator(state.obj(), key, value, False) - def set_(state, value, oldvalue, initiator): - return validator(state.obj(), key, value) + def set_(state, value, oldvalue, initiator): + return validator(state.obj(), key, value, False) + + def remove(state, value, initiator): + validator(state.obj(), key, value, True) + else: + def append(state, value, initiator): + return validator(state.obj(), key, value) + + def set_(state, value, oldvalue, initiator): + return validator(state.obj(), key, value) event.listen(desc, 'append', append, raw=True, retval=True) event.listen(desc, 'set', set_, raw=True, retval=True) + if include_removes: + event.listen(desc, "remove", remove, raw=True, retval=True) def polymorphic_union(table_map, typecolname, aliasname='p_union', cast_nulls=True): """Create a ``UNION`` statement used by a polymorphic mapper. See :ref:`concrete_inheritance` for an example of how this is used. - + :param table_map: mapping of polymorphic identities to :class:`.Table` objects. :param typecolname: string name of a "discriminator" column, which will be @@ -236,7 +248,7 @@ class AliasedClass(object): session.query(User, user_alias).\\ join((user_alias, User.id > user_alias.id)).\\ filter(User.name==user_alias.name) - + The resulting object is an instance of :class:`.AliasedClass`, however it implements a ``__getattribute__()`` scheme which will proxy attribute access to that of the ORM class being aliased. All classmethods @@ -244,7 +256,7 @@ class AliasedClass(object): hybrids created with the :ref:`hybrids_toplevel` extension, which will receive the :class:`.AliasedClass` as the "class" argument when classmethods are called. - + :param cls: ORM mapped entity which will be "wrapped" around an alias. :param alias: a selectable, such as an :func:`.alias` or :func:`.select` construct, which will be rendered in place of the mapped table of the @@ -259,28 +271,28 @@ class AliasedClass(object): otherwise have a column that corresponds to one on the entity. The use case for this is when associating an entity with some derived selectable such as one that uses aggregate functions:: - + class UnitPrice(Base): __tablename__ = 'unit_price' ... unit_id = Column(Integer) price = Column(Numeric) - + aggregated_unit_price = Session.query( func.sum(UnitPrice.price).label('price') ).group_by(UnitPrice.unit_id).subquery() - + aggregated_unit_price = aliased(UnitPrice, alias=aggregated_unit_price, adapt_on_names=True) - + Above, functions on ``aggregated_unit_price`` which refer to ``.price`` will return the ``fund.sum(UnitPrice.price).label('price')`` column, as it is matched on the name "price". Ordinarily, the "price" function wouldn't have any "column correspondence" to the actual ``UnitPrice.price`` column as it is not a proxy of the original. - + ``adapt_on_names`` is new in 0.7.3. - + """ def __init__(self, cls, alias=None, name=None, adapt_on_names=False): self.__mapper = _class_to_mapper(cls) @@ -447,7 +459,7 @@ class _ORMJoin(expression.Join): def join(left, right, onclause=None, isouter=False, join_to_left=True): """Produce an inner join between left and right clauses. - + :func:`.orm.join` is an extension to the core join interface provided by :func:`.sql.expression.join()`, where the left and right selectables may be not only core selectable @@ -460,7 +472,7 @@ def join(left, right, onclause=None, isouter=False, join_to_left=True): in whatever form it is passed, to the selectable passed as the left side. If False, the onclause is used as is. - + :func:`.orm.join` is not commonly needed in modern usage, as its functionality is encapsulated within that of the :meth:`.Query.join` method, which features a @@ -468,22 +480,22 @@ def join(left, right, onclause=None, isouter=False, join_to_left=True): by itself. Explicit usage of :func:`.orm.join` with :class:`.Query` involves usage of the :meth:`.Query.select_from` method, as in:: - + from sqlalchemy.orm import join session.query(User).\\ select_from(join(User, Address, User.addresses)).\\ filter(Address.email_address=='foo@bar.com') - + In modern SQLAlchemy the above join can be written more succinctly as:: - + session.query(User).\\ join(User.addresses).\\ filter(Address.email_address=='foo@bar.com') See :meth:`.Query.join` for information on modern usage of ORM level joins. - + """ return _ORMJoin(left, right, onclause, isouter, join_to_left) |