En SQL, une clé étrangère doit être mappée à une table spécifique, vous devez donc placer la clé étrangère dans la table 'car' ou 'suv' pointant sur 'info.id'.
Ceci est probablement exagéré pour ce que vous avez besoin, mais voici une façon de le résoudre (en supposant que vous faites en fait vouloir chaque voiture pour avoir une seule Info):
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'
Ce que la complexité des écouteurs d'événements vous fait est que le type est géré automatiquement lorsque vous faites quelque chose comme:
car1.info = Info()
assert (car1.info.type == 'car')
ou
info1 = car1.info
info1.suv = suv1
assert (car1.info is None)
assert (info1.type == 'suv')
Si vous souhaitez conserver vous-même Info.type, Info.car et Info.suv, vous pouvez omettre toutes les fonctions de l'écouteur d'événements.
Il serait également très raisonnable d'avoir des objets et des tables séparés pour CarInfo et SuvInfo, et d'éviter toute cette complexité.
Est-ce que chaque voiture devrait avoir une relation un-à-un avec Item, ou un one-to-many? –
Il est un-à-un entre voiture-ou-suv et info –