From: Mike Bayer Date: Fri, 28 Mar 2014 14:49:37 +0000 (-0400) Subject: - Added support to automap for the case where a relationship should X-Git-Tag: rel_0_9_4~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4d93a52e77895f87570b00f8f0c9b4088d078695;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Added support to automap for the case where a relationship should not be created between two classes that are in a joined inheritance relationship, for those foreign keys that link the subclass back to the superclass. fixes #3004 --- diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst index 321deb6473..d0116b3336 100644 --- a/doc/build/changelog/changelog_09.rst +++ b/doc/build/changelog/changelog_09.rst @@ -14,6 +14,15 @@ .. changelog:: :version: 0.9.4 + .. change:: + :tags: bug, ext, automap + :tickets: 3004 + + Added support to automap for the case where a relationship should + not be created between two classes that are in a joined inheritance + relationship, for those foreign keys that link the subclass back to + the superclass. + .. change:: :tags: bug, orm :tickets: 2948 diff --git a/lib/sqlalchemy/ext/automap.py b/lib/sqlalchemy/ext/automap.py index b98ac17073..1f9e00a12f 100644 --- a/lib/sqlalchemy/ext/automap.py +++ b/lib/sqlalchemy/ext/automap.py @@ -325,6 +325,61 @@ is as follows: many-to-one; the :func:`.generate_relationship` function is called upon to generate the strucures and existing attributes will be maintained. +Relationships with Inheritance +------------------------------ + +:mod:`.sqlalchemy.ext.automap` will not generate any relationships between +two classes that are in an inheritance relationship. That is, with two classes +given as follows:: + + class Employee(Base): + __tablename__ = 'employee' + id = Column(Integer, primary_key=True) + type = Column(String(50)) + __mapper_args__ = { + 'polymorphic_identity':'employee', 'polymorphic_on': type + } + + class Engineer(Employee): + __tablename__ = 'engineer' + id = Column(Integer, ForeignKey('employee.id'), primary_key=True) + __mapper_args__ = { + 'polymorphic_identity':'engineer', + } + +The foreign key from ``Engineer`` to ``Employee`` is used not for a relationship, +but to establish joined inheritance between the two classes. + +Note that this means automap will not generate **any relationships** that are +between these two classes, nor for any other classes in the same hierarchy. +If there are actually relationships between classes in the hierarchy, they +must be declared explicitly. Below, as we have two separate foreign keys +from ``Engineer`` to ``Employee``, we need to set up both the relationship +we want as well as the ``inherit_condition``, as these are not things +SQLAlchemy can guess:: + + class Employee(Base): + __tablename__ = 'employee' + id = Column(Integer, primary_key=True) + type = Column(String(50)) + + __mapper_args__ = { + 'polymorphic_identity':'employee', 'polymorphic_on':type + } + + class Engineer(Employee): + __tablename__ = 'engineer' + id = Column(Integer, ForeignKey('employee.id'), primary_key=True) + favorite_employee_id = Column(Integer, ForeignKey('employee.id')) + + favorite_employee = relationship(Employee, foreign_keys=favorite_employee_id) + + __mapper_args__ = { + 'polymorphic_identity':'engineer', + 'inherit_condition': id == Employee.id + } + + Using Automap with Explicit Declarations ======================================== @@ -731,6 +786,9 @@ def _relationships_for_fks(automap_base, map_config, table_to_map_config, continue referred_cls = referred_cfg.cls + if local_cls is not referred_cls and issubclass(local_cls, referred_cls): + continue + relationship_name = name_for_scalar_relationship( automap_base, local_cls, @@ -752,8 +810,7 @@ def _relationships_for_fks(automap_base, map_config, table_to_map_config, collection_class=collection_class) else: backref_obj = None - map_config.properties[relationship_name] = \ - generate_relationship(automap_base, + rel = generate_relationship(automap_base, interfaces.MANYTOONE, relationship, relationship_name, @@ -762,11 +819,12 @@ def _relationships_for_fks(automap_base, map_config, table_to_map_config, backref=backref_obj, remote_side=[fk.column for fk in constraint.elements] ) - if not create_backref: - referred_cfg.properties[backref_name].back_populates = relationship_name + if rel is not None: + map_config.properties[relationship_name] = rel + if not create_backref: + referred_cfg.properties[backref_name].back_populates = relationship_name elif create_backref: - referred_cfg.properties[backref_name] = \ - generate_relationship(automap_base, + rel = generate_relationship(automap_base, interfaces.ONETOMANY, relationship, backref_name, @@ -774,7 +832,9 @@ def _relationships_for_fks(automap_base, map_config, table_to_map_config, foreign_keys=[fk.parent for fk in constraint.elements], back_populates=relationship_name, collection_class=collection_class) - map_config.properties[relationship_name].back_populates = backref_name + if rel is not None: + referred_cfg.properties[backref_name] = rel + map_config.properties[relationship_name].back_populates = backref_name def _m2m_relationship(automap_base, lcl_m2m, rem_m2m, m2m_const, table, table_to_map_config, @@ -815,8 +875,7 @@ def _m2m_relationship(automap_base, lcl_m2m, rem_m2m, m2m_const, table, ) else: backref_obj = None - map_config.properties[relationship_name] = \ - generate_relationship(automap_base, + rel = generate_relationship(automap_base, interfaces.MANYTOMANY, relationship, relationship_name, @@ -827,11 +886,13 @@ def _m2m_relationship(automap_base, lcl_m2m, rem_m2m, m2m_const, table, backref=backref_obj, collection_class=collection_class ) - if not create_backref: - referred_cfg.properties[backref_name].back_populates = relationship_name + if rel is not None: + map_config.properties[relationship_name] = rel + + if not create_backref: + referred_cfg.properties[backref_name].back_populates = relationship_name elif create_backref: - referred_cfg.properties[backref_name] = \ - generate_relationship(automap_base, + rel = generate_relationship(automap_base, interfaces.MANYTOMANY, relationship, backref_name, @@ -841,4 +902,6 @@ def _m2m_relationship(automap_base, lcl_m2m, rem_m2m, m2m_const, table, secondaryjoin=and_(fk.column == fk.parent for fk in m2m_const[0].elements), back_populates=relationship_name, collection_class=collection_class) - map_config.properties[relationship_name].back_populates = backref_name + if rel is not None: + referred_cfg.properties[backref_name] = rel + map_config.properties[relationship_name].back_populates = backref_name diff --git a/test/ext/test_automap.py b/test/ext/test_automap.py index 3a2d4d31c7..da80f377ea 100644 --- a/test/ext/test_automap.py +++ b/test/ext/test_automap.py @@ -208,4 +208,11 @@ class AutomapInhTest(fixtures.MappedTest): assert SubJoined.__mapper__.inherits is Joined.__mapper__ + assert not Joined.__mapper__.relationships + assert not SubJoined.__mapper__.relationships + def test_conditional_relationship(self): + Base = automap_base() + def _gen_relationship(*arg, **kw): + return None + Base.prepare(engine=testing.db, reflect=True, generate_relationship=_gen_relationship)