在association_proxy中更改引用的集合类键

时间:2012-06-26 17:34:02

标签: python sqlalchemy

是否可以在关联代理中更改引用的集合类的键?

示例:

class Event(ManagerBase):
    """Defines an event."""

    __tablename__ = 'eventing_events'

    id = Column(Integer, primary_key=True)
    device_id = Column(Integer, ForeignKey(EventingDevice.id), nullable=False)
    device = relation(EventingDevice)
    type_id = Column(Integer, ForeignKey(EventType.id), nullable=False)
    type = relation(EventType)
    datetime = Column(DateTime, nullable=False)
    summary = Column(String(500))

    fields = association_proxy("field_values", "value")

class EventFieldValue(ManagerBase):
    """The value of a single field of an event."""

    __tablename__ = 'eventing_event_field_values'

    event_id = Column(Integer, ForeignKey(Event.id), primary_key=True)
    event = relation(Event, backref=backref("field_values",
                                            collection_class=attribute_mapped_collection("field")))
    field_id = Column(Integer, ForeignKey(Field.id), primary_key=True)
    field = relation(Field)
    value = Column(Text)

在这种情况下,field的{​​{1}}属性将代表以Event为关键字的字典。是否可以仅在代理本身中更改此密钥而不影响关联?

1 个答案:

答案 0 :(得分:1)

似乎原始SQLAlchemy AssociationProxy无法实现这一点,因为类直接访问原始关系,因此任何键都将在基础关系上以1:1查询。

我写了一个类DictProxy,它允许更改键和值字段:

class DictProxy(object):
    def __init__(self, col, keyattr, valattr = None):
        self.obj = None
        self.col = col
        self.keyattr = keyattr
        self.valattr = valattr

    def __get__(self, obj, class_):
        self.obj = obj
        return self

    def __repr__(self):
        outdict = {}
        for k, v in getattr(self.obj, self.col).iteritems():
            if not k is None:
                if self.valattr == None:
                    outdict[getattr(k, self.keyattr)] = v
                elif v is not None:
                    outdict[getattr(k, self.keyattr)] = getattr(v, self.valattr)
                else:
                    outdict[getattr(k, self.keyattr)] = None
        return repr(outdict)

    def __getitem__(self, key):
        keyobj = [obj for obj in getattr(self.obj, self.col) if getattr(obj, self.keyattr) == key]
        if not len(keyobj):
            return None
        if self.valattr == None:
            return getattr(self.obj, self.col)[keyobj[0]]
        else:
            return getattr(getattr(self.obj, self.col)[keyobj[0]], self.valattr)

    def __contains__(self, key):
        return len([obj for obj in getattr(self.obj, self.col) if getattr(obj, self.keyattr) == key]) != 0

def dict_proxy(*arg):
    return DictProxy(*arg)

使用示例:

class Event(ManagerBase):
    """Defines an event."""

    __tablename__ = 'eventing_events'

    id = Column(Integer, primary_key=True)
    device_id = Column(Integer, ForeignKey(EventingDevice.id), nullable=False)
    device = relation(EventingDevice)
    type_id = Column(Integer, ForeignKey(EventType.id), nullable=False)
    type = relation(EventType)
    datetime = Column(DateTime, nullable=False)
    summary = Column(String(500))

    fields = dict_proxy("field_values", "name", "value")
fields的{​​{1}}现在是一个以Event为关键字的字典,并访问关系field_values(来自EventFieldValue.name来自EventFieldValue)。

注意: 实际上这是一个只读代理,但是可能(尽管很棘手)通过backref方法扩展代理。