有一个info表,它与car表或suv表有关系。
在info.type字段中指定。
那么,如何根据记录的类型数据创建动态关联呢?
class Info(Base):
item_id = Column(ForeignKey('cars-or-suvs-table.id'))
type = Column(String())
class Car(Base):
- data -
info = relationship('Info', backref="car")
class Suv(Base):
- data -
info = relationship('Info', backref="suv")编辑:--我已经有了填充了数据的表,所以我不能更改数据库模式。
发布于 2012-02-05 06:47:06
由于您正在寻找不需要将外键移动到其他表的解决方案,因此可以尝试以下方法:
import sqlalchemy
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, String, Integer
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy import sql
Base = declarative_base()
engine = sqlalchemy.create_engine('sqlite:///:memory:')
Session = sessionmaker(bind=engine)
session = Session()
class Info(Base):
__tablename__ = 'info'
id = Column(Integer(), primary_key=True)
type = Column(String())
item_id = Column(Integer())
@property
def item(self):
if self.type == 'car':
return self._car
elif self.type == 'suv':
return self._suv
return None
@item.setter
def item(self, value):
if value is not None:
self.item_id = value.id
self.type = value.__tablename__
else:
self.item_id = None
class Car(Base):
__tablename__ = 'car'
id = Column(Integer(), primary_key=True)
info = relationship(Info, primaryjoin=sql.and_(id == Info.item_id, Info.type == 'car'), foreign_keys=Info.item_id, uselist=False, backref='_car')
class Suv(Base):
__tablename__ = 'suv'
id = Column(Integer(), primary_key=True)
info = relationship(Info, primaryjoin=sql.and_(id == Info.item_id, Info.type == 'suv'), foreign_keys=Info.item_id, uselist=False, backref='_suv')我将Info.car改名为Info._car,因为即使.type是'suv‘,._car也不可避免地是一个假的汽车对象。
为了保持简单,我把事件侦听器的内容省去了,但是您肯定可以从我的另一个答案中调整您需要的部分,以避免事情陷入不一致的状态。
发布于 2012-02-04 10:23:46
在SQL中,外键必须映射到一个特定的表中,因此您需要将外键放在指向'info.id‘的“car”或“suv”表中。
对于您所需的内容,这可能有些过分,但这里有一种解决方法(假设您实际上希望每一辆汽车都只有一个信息):
import sqlalchemy
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, String, Integer, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy import event
Base = declarative_base()
class Info(Base):
__tablename__ = 'info'
id = Column(Integer(), primary_key=True)
type = Column(String())
# NOTE: can't use backref='info' because we need the attributes defined
# directly on both classes so we can attach event listeners
car = relationship('Car', back_populates='info', uselist=False)
suv = relationship('Suv', back_populates='info', uselist=False)
@property
def item(self):
# could check self.type here if you wanted
return self.car or self.suv
@item.setter
def item(self, value):
if isinstance(value, Car):
self.car = value
elif isinstance(value, Suv):
self.suv = value
elif value is None:
self.car = None
self.suv = None
else:
raise ValueError("item must be Car or Suv")
@event.listens_for(Info.car, 'set')
def _car_set_event(target, value, oldvalue, initiator):
if value is not None:
target.type = 'car'
if target.suv:
target.suv = None
elif target.type == 'car':
target.type = None
@event.listens_for(Info.suv, 'set')
def _suv_set_event(target, value, oldvalue, initiator):
if value is not None:
target.type = 'suv'
if target.car:
target.car = None
elif target.type == 'suv':
target.type = None
class Car(Base):
__tablename__ = 'car'
id = Column(Integer(), primary_key=True)
info_id = Column(Integer(), ForeignKey('info.id'))
info = relationship(Info, back_populates='car')
@event.listens_for(Car.info, 'set')
def _car_info_set_event(target, value, oldvalue, initiator):
if value is not None:
value.type = 'car'
class Suv(Base):
__tablename__ = 'suv'
id = Column(Integer(), primary_key=True)
info_id = Column(Integer(), ForeignKey('info.id'))
info = relationship(Info, back_populates='suv')
@event.listens_for(Suv.info, 'set')
def _suv_info_set_event(target, value, oldvalue, initiator):
if value is not None:
value.type = 'suv'事件侦听器的复杂性在于,当您执行以下操作时,将自动管理该类型:
car1.info = Info()
assert (car1.info.type == 'car')或
info1 = car1.info
info1.suv = suv1
assert (car1.info is None)
assert (info1.type == 'suv')如果希望保持Info.type、Info.car和Info.suv的一致性,可以省略所有事件侦听器函数。
为CarInfo和SuvInfo设置单独的对象和表,并完全避免所有这些复杂性,也是一个非常合理的选择。
https://stackoverflow.com/questions/9137071
复制相似问题