最佳访问SQLAlchemy模型参数,并在

时间:2016-04-06 22:45:02

标签: python sqlalchemy

问题的标题可能令人困惑,但我相信我已经在下面描述了这个问题。

我有各种声明"测试类型"我用SQLAlchemy构建的模型。每个"测试类型"模型与一个"设备"位于"位置"。因此,每个测试类型都有一个" location.name"参数与实际测试模型之间存在不同数量的关系。我需要这个" location.name"与每个"测试"相关联的值宾语。

我设置它的方式现在在查询测试时会发出大量的SQL。我想这是因为关系级联。我在一个表中显示所有测试,我希望能够以某种方式根据它们所关联的位置过滤测试。

这个模型配置对我来说是新的,我不知道如何使用原始SQL与它进行交互,我只知道如何使用模型。

我想知道获取特定测试实例的位置名称参数的最佳方法是什么。请注意,每个测试的location.name函数都会返回get_json(),尽管我希望有一种更好的方法可以减少SQL并且在过滤所有测试类型时更加简单。

希望以下模型定义明确

class Location(Model):
    __tablename__ = 'locations'
    id = Column(Integer, Sequence('location_id_seq'), primary_key=True)
    name = Column(String(50), unique=True)
    ...


class EquipmentFoo(Model):
    __tablename__ = 'equipmentfoos'
    id = Column(Integer, Sequence('equipmentfoo_id_seq'), primary_key=True)
    location_id = Column(Integer, ForeignKey('locations.id'))
    location = relationship('Location', backref='equipmentfoos')
    footests = relationship('FooTest', backref='equipmentfoo')
    ...


class EquipmentBar(Model):
    __tablename__ = 'equipmentbars'
    id = Column(Integer, Sequence('equipmentbar_id_seq'), primary_key=True)
    equipmentfoo_id = Column(Integer, ForeignKey('equipmentfoos.id'))
    equipmentfoo = relationship('EquipmentFoo', backref='equipmentbars')
    bartests = relationship('BarTest', backref='equipmentbar')
    ...


class EquipmentZab(Model):
    __tablename__ = 'equipmentzabs'
    id = Column(Integer, Sequence('equipmentzab_id_seq'), primary_key=True)
    equipmentbar_id = Column(Integer, ForeignKey('equipmentbars.id'))
    equipmentbar = relationship('EquipmentBar', backref='equipmentzabs')
    zabtests = relationship('ZabTest', backref='equipmentzab')
    ...

因此,有设备模型以及它们与位置的关系。还包括他们与下面各自测试的关系。

class HasId(object):
    @declared_attr
    def id(cls):
        return Column('id', Integer, Sequence('test_id_seq'), primary_key=True)
    @declared_attr
    def status(cls):
        return Column('status', String(50))
    ...


class TestParent(HasId, Model):
    __tablename__ = 'tests'
    discriminator = Column(String(50))
    __mapper_args__ = {'polymorphic_on': discriminator}
    ...
    def parent_json(self):
        return {'id': self.id,
                'status': self.status,
                ...
                }         


class FooTest(TestParent):
    __tablename__ = 'footests'
    __mapper_args__ = {'polymorphic_identity': 'footests'}
    id = Column(Integer, ForeignKey('tests.id'), primary_key=True) cascade='save-update, merge')
    pressure_start = Column(Float)
    ...
    def get_json():
        my_json = {'location': self.equipmentfoo.location.name,
                   'pressure_start': self.pressure_start,
                   ...
                   }
        parent_json = super(FooTest, self).parent_json()
        my_json.update(parent_json)
        return my_json


class BarTest(TestParent):
    __tablename__ = 'bartests'
    __mapper_args__ = {'polymorphic_identity': 'bartests'}
    id = Column(Integer, ForeignKey('tests.id'), primary_key=True) cascade='save-update, merge')
    hatch_value = Column(Boolean)
    ...
    def get_json():
        my_json = {'location': self.equipmentbar.equipmentfoo.location.name,
                   'hatch_value': self.hatch_value,
                   ...
                   }
        parent_json = super(BarTest, self).parent_json()
        my_json.update(parent_json)
        return my_json


class ZabTest(TestParent):
    __tablename__ = 'zabtests'
    __mapper_args__ = {'polymorphic_identity': 'zabtests'}
    id = Column(Integer, ForeignKey('tests.id'), primary_key=True) cascade='save-update, merge')
    safety_check = Column(Boolean)
    ...
    def get_json():
        my_json = {'location': self.equipmentzab.equipmentbar.equipmentfoo.location.name,
                   'safety_check': self.hatch_value,
                   ...
                   }
        parent_json = super(ZabTest, self).parent_json()
        my_json.update(parent_json)
        return my_json

1 个答案:

答案 0 :(得分:1)

默认情况下,不会加载关系。这意味着当您第一次访问关系时,会发出查询以获取数据,而数据又不会加载关系。对于您的方案,对于n ZabTest个实例,它会发出4n个查询以获取该位置的名称。

使许多小型操作高效的一般方法是通过批处理,将许多操作的开销合二为一。在SQL中执行此操作的方法是一次查询多行而不是一次查询一行。 SQLAlchemy通过eager loading公开此功能:

session.query(ZabTest).options(
    joinedload(ZabTest.equipmentzab)
    .joinedload(EquipmentZab.equipmentbar)
    .joinedload(EquipmentBar.equipmentfoo)
    .joinedload(EquipmentFoo.location))

这实现了它在一个查询中填充了self.equipmentbar.equipmentfoo.location.name的整个链,包括ZabTest本身,

或者,您可以将SQLAlchemy配置为始终预先加载:

zabtests = relationship('ZabTest', backref=backref('equipmentzab', lazy="joined"))

预先警告:因为你也在使用继承,所以急切的加载语法可能会变得毛茸茸。始终检查发出的SQL以确保正确连接。