Re: [Sqlalchemy-tickets] [sqlalchemy] #2751: assocaition proxy support for scalar-targeted attribut
Brought to you by:
zzzeek
|
From: sqlalchemy <mi...@zz...> - 2013-06-08 17:33:07
|
#2751: assocaition proxy support for scalar-targeted attributes
-----------------------------------+----------------------------------
Reporter: zzzeek | Owner: zzzeek
Type: defect | Status: closed
Priority: high | Milestone: 0.9.0
Component: ext | Severity: minor - half an hour
Resolution: fixed | Keywords:
Progress State: completed/closed |
-----------------------------------+----------------------------------
Description changed by zzzeek:
Old description:
> {{{
> #!python
> from sqlalchemy.engine import create_engine
> from sqlalchemy.ext.associationproxy import association_proxy
> from sqlalchemy.ext.declarative.api import declarative_base
> from sqlalchemy.orm import relationship
> from sqlalchemy.orm.session import sessionmaker
> from sqlalchemy.schema import Column, ForeignKey
> from sqlalchemy.types import Integer, String
>
> Base = declarative_base()
>
> class A(Base):
> __tablename__ = 'table_a'
> id = Column(Integer, primary_key=True)
> color = Column(String)
> def __init__(self, color):
> self.color = color
>
> class B(Base):
> __tablename__ = 'table_b'
> id = Column(Integer, primary_key=True)
> a_id = Column(Integer, ForeignKey('table_a.id'))
> a_re = relationship('A', backref='b_re')
> a_color = association_proxy('a_re', 'color')
>
> if __name__ == '__main__':
> engine = create_engine('sqlite:///:memory:', echo=True)
> Session = sessionmaker(engine)
> session = Session()
> Base.metadata.create_all(engine)
>
> b1 = B()
> b2 = B()
> b3 = B()
> b4 = B()
>
> b1.a_color = 'blue'
> b2.a_re = A(color=None)
>
> session.add_all([b1, b2, b3, b4])
>
> q = session.query(B).filter(B.a_color == None).all()
> p = session.query(B).filter(B.a_color != None).all()
> v = session.query(B).filter(B.a_color == 'blue').all()
>
> from sqlalchemy import __version__
> if __version__ >= "0.9.0":
>
> assert v == [b1]
> assert q == [b2, b3, b4]
> assert p == [b1]
>
> # will also add a special feature for this
> r = session.query(B).filter(B.a_color.has()).all()
> assert r == [b1, b2]
>
> # will also add a special feature for this
> w = session.query(B).filter(~B.a_color.has()).all()
> assert w == [b3, b4]
>
> # this is the equivalent of 0.8's B.a_color != None
> s = session.query(B).filter(
> or_(B.a_color != None,
> ~B.a_color.has())
> ).all()
> assert s == [b1, b3, b4]
> else:
> assert v == [b1]
> assert q == [b2]
> assert p == [b1, b3, b4]
> }}}
>
> this involves a behavioral change even to non-scalar association proxies,
> unless we special case it. If we have
> Parent.m2o_something.m2o_otherthing / Parent.other, the meaning of
> `Parent.other == None` normally includes just
> `Parent.m2o_something.has(m2o_otherthing=None)` - this adds in
> `~Parent.m2o_something.has()`.
>
> As the example illustrates, this totally changes return results so
> there's a very high chance applications are relying on the current
> behavior, and am leaning towards an 0.9 only here.
New description:
{{{
#!python
from sqlalchemy.engine import create_engine
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.declarative.api import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.orm.session import sessionmaker
from sqlalchemy.schema import Column, ForeignKey
from sqlalchemy.types import Integer, String
Base = declarative_base()
class A(Base):
__tablename__ = 'table_a'
id = Column(Integer, primary_key=True)
color = Column(String)
def __init__(self, color):
self.color = color
class B(Base):
__tablename__ = 'table_b'
id = Column(Integer, primary_key=True)
a_id = Column(Integer, ForeignKey('table_a.id'))
a_re = relationship('A', backref='b_re')
a_color = association_proxy('a_re', 'color')
if __name__ == '__main__':
engine = create_engine('sqlite:///:memory:', echo=True)
Session = sessionmaker(engine)
session = Session()
Base.metadata.create_all(engine)
b1 = B()
b2 = B()
b3 = B()
b4 = B()
b1.a_color = 'blue'
b2.a_re = A(color=None)
session.add_all([b1, b2, b3, b4])
q = session.query(B).filter(B.a_color == None).all()
p = session.query(B).filter(B.a_color != None).all()
v = session.query(B).filter(B.a_color == 'blue').all()
z = session.query(B).filter(B.a_color != 'blue').all()
from sqlalchemy import __version__
if __version__ >= "0.9.0":
assert v == [b1]
assert q == [b2, b3]
assert p == [b1, b4]
assert z == [b4]
# will also add a special feature for this
r = session.query(B).filter(B.a_color.has()).all()
assert r == [b1, b2, b4]
# will also add a special feature for this
w = session.query(B).filter(~B.a_color.has()).all()
assert w == [b3]
# this is the equivalent of 0.8's B.a_color != None
s = session.query(B).filter(
or_(B.a_color != None,
~B.a_color.has())
).all()
assert s == [b1, b3, b4]
# the equivalent of 0.8's B.a_color != 'blue'
t = session.query(B).filter(
or_(
B.a_color != 'blue',
B.a_color == None
)
).all()
assert t == [b2, b3, b4]
else:
assert v == [b1]
assert q == [b2]
assert p == [b1, b3, b4]
assert z == [b2, b3, b4]
}}}
this involves a behavioral change even to non-scalar association proxies,
unless we special case it. If we have
Parent.m2o_something.m2o_otherthing / Parent.other, the meaning of
`Parent.other == None` normally includes just
`Parent.m2o_something.has(m2o_otherthing=None)` - this adds in
`~Parent.m2o_something.has()`.
As the example illustrates, this totally changes return results so there's
a very high chance applications are relying on the current behavior, and
am leaning towards an 0.9 only here.
--
--
Ticket URL: <http://www.sqlalchemy.org/trac/ticket/2751#comment:4>
sqlalchemy <http://www.sqlalchemy.org/>
The Database Toolkit for Python
|