【问题标题】:SqlAlchemy relationship to specific columnsSqlAlchemy 与特定列的关系
【发布时间】:2012-02-23 11:50:31
【问题描述】:

假设我有一个类似这样的 SqlAlchemy 模型:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, String, Integer, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship
Base = declarative_base()
Session = sessionmaker()

class EmployeeType(Base):
    __tablename__ = 'employee_type'
    id = Column(Integer(), primary_key=True)
    name = Column(String(20))

class Employee(Base):
    __tablename__ = 'employee'
    id = Column(Integer(), primary_key=True)
    type_id = Column(Integer(), ForeignKey(EmployeeType.id))
    type = relationship(EmployeeType, uselist=False)

session = Session()
session.add(EmployeeType(name='drone'))
session.add(EmployeeType(name='PHB'))

为了方便起见,我希望 Employee 直接与 EmployeeType.name 建立某种“关系”,因此如果我有类型名称,我可以跳过查找 id 或 EmployeeType 对象的步骤:

emp = Employee()
emp.type_name = "drone"
session.add(emp)
session.commit()
assert (emp.type.id == 1)

这样的事情可能吗?

编辑:我发现 Association_proxy 可以让我中途:

class Employee(Base):
    ...
    type_name = association_proxy("type", "name")

唯一的问题是,如果我分配给它:

emp = session.query(Employee).filter_by(EmployeeType.name=='PHB').first()
emp.type_name = 'drone'

它修改了employee_type.name 列,而不是employee.type_id 列。

【问题讨论】:

    标签: python sqlalchemy foreign-key-relationship


    【解决方案1】:

    我同意乔纳森的一般做法,但我觉得在会话中添加一个员工对象和设置员工类型应该是独立的操作。这是一个将 type_name 作为属性并需要在设置之前添加到会话的实现:

    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, String, Integer, ForeignKey
    from sqlalchemy.orm import sessionmaker, relationship
    Base = declarative_base()
    Session = sessionmaker()
    
    class EmployeeType(Base):
        __tablename__ = 'employee_type'
        id = Column(Integer(), primary_key=True)
        name = Column(String(20))
    
    class Employee(Base):
        __tablename__ = 'employee'
        id = Column(Integer(), primary_key=True)
        type_id = Column(Integer(), ForeignKey(EmployeeType.id))
        type = relationship(EmployeeType)
    
        @property
        def type_name(self):
            if self.type is not None:
                return self.type.name
            return None
    
        @type_name.setter
        def type_name(self, value):
            if value is None:
                self.type = None
            else:
                session = Session.object_session(self)
                if session is None:
                    raise Exception("Can't set Employee type by name until added to session")
                self.type = session.query(EmployeeType).filter_by(name=value).one()
    

    【讨论】:

    • 作为练习,我还创建了一个版本,允许在将 Employee 附加到会话之前设置 type_name,方法是将“after_attach”事件侦听器附加到 Session:gist.github.com/1750520
    【解决方案2】:

    我会通过创建一个为我执行此操作的方法来做到这一点。

    class EmployeeType(Base):
        __tablename__ = 'employee_type'
        id = Column(Integer(), primary_key=True)
        name = Column(String(20))
    
    class Employee(Base):
        __tablename__ = 'employee'
        id = Column(Integer(), primary_key=True)
        type_id = Column(Integer(), ForeignKey(EmployeeType.id))
        type = relationship(EmployeeType, uselist=False)
    
        def __init__(self, type):
            self.type = type
    
        def add(self, type_name=None):
            if type_name is not None:
                emp_type = DBSession.query(EmployeeType).filter(EmployeeType.name == type_name).first()
                if emp_type:
                    type = emp_type
                else:
                    type = EmployeeType(name=type_name)
            else:
                type = None
            DBSession.add(Employee(type=type))
    

    然后你做:

    Employee.add(type_name='boss')
    

    【讨论】:

      猜你喜欢
      • 2023-04-02
      • 2017-10-26
      • 2011-09-22
      • 1970-01-01
      • 2020-07-21
      • 1970-01-01
      • 2016-07-11
      • 2017-04-05
      • 2018-03-03
      相关资源
      最近更新 更多