SQLAlchemy如何使用声明式映射单列一对一关系

时间:2014-10-28 16:25:59

标签: python orm sqlalchemy flask-sqlalchemy

这与此问题converting to declarative method and column property有关,该问题从未得到解答。

我们正在尝试在现有模式(我们无法更改)上设置Flask-SQLAlchemy项目,并决定声明性语法,以便我们可以以合理的方式将类组织到多个文件中进行维护。这适用于我们的大多数关系,除了我们称之为的东西,因为缺少更好的术语,属性表。这些是一些主要对象的一对一叶表,通常包含属性的某种受控词汇表。 ORM的目标是将所有这些(其中有很多种)表格映射为好像它们是主表的属性。

这是一个包含两个表的SQA示例:

class MarkerType(db.Model):
    __tablename__="mrk_types"
    _marker_type_key = db.Column(db.Integer,primary_key=True)
    name = db.Column(db.String())

class Marker(db.Model):
    __tablename__="mrk_marker"
    _marker_key=db.Column(db.Integer,primary_key=True)
    _marker_type_key=db.Column(db.Integer())

我们想要访问MarkerType.name,就好像我们说Marker.markertype一样,或者在查询中使用Marker.markertype ==' thing'。我可以管理的唯一方法是使用Marker类中的column_property,如下所示:

markertype = db.column_property(
            db.select([MarkerType.name]).
            where(MarkerType._marker_type_key==_marker_type_key)
    )

但是,我似乎无法以声明的方式找到如何做到这一点,也许这种方式不存在。是否有一种理智的方式,我可以实现这一点,而不必担心我的进口,甚至更糟糕的我的课程的顺序?由于我们有数百个表要映射,如果我们不得不担心类和导入顺序,我会发现这是一个维护噩梦。

如果所有这一切都完全不可能,一厢情愿,那么映射这些表的更好方法是什么?

3 个答案:

答案 0 :(得分:2)

这听起来像是Association Proxy的一个很好的用例。这代理相关模型的字段。在这种情况下,实现将是:

from sqlalchemy.orm import relationship
from sqlalchemy.ext.associationproxy import association_proxy

class MarkerType(db.Model):
    __tablename__="mrk_types"
    _marker_type_key = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String())

class Marker(db.Model):
    __tablename__="mrk_marker"
    _marker_key=db.Column(db.Integer,primary_key=True)
    _marker_type_key=db.Column(db.Integer, ForeignKey('mrk_types._marker_type_key')

    mt = relationship(MarkerType, uselist=False)
    marker_type = association_proxy('mt', 'name')

这允许查询session.query(Marker).filter_by(marker_type='my_marker_type')

marker_type字段是MarkerType对象上name字段的代理。该对象可以由mt字段(关系字段)

引用

请注意uselist=False。这表明每个标记具有1种标记类型。该关系会自动检测ForeignKey并使用它。

答案 1 :(得分:1)

所以从我收集的内容来看,你会遇到两张桌子。一个带有整数col,一个带有字符串col。

Class Marker
    _marker_key_ primary_key
    # _ = Integer ## This can be ignored as its unnecessary.

另一个有

Class MarkerType
    _marker_type_key = primary_key
    name = String

在我阅读它时,您希望Class Marker具有许多可以轻松操作或调用的Class MarkerType字符串。虽然,我不确定这是不是你想要的。

如果是,您可以通过控制播种数据库来实现此目的。您可以在每个名称的开头构建一个指向Markers主键的标志。

示例:MarkerType.name = 10324_Orange

我不熟悉在没有会话的情况下使用SQLAlchemy,并且真的不想做研究,所以我假设你正在使用SQLAlchemy会话,我会写下我的答案,所以你可以得到这个概念并且可以调整为需要。

### !!! ASSUME 'create_session' method exists that 
####    creates a sqlalchemy session instance

Class Marker:
    # ... initialize and such
    # ... then add these helper methods

    ## Get all properties linked to this primary table row
    def marker_types():
        return db.query(MarkerType).
            filter(MarkerType.name.like(str(self._marker_key_)+"_%")).all()
    ## Get specific property linked to this primary table row
    def marker_type(marker_type_name):
        db = create_session()
        marker_type_list = db.query(MarkerType).
            filter(MarkerType.name.like(str(self._marker_key_)+"_%")
            AND marker_type_name == MarkerType.name ).first()
        db.close()
        return marker_type_list

    def update_marker_type(old_val, new_val)
        db = create_session()
        updated_marker_type = marker_type(old_val)
        updated_marker_type.name = str(self._marker_key_)+" "+new_val
        db.close()
        return True

    def create_marker_type(val)
        marker_type = MarkerType(name = str(self._marker_key_)+" "+val)
        db = create_session()
        db.add(marker_type)
        db.commit()
        db.close()
        return marker_type._marker_type_key

从这里,您可以为名称字符串添加其他标志。像属性类型的东西。

Marker.id = 193

MarkerType.id = 1
MarkerType.name = "193_color_Black"
MarkerType.id = 2
MarkerType.name = "193_style_Fine"

这个附加标志可以让您搜索链接到特定行的特定属性,并且更加可用,但稍微复杂一些。真的取决于你的用例。

答案 2 :(得分:1)

使用relationship允许从marker_type表访问Marker并指定ForeignKey约束,以便SQLAlchemy了解表之间的关系。

这使您可以轻松地从Marker记录访问MarkerType属性,以便查询MarkerType.name。以下显示插入两个记录,然后根据属性name进行过滤。

>>> db.session.add(Marker(marker_type=MarkerType(name="blue")))
>>> db.session.add(Marker(marker_type=MarkerType(name="red")))
>>> db.session.commit()


>>> markers = Marker.query.all()
>>> print({m._marker_key: m.marker_type.name for m in markers})

{1: 'blue', 2: 'red'}

>>> result = Marker.query.filter(Marker._marker_type_key==MarkerType._marker_type_key) \
...                      .filter(MarkerType.name=='blue').all()
>>> print({m._marker_key: m.marker_type.name for m in result})

{1: 'blue'}

类的声明顺序很重要,并且类需要一起声明。仍然,必须针对同一个db实例注册模式,并且当您查询表时,需要导入您引用的表类。

relationshipForeignKey添加到Marker之后,声明性架构将变为:

class MarkerType(db.Model):
    __tablename__="mrk_types"
    _marker_type_key = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String())

class Marker(db.Model):
    __tablename__="mrk_marker"
    _marker_key=db.Column(db.Integer, primary_key=True)
    _marker_type_key=db.Column(db.Integer(), db.ForeignKey('mrk_types._marker_type_key'))
    marker_type=db.relationship('MarkerType')