【问题标题】:How to combine two model class in SQLAlchemy如何在 SQLAlchemy 中组合两个模型类
【发布时间】:2017-02-17 01:35:04
【问题描述】:

我是使用 SQLAlchemy 的 ORM 的新手,我以前只使用原始 SQL。我有数据库表,LabelPositionDataSet,如下所示:

以及对应的python类如下:

class Label(Base):
    __tablename__ = 'Label'

    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False, unique=true)


class Position(Base):
    __tablename__ = 'Position'

    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False, unique=true)


class DataSet(Base):
    __tablename__ = 'DataSet'

    id = Column(Integer, primary_key=True)
    label_id = Column(Integer, ForeignKey('Label.id'))
    position_id = Column(Integer, ForeignKey('Position.id'))
    timestamp = Column(Integer, nullable=False)

但在我的服务中,我不会公开那些label_idposition_id。所以我创建了一个新类Data 来保存labelposition 作为字符串。

# Not a full class to only show my concept
class Data:
    # data dictionary will have data 
    def __init__(self, **kwargs):
        # So it doesn't have ids. Label and Position as string
        keys = {'label', 'position', 'timestamp'}
        self.data = {k: kwargs[k] for k in keys}

    # An example of inserting data.
    # skipped detail and error handling to clarify
    def insert(self):
        session = Session()
        # get id of label and position
        # remember that it returns a tuple, not a single value
        self.data['label_id'] = session.query(Label.id).\
            filter(Label.name == self.data['label']).one_or_none()
        self.data['position_id'] = session.query(Position.id).\
            filter(Position.name == self.data['position']).one_or_none()
        # add new dataset
        self.data.pop('label')
        self.data.pop('position')
        new_data = DataSet(**self.data)
        session.add(new_data)
        session.commit()

但它看起来有些难看,我认为应该有一种更简单的方法来做到这一点。有没有办法使用 SQLAlchemy API 组合这些表类?

【问题讨论】:

    标签: python database orm sqlalchemy


    【解决方案1】:

    您可以使用relationshipsassociation proxiesDataSet 链接到LabelPosition 对象:

    from sqlalchemy.orm import relationship
    from sqlalchemy.ext.associationproxy import association_proxy
    
    class DataSet(Base):
        __tablename__ = 'DataSet'
    
        id = Column(Integer, primary_key=True)
    
        label_id = Column(Integer, ForeignKey('Label.id'))
        label = relationship('Label')
        label_name = association_proxy('label', 'name')
    
        position_id = Column(Integer, ForeignKey('Position.id'))
        position = relationship('Position')
        position_name = association_proxy('position', 'name')
    
        timestamp = Column(Integer, nullable=False)
    

    之后,您可以通过新属性访问链接到DataSet(及其名称)的LabelPosition 对象:

    >>> d = session.query(DataSet).first()
    >>> d.position
    <Position object at 0x7f3021a9ed30>
    >>> d.position_name
    'position1'
    

    不幸的是,插入DataSet 对象并不那么漂亮。您可以为association_proxy 指定creator 函数,该函数可以获取名称并创建或检索相应的对象(在this answer 中找到):

    def _label_creator(name):
        session = Session()
        label = session.query(Label).filter_by(name=name).first()
        if not label:
            label = Label(name=name)
            session.add(label)
            session.commit()
        session.close()
        return label
    
    label_name = association_proxy('label', 'name', creator=_label_creator)
    

    为两个代理指定creator 函数后,您可以通过这种方式创建新的DataSet 对象:

    dataset = DataSet(
        label_name='label1',
        position_name='position2',
        timestamp=datetime.datetime.now()
    )
    

    【讨论】:

    • 我刚读过关系,但不知道关联代理。这将节省我的时间。非常感谢!
    猜你喜欢
    • 2021-12-01
    • 1970-01-01
    • 2016-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多