定义一个有时只有真的关系()

时间:2014-02-06 01:04:47

标签: sqlalchemy

我使用的数据库架构的关系并不总是正确的,而且我不确定如何用sqlalchemy的ORM来描述它。

此数据库中的所有主键都存储为blob类型,并且是16字节的二进制字符串。

我有一个名为attribute的表,此表有一个名为data_type的列。有许多内置data_type,未在数据库中明确定义。因此,data_type 00表示它是一个字符串,01表示它是一个浮点数等(这些是十六进制值)。内置数据类型的最高值为12(十进制为18)。

但是,对于attribute中的某些行,存储在行中的属性值必须存在于预定义的值列表中。在这种情况下,data_type引用lookup.lookup_id。然后,可以从lookup.data_type检索属性的实际数据类型。

我希望能够只拨打Attribue.data_type并回复“' string'或者'数字'。显然我需要在某处定义{0x00: 'string', 0x01: 'number'}映射,但是如果lookup.data_type的值大于18,我怎么能告诉sqlalchemy我想要attribute.data_type

1 个答案:

答案 0 :(得分:0)

有几种方法可以做到这一点。

  1. 到目前为止,最简单的方法是将预定义的数据类型放入表lookup中。你说你“需要在某处定义......映射”,而且表格和任何地方一样好。

  2. 假设你不能这样做,下一个最简单的事情是在类Attribute上创建一个python property。唯一的问题是你无法查询它。您需要重新分配列data_type,以便它映射到_data_type

    data_type_dict = {0x00: 'string',
                      0x01: 'number,
                      ...}
    
    class Attribute(Base):
    
        __tablename__ = 'attribute'
    
    
        _data_type = Column('data_type')
    
        ...
    
        @property
        def data_type(self):
            dt = data_type_dict.get(self._data_type, None)
            if dt is None:
                s = Session.object_session(self)
                lookup = s.query(Lookup).filter_by(id=self._data_type).one()
                dt = lookup.data_type
            return dt
    
  3. 如果您希望这是可查询的,也就是说,如果您希望能够执行session.query(Attribute).filter_by(data_type='string'),则需要将data_type映射到数据库可以处理的内容,即SQL语句。您可以在原始SQL中以CASE表达式执行此操作:

    from sqlalchemy.sql.expression import select, case
    
    class Attribute(Base):
    
        ...
    
        data_type = column_property(select([attribute, lookup])\
                                    .where(attribute.data_type==lookup.lookup_id)\
                                    .where(case([(attribute.data_type==0x00, 'string'),
                                                 (attribute.data_type==0x01, 'number'),
                                                 ...],
                                                else_=lookup.data_type))
    

    我不是100%肯定最后一部分会起作用;您可能需要显式连接表attributelookup以指定它是外连接,但我认为 SQLAlchemy默认情况下会这样做。这种方法的缺点是你总是试图加入表lookup,但是要使用SQL进行查询,你必须这样做。

  4. 最后一个选项是使用多态,并将两种情况(data_type大于/小于18)映射到两个不同的子类:

    class Attribute(Base):
        __tablename__ = 'attribute'
    
        _data_type = Column('data_type')
        _lookup = column_property(attribute.data_type > 18)
        __mapper_args__ = {'polymorphic_on': _lookup}
    
    class FixedAttribute(Attribute):
    
        __mapper_args__ = {'polymorphic_identity': 0}
    
        data_type = column_property(select([attribute.data_type])\
                                    .where(case([(attribute.data_type==0x00, 'string'),
                                                 (attribute.data_type==0x01, 'number'),
                                                 ...])))
    
    class LookupAttribute(Attribute):
    
        __mapper_args__ = {'polymorphic_identity': 1}
    
        data_type = column_property(select([lookup.data_type],
                                       whereclause=attribute.data_type==lookup.lookup_id))
    

    您可能必须使用明确的'polymorphic_on': _lookup替换attribute.data_type > 18,具体取决于ColumnProperty何时被绑定。

  5. 正如你所看到的,这些都非常混乱。如果可能的话,做#1。