mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-05-23 17:11:39 -04:00
- A quasi-regression where apparently in 0.8 you can set a class-level
attribute on declarative to simply refer directly to an :class:`.InstrumentedAttribute`
on a superclass or on the class itself, and it
acts more or less like a synonym; in 0.9, this fails to set up enough
bookkeeping to keep up with the more liberalized backref logic
from 🎫`2789`. Even though this use case was never directly
considered, it is now detected by declarative at the "setattr()" level
as well as when setting up a subclass, and the mirrored/renamed attribute
is now set up as a :func:`.synonym` instead. [ticket:2900]
This commit is contained in:
Vendored
+14
@@ -14,6 +14,20 @@
|
||||
.. changelog::
|
||||
:version: 0.9.1
|
||||
|
||||
.. change::
|
||||
:tags: bug, orm, declarative
|
||||
:tickets: 2900
|
||||
|
||||
A quasi-regression where apparently in 0.8 you can set a class-level
|
||||
attribute on declarative to simply refer directly to an :class:`.InstrumentedAttribute`
|
||||
on a superclass or on the class itself, and it
|
||||
acts more or less like a synonym; in 0.9, this fails to set up enough
|
||||
bookkeeping to keep up with the more liberalized backref logic
|
||||
from :ticket:`2789`. Even though this use case was never directly
|
||||
considered, it is now detected by declarative at the "setattr()" level
|
||||
as well as when setting up a subclass, and the mirrored/renamed attribute
|
||||
is now set up as a :func:`.synonym` instead.
|
||||
|
||||
.. change::
|
||||
:tags: bug, orm
|
||||
:tickets: 2903
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
"""Internal implementation for declarative."""
|
||||
|
||||
from ...schema import Table, Column
|
||||
from ...orm import mapper, class_mapper
|
||||
from ...orm import mapper, class_mapper, synonym
|
||||
from ...orm.interfaces import MapperProperty
|
||||
from ...orm.properties import ColumnProperty, CompositeProperty
|
||||
from ...orm.attributes import QueryableAttribute
|
||||
from ...orm.base import _is_mapped_class
|
||||
from ... import util, exc
|
||||
from ...sql import expression
|
||||
@@ -148,6 +149,15 @@ def _as_declarative(cls, classname, dict_):
|
||||
if isinstance(value, declarative_props):
|
||||
value = getattr(cls, k)
|
||||
|
||||
elif isinstance(value, QueryableAttribute) and \
|
||||
value.class_ is not cls and \
|
||||
value.key != k:
|
||||
# detect a QueryableAttribute that's already mapped being
|
||||
# assigned elsewhere in userland, turn into a synonym()
|
||||
value = synonym(value.key)
|
||||
setattr(cls, k, value)
|
||||
|
||||
|
||||
if (isinstance(value, tuple) and len(value) == 1 and
|
||||
isinstance(value[0], (Column, MapperProperty))):
|
||||
util.warn("Ignoring declarative-like tuple value of attribute "
|
||||
@@ -397,6 +407,7 @@ def _add_attribute(cls, key, value):
|
||||
adds it to the Mapper, adds a column to the mapped Table, etc.
|
||||
|
||||
"""
|
||||
|
||||
if '__mapper__' in cls.__dict__:
|
||||
if isinstance(value, Column):
|
||||
_undefer_column_name(key, value)
|
||||
@@ -413,6 +424,14 @@ def _add_attribute(cls, key, value):
|
||||
key,
|
||||
clsregistry._deferred_relationship(cls, value)
|
||||
)
|
||||
elif isinstance(value, QueryableAttribute) and value.key != key:
|
||||
# detect a QueryableAttribute that's already mapped being
|
||||
# assigned elsewhere in userland, turn into a synonym()
|
||||
value = synonym(value.key)
|
||||
cls.__mapper__.add_property(
|
||||
key,
|
||||
clsregistry._deferred_relationship(cls, value)
|
||||
)
|
||||
else:
|
||||
type.__setattr__(cls, key, value)
|
||||
else:
|
||||
|
||||
@@ -11,7 +11,7 @@ from sqlalchemy.testing.schema import Table, Column
|
||||
from sqlalchemy.orm import relationship, create_session, class_mapper, \
|
||||
joinedload, configure_mappers, backref, clear_mappers, \
|
||||
deferred, column_property, composite,\
|
||||
Session
|
||||
Session, properties
|
||||
from sqlalchemy.testing import eq_
|
||||
from sqlalchemy.util import classproperty, with_metaclass
|
||||
from sqlalchemy.ext.declarative import declared_attr, AbstractConcreteBase, \
|
||||
@@ -792,6 +792,64 @@ class DeclarativeTest(DeclarativeTestBase):
|
||||
eq_(a1, Address(email='two'))
|
||||
eq_(a1.user, User(name='u1'))
|
||||
|
||||
def test_alt_name_attr_subclass_column_inline(self):
|
||||
# [ticket:2900]
|
||||
class A(Base):
|
||||
__tablename__ = 'a'
|
||||
id = Column('id', Integer, primary_key=True)
|
||||
data = Column('data')
|
||||
|
||||
class ASub(A):
|
||||
brap = A.data
|
||||
assert ASub.brap.property is A.data.property
|
||||
assert isinstance(ASub.brap.original_property, properties.SynonymProperty)
|
||||
|
||||
def test_alt_name_attr_subclass_relationship_inline(self):
|
||||
# [ticket:2900]
|
||||
class A(Base):
|
||||
__tablename__ = 'a'
|
||||
id = Column('id', Integer, primary_key=True)
|
||||
b_id = Column(Integer, ForeignKey('b.id'))
|
||||
b = relationship("B", backref="as_")
|
||||
|
||||
class B(Base):
|
||||
__tablename__ = 'b'
|
||||
id = Column('id', Integer, primary_key=True)
|
||||
|
||||
configure_mappers()
|
||||
class ASub(A):
|
||||
brap = A.b
|
||||
assert ASub.brap.property is A.b.property
|
||||
assert isinstance(ASub.brap.original_property, properties.SynonymProperty)
|
||||
ASub(brap=B())
|
||||
|
||||
def test_alt_name_attr_subclass_column_attrset(self):
|
||||
# [ticket:2900]
|
||||
class A(Base):
|
||||
__tablename__ = 'a'
|
||||
id = Column('id', Integer, primary_key=True)
|
||||
data = Column('data')
|
||||
A.brap = A.data
|
||||
assert A.brap.property is A.data.property
|
||||
assert isinstance(A.brap.original_property, properties.SynonymProperty)
|
||||
|
||||
def test_alt_name_attr_subclass_relationship_attrset(self):
|
||||
# [ticket:2900]
|
||||
class A(Base):
|
||||
__tablename__ = 'a'
|
||||
id = Column('id', Integer, primary_key=True)
|
||||
b_id = Column(Integer, ForeignKey('b.id'))
|
||||
b = relationship("B", backref="as_")
|
||||
A.brap = A.b
|
||||
class B(Base):
|
||||
__tablename__ = 'b'
|
||||
id = Column('id', Integer, primary_key=True)
|
||||
|
||||
assert A.brap.property is A.b.property
|
||||
assert isinstance(A.brap.original_property, properties.SynonymProperty)
|
||||
A(brap=B())
|
||||
|
||||
|
||||
def test_eager_order_by(self):
|
||||
|
||||
class Address(Base, fixtures.ComparableEntity):
|
||||
|
||||
Reference in New Issue
Block a user