diff options
Diffstat (limited to 'lib/python2.7/site-packages/SQLAlchemy-0.7.0-py2.7-linux-x86_64.egg/sqlalchemy/ext/hybrid.py')
-rwxr-xr-x | lib/python2.7/site-packages/SQLAlchemy-0.7.0-py2.7-linux-x86_64.egg/sqlalchemy/ext/hybrid.py | 425 |
1 files changed, 0 insertions, 425 deletions
diff --git a/lib/python2.7/site-packages/SQLAlchemy-0.7.0-py2.7-linux-x86_64.egg/sqlalchemy/ext/hybrid.py b/lib/python2.7/site-packages/SQLAlchemy-0.7.0-py2.7-linux-x86_64.egg/sqlalchemy/ext/hybrid.py deleted file mode 100755 index c16c38b2..00000000 --- a/lib/python2.7/site-packages/SQLAlchemy-0.7.0-py2.7-linux-x86_64.egg/sqlalchemy/ext/hybrid.py +++ /dev/null @@ -1,425 +0,0 @@ -# ext/hybrid.py -# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - -"""Define attributes on ORM-mapped classes that have "hybrid" behavior. - -"hybrid" means the attribute has distinct behaviors defined at the -class level and at the instance level. - -The :mod:`~sqlalchemy.ext.hybrid` extension provides a special form of method -decorator, is around 50 lines of code and has almost no dependencies on the rest -of SQLAlchemy. It can in theory work with any class-level expression generator. - -Consider a table ``interval`` as below:: - - from sqlalchemy import MetaData, Table, Column, Integer - - metadata = MetaData() - - interval_table = Table('interval', metadata, - Column('id', Integer, primary_key=True), - Column('start', Integer, nullable=False), - Column('end', Integer, nullable=False) - ) - -We can define higher level functions on mapped classes that produce SQL -expressions at the class level, and Python expression evaluation at the -instance level. Below, each function decorated with :func:`.hybrid_method` -or :func:`.hybrid_property` may receive ``self`` as an instance of the class, -or as the class itself:: - - from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method - from sqlalchemy.orm import mapper, Session, aliased - - class Interval(object): - def __init__(self, start, end): - self.start = start - self.end = end - - @hybrid_property - def length(self): - return self.end - self.start - - @hybrid_method - def contains(self,point): - return (self.start <= point) & (point < self.end) - - @hybrid_method - def intersects(self, other): - return self.contains(other.start) | self.contains(other.end) - - mapper(Interval, interval_table) - -Above, the ``length`` property returns the difference between the ``end`` and -``start`` attributes. With an instance of ``Interval``, this subtraction occurs -in Python, using normal Python descriptor mechanics:: - - >>> i1 = Interval(5, 10) - >>> i1.length - 5 - -At the class level, the usual descriptor behavior of returning the descriptor -itself is modified by :class:`.hybrid_property`, to instead evaluate the function -body given the ``Interval`` class as the argument:: - - >>> print Interval.length - interval."end" - interval.start - - >>> print Session().query(Interval).filter(Interval.length > 10) - SELECT interval.id AS interval_id, interval.start AS interval_start, - interval."end" AS interval_end - FROM interval - WHERE interval."end" - interval.start > :param_1 - -ORM methods such as :meth:`~.Query.filter_by` generally use ``getattr()`` to -locate attributes, so can also be used with hybrid attributes:: - - >>> print Session().query(Interval).filter_by(length=5) - SELECT interval.id AS interval_id, interval.start AS interval_start, - interval."end" AS interval_end - FROM interval - WHERE interval."end" - interval.start = :param_1 - -The ``contains()`` and ``intersects()`` methods are decorated with :class:`.hybrid_method`. -This decorator applies the same idea to methods which accept -zero or more arguments. The above methods return boolean values, and take advantage -of the Python ``|`` and ``&`` bitwise operators to produce equivalent instance-level and -SQL expression-level boolean behavior:: - - >>> i1.contains(6) - True - >>> i1.contains(15) - False - >>> i1.intersects(Interval(7, 18)) - True - >>> i1.intersects(Interval(25, 29)) - False - - >>> print Session().query(Interval).filter(Interval.contains(15)) - SELECT interval.id AS interval_id, interval.start AS interval_start, - interval."end" AS interval_end - FROM interval - WHERE interval.start <= :start_1 AND interval."end" > :end_1 - - >>> ia = aliased(Interval) - >>> print Session().query(Interval, ia).filter(Interval.intersects(ia)) - SELECT interval.id AS interval_id, interval.start AS interval_start, - interval."end" AS interval_end, interval_1.id AS interval_1_id, - interval_1.start AS interval_1_start, interval_1."end" AS interval_1_end - FROM interval, interval AS interval_1 - WHERE interval.start <= interval_1.start - AND interval."end" > interval_1.start - OR interval.start <= interval_1."end" - AND interval."end" > interval_1."end" - -Defining Expression Behavior Distinct from Attribute Behavior --------------------------------------------------------------- - -Our usage of the ``&`` and ``|`` bitwise operators above was fortunate, considering -our functions operated on two boolean values to return a new one. In many cases, the construction -of an in-Python function and a SQLAlchemy SQL expression have enough differences that two -separate Python expressions should be defined. The :mod:`~sqlalchemy.ext.hybrid` decorators -define the :meth:`.hybrid_property.expression` modifier for this purpose. As an example we'll -define the radius of the interval, which requires the usage of the absolute value function:: - - from sqlalchemy import func - - class Interval(object): - # ... - - @hybrid_property - def radius(self): - return abs(self.length) / 2 - - @radius.expression - def radius(cls): - return func.abs(cls.length) / 2 - -Above the Python function ``abs()`` is used for instance-level operations, the SQL function -``ABS()`` is used via the :attr:`.func` object for class-level expressions:: - - >>> i1.radius - 2 - - >>> print Session().query(Interval).filter(Interval.radius > 5) - SELECT interval.id AS interval_id, interval.start AS interval_start, - interval."end" AS interval_end - FROM interval - WHERE abs(interval."end" - interval.start) / :abs_1 > :param_1 - -Defining Setters ----------------- - -Hybrid properties can also define setter methods. If we wanted ``length`` above, when -set, to modify the endpoint value:: - - class Interval(object): - # ... - - @hybrid_property - def length(self): - return self.end - self.start - - @length.setter - def length(self, value): - self.end = self.start + value - -The ``length(self, value)`` method is now called upon set:: - - >>> i1 = Interval(5, 10) - >>> i1.length - 5 - >>> i1.length = 12 - >>> i1.end - 17 - -Working with Relationships --------------------------- - -There's no essential difference when creating hybrids that work with related objects as -opposed to column-based data. The need for distinct expressions tends to be greater. -Consider the following declarative mapping which relates a ``User`` to a ``SavingsAccount``:: - - from sqlalchemy import Column, Integer, ForeignKey, Numeric, String - from sqlalchemy.orm import relationship - from sqlalchemy.ext.declarative import declarative_base - from sqlalchemy.ext.hybrid import hybrid_property - - Base = declarative_base() - - class SavingsAccount(Base): - __tablename__ = 'account' - id = Column(Integer, primary_key=True) - user_id = Column(Integer, ForeignKey('user.id'), nullable=False) - balance = Column(Numeric(15, 5)) - - class User(Base): - __tablename__ = 'user' - id = Column(Integer, primary_key=True) - name = Column(String(100), nullable=False) - - accounts = relationship("SavingsAccount", backref="owner") - - @hybrid_property - def balance(self): - if self.accounts: - return self.accounts[0].balance - else: - return None - - @balance.setter - def balance(self, value): - if not self.accounts: - account = Account(owner=self) - else: - account = self.accounts[0] - account.balance = balance - - @balance.expression - def balance(cls): - return SavingsAccount.balance - -The above hybrid property ``balance`` works with the first ``SavingsAccount`` entry in the list of -accounts for this user. The in-Python getter/setter methods can treat ``accounts`` as a Python -list available on ``self``. - -However, at the expression level, we can't travel along relationships to column attributes -directly since SQLAlchemy is explicit about joins. So here, it's expected that the ``User`` class will be -used in an appropriate context such that an appropriate join to ``SavingsAccount`` will be present:: - - >>> print Session().query(User, User.balance).join(User.accounts).filter(User.balance > 5000) - SELECT "user".id AS user_id, "user".name AS user_name, account.balance AS account_balance - FROM "user" JOIN account ON "user".id = account.user_id - WHERE account.balance > :balance_1 - -Note however, that while the instance level accessors need to worry about whether ``self.accounts`` -is even present, this issue expresses itself differently at the SQL expression level, where we basically -would use an outer join:: - - >>> from sqlalchemy import or_ - >>> print (Session().query(User, User.balance).outerjoin(User.accounts). - ... filter(or_(User.balance < 5000, User.balance == None))) - SELECT "user".id AS user_id, "user".name AS user_name, account.balance AS account_balance - FROM "user" LEFT OUTER JOIN account ON "user".id = account.user_id - WHERE account.balance < :balance_1 OR account.balance IS NULL - -.. _hybrid_custom_comparators: - -Building Custom Comparators ---------------------------- - -The hybrid property also includes a helper that allows construction of custom comparators. -A comparator object allows one to customize the behavior of each SQLAlchemy expression -operator individually. They are useful when creating custom types that have -some highly idiosyncratic behavior on the SQL side. - -The example class below allows case-insensitive comparisons on the attribute -named ``word_insensitive``:: - - from sqlalchemy.ext.hybrid import Comparator - - class CaseInsensitiveComparator(Comparator): - def __eq__(self, other): - return func.lower(self.__clause_element__()) == func.lower(other) - - class SearchWord(Base): - __tablename__ = 'searchword' - id = Column(Integer, primary_key=True) - word = Column(String(255), nullable=False) - - @hybrid_property - def word_insensitive(self): - return self.word.lower() - - @word_insensitive.comparator - def word_insensitive(cls): - return CaseInsensitiveComparator(cls.word) - -Above, SQL expressions against ``word_insensitive`` will apply the ``LOWER()`` -SQL function to both sides:: - - >>> print Session().query(SearchWord).filter_by(word_insensitive="Trucks") - SELECT searchword.id AS searchword_id, searchword.word AS searchword_word - FROM searchword - WHERE lower(searchword.word) = lower(:lower_1) - -""" -from sqlalchemy import util -from sqlalchemy.orm import attributes, interfaces - -class hybrid_method(object): - """A decorator which allows definition of a Python object method with both - instance-level and class-level behavior. - - """ - - - def __init__(self, func, expr=None): - """Create a new :class:`.hybrid_method`. - - Usage is typically via decorator:: - - from sqlalchemy.ext.hybrid import hybrid_method - - class SomeClass(object): - @hybrid_method - def value(self, x, y): - return self._value + x + y - - @value.expression - def value(self, x, y): - return func.some_function(self._value, x, y) - - """ - self.func = func - self.expr = expr or func - - def __get__(self, instance, owner): - if instance is None: - return self.expr.__get__(owner, owner.__class__) - else: - return self.func.__get__(instance, owner) - - def expression(self, expr): - """Provide a modifying decorator that defines a SQL-expression producing method.""" - - self.expr = expr - return self - -class hybrid_property(object): - """A decorator which allows definition of a Python descriptor with both - instance-level and class-level behavior. - - """ - - def __init__(self, fget, fset=None, fdel=None, expr=None): - """Create a new :class:`.hybrid_property`. - - Usage is typically via decorator:: - - from sqlalchemy.ext.hybrid import hybrid_property - - class SomeClass(object): - @hybrid_property - def value(self): - return self._value - - @value.setter - def value(self, value): - self._value = value - - """ - self.fget = fget - self.fset = fset - self.fdel = fdel - self.expr = expr or fget - util.update_wrapper(self, fget) - - def __get__(self, instance, owner): - if instance is None: - return self.expr(owner) - else: - return self.fget(instance) - - def __set__(self, instance, value): - self.fset(instance, value) - - def __delete__(self, instance): - self.fdel(instance) - - def setter(self, fset): - """Provide a modifying decorator that defines a value-setter method.""" - - self.fset = fset - return self - - def deleter(self, fdel): - """Provide a modifying decorator that defines a value-deletion method.""" - - self.fdel = fdel - return self - - def expression(self, expr): - """Provide a modifying decorator that defines a SQL-expression producing method.""" - - self.expr = expr - return self - - def comparator(self, comparator): - """Provide a modifying decorator that defines a custom comparator producing method. - - The return value of the decorated method should be an instance of - :class:`~.hybrid.Comparator`. - - """ - - proxy_attr = attributes.\ - create_proxied_attribute(self) - def expr(owner): - return proxy_attr(owner, self.__name__, self, comparator(owner)) - self.expr = expr - return self - - -class Comparator(interfaces.PropComparator): - """A helper class that allows easy construction of custom :class:`~.orm.interfaces.PropComparator` - classes for usage with hybrids.""" - - - def __init__(self, expression): - self.expression = expression - - def __clause_element__(self): - expr = self.expression - while hasattr(expr, '__clause_element__'): - expr = expr.__clause_element__() - return expr - - def adapted(self, adapter): - # interesting.... - return self - - |