SqlAlchemy 关系到特定列

6 投票
2 回答
5887 浏览
提问于 2025-04-17 12:03

假设我有一个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这一列。

2 个回答

3

我会通过创建一个方法来帮我完成这个任务。

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')
4

我同意Jonathan的整体思路,但我觉得把员工对象放到会话中和设置员工类型这两个操作应该是独立的。下面是一个实现方式,其中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()

撰写回答