使用SQLAlchemy基于字段值实例化父类或子类

时间:2013-10-09 15:23:21

标签: python inheritance sqlalchemy

使用SQLAlchemy,我有一个表示表的类。我有一个继承自第一个类的第二个类并覆盖了一些方法。使用哪个类取决于另一个表中的值。我应该如何安排我的代码,以便SQLAlchemy在执行查询或访问关系属性时为我创建正确的类?

我的代码看起来像这样:

class Thing(Base):                                                                                                                                                                                             
    ...                                                                                                                                                                                                        
    relevant_property = Column(Bool)                                                                                                                                                                           


class RelatedThing(Base):  # Should be used if self.thing.relevant_property is True                                                                                                                            
    ...                                                                                                                                                                                                        
    thing = relationship("Thing")                                                                                                                                                                              
    def do_stuff(self):                                                                                                                                                                                        
        return 1                                                                                                                                                                                               


class RelatedThingChild(RelatedThing):  # Should be used if self.thing.relevant_property is False                                                                                                              
    def do_stuff(self):                                                                                                                                                                                        
        return 2                                                                                                                                                                                               


class SomethingDifferent(Base):                                                                                                                                                                                
    ...                                                                                                                                                                                                        
    related_thing = relationship("RelatedThing")  # Plugging in "RelatedThing" here probably isn't right, but I don't know what is                                                                             


def get_related_things():                                                                                                                                                                                      
    # This function doesn't know which version of RelatedThing it wants. Might even want a mix of both.                                                                                                        
    return DBSession.query(RelatedThing).all()  # Plugging in "RelatedThing" here probably isn't right, but I don't know what is                                                                               


def use_something_different(sd):                                                                                                                                                                               
    sd.related_thing.do_stuff()  # Needs sd.related_thing to be the right class.

更新:这是我相关表格的实际数据库架构。表示tests表的类需要根据series.regression进行更改。

pdiff=# \dS series
                                           Table "public.series"
     Column     |            Type             |                         Modifiers                          
----------------+-----------------------------+------------------------------------------------------------
 series_id      | integer                     | not null default nextval('series_series_id_seq'::regclass)
 name           | text                        | 
 control_domain | text                        | 
 test_domain    | text                        | 
 created        | timestamp without time zone | default now()
 reload_url     | text                        | 
 regression     | boolean                     | 
Indexes:
    "series_pkey" PRIMARY KEY, btree (series_id)
Referenced by:
    TABLE "tests" CONSTRAINT "tests_series_id_fkey" FOREIGN KEY (series_id) REFERENCES series(series_id)
    TABLE "urls" CONSTRAINT "urls_series_id_fkey" FOREIGN KEY (series_id) REFERENCES series(series_id)

pdiff=# \dS tests
                                       Table "public.tests"
  Column   |            Type             |                        Modifiers                        
-----------+-----------------------------+---------------------------------------------------------
 test_id   | integer                     | not null default nextval('tests_test_id_seq'::regclass)
 series_id | integer                     | 
 created   | timestamp without time zone | default now()
 status    | text                        | 
Indexes:
    "tests_pkey" PRIMARY KEY, btree (test_id)
Check constraints:
    "status_check" CHECK (status = ANY (ARRAY['complete'::text, 'running'::text, 'queued'::text, 'failed'::text]))
Foreign-key constraints:
    "tests_series_id_fkey" FOREIGN KEY (series_id) REFERENCES series(series_id)
Referenced by:
    TABLE "engine_tests" CONSTRAINT "diffs_test_id_fkey" FOREIGN KEY (test_id) REFERENCES tests(test_id)
    TABLE "urls_tests" CONSTRAINT "urls_tests_test_id_fkey" FOREIGN KEY (test_id) REFERENCES tests(test_id)

1 个答案:

答案 0 :(得分:1)

Okey doke,只是一个布尔值并且是数据驱动的(例如,它不像下拉值的固定表或其他东西)。因此,我们称之为鉴别器的列将是一个标量(单列/行)选择,它从“系列”表中的“回归”中拉出。有几种方法可以配置配置,下面我们在定义两个类之后设置鉴别器:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Series(Base):
    __tablename__ = 'series'

    id = Column(Integer, primary_key=True)
    regression = Column(Boolean)

class Test(Base):
    __tablename__ = 'test'

    id = Column(Integer, primary_key=True)
    series_id = Column(Integer, ForeignKey('series.id'))

    series = relationship("Series")

    __mapper_args__ = {
        "polymorphic_identity": False,
    }

Test.is_regression = column_property(
                            select([
                                    Series.regression
                                ]).
                                where(Test.series).as_scalar()
                        )
Test.polymorphic_on = Test.is_regression

class RegressionTest(Test):
    __mapper_args__ = {"polymorphic_identity": True}

e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)

sess = Session(e)

s1, s2, s3 = Series(regression=True), Series(regression=False), \
                    Series(regression=True)
sess.add_all([
    Test(series=s1),
    Test(series=s2),
    Test(series=s2),
    Test(series=s3),
    Test(series=s1),
    Test(series=s2),
])
sess.commit()
sess.close() # clears out the Session since we're going to reload with specific classes

for t in sess.query(Test).order_by(Test.id):
    print(t, t.is_regression)

此脚本至少需要SQLAlchemy 0.8才能以上述形式工作。