Verständnisfrage, hinsichtlich der Modellierung

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute,

ich möchte dieses Forum für eine allgemeine Verständnisfrage entführen :) Ich habe für euch zwei Bilder mitgebracht, damit ihr seht, worum es sich hierbei handelt. Ihr seht einmal einen Ausschnitt aus dem EER-Model. Bei diesem EER-Model habe ich eine 1:n-Beziehung zwischen der Tabelle Person und der Tabelle Family hergestellt. In der Person-Tabelle werden beliebige Personen gespeichert. Und wir wissen ja alle, dass Personen untereinander in ein Beziehungsverhältnis stehen können (Mutter, Vater, Tochter, Sohn, Cousin, Cousine, Neffe, etc...). Aus diesem Grund habe ich die Family-Tabelle eingerichtet.
Bild

Um das ganze mal beispielhaft durchzuspielen, habe ich die Tabellen mit fiktiven Daten gefüllt. Mit einem Blick in die Family-Tabelle sehen wir gleich in der ersten Spalte, um welche zentrale Person es sich hierbei handelt, nämlich um Hans Schmidt (mit der ID 1). In der Family-Tabelle sind nun folgende Personen durch deren Fremdschlüssel gespeichert: Kurt Schmidt (Sohn), Gerda Schmidt (Ehefrau), Gisela Schmidt (Hans' Mutter), und Julia Schmidt (Tochter). Damit möchte ich das Beziehungsgeflecht von Hans Schmidt darstellen.
Bild

Und in meinem Quelltext habe ich dieses ORM-Model wie folgt abgebildet:

Code: Alles auswählen

class FAMILY(Base):

    __tablename__ = "family"

    id = Column(Integer, primary_key=True, unique=True, autoincrement=True)
    status = Column(String(255), nullable=False)

    person_id = Column(Integer, ForeignKey('person.id'))
    person = relationship("PERSON")

    family_person_id = Column(Integer, ForeignKey('person.id'))
    family_person = relationship("PERSON")

class PERSON(Base):

    __tablename__ = "person"

    id = Column(Integer, primary_key=True, unique=True, autoincrement=True)
    nickname = Column(String(255))
    alias_name  = Column(String (255))
    name_normally_used = Column(String(50), nullable=False)
    first_middle_name = Column(String(255))
    last_name = Column(String(100))
    birth_name = Column(String(100))
    body_height = Column(String(10))
    wedding_anniversary = Column(Date)
    birthday = Column(Date)
    day_of_death = Column(Date)
    notice = Column(Text())

    gender_id = Column(Integer, ForeignKey('person_gender.id'))
    gender = relationship("PERSON_GENDER")

    hair_color_id = Column(Integer, ForeignKey('person_hair_color.id'))
    hair_color = relationship("PERSON_HAIR_COLOR")

    eye_color_id = Column(Integer, ForeignKey('person_eye_color.id'))
    eye_color = relationship("PERSON_EYE_COLOR")

    title_id = Column(Integer, ForeignKey('person_title.id'))
    title = relationship("PERSON_TITLE")

    salutation_id = Column(Integer, ForeignKey('person_salutation.id'))
    salutation = relationship("PERSON_SALUTATION")

    place_id = Column(Integer, ForeignKey('general_place.id'))
    place = relationship("GENERAL_PLACE")

    religion_id = Column(Integer, ForeignKey('person_religion.id'))
    religion = relationship("PERSON_RELIGION")

    relationship_status_id = Column(Integer, ForeignKey('person_relationship_status.id'))#, index=True)
    relationship_status = relationship("PERSON_RELATIONSHIP_STATUS")
Beim Anlegen des Models macht SQLAlchemy keinerlei Probleme (echo ist auf True, daher konnte ich es mitverfolgen, dass es keine Probleme gab.). Jedoch wirft SQLAlchemy eine Exception, sobald ich die Benutzeroberfläche starte, in welcher man die Daten einer Person verwalten kann.
Traceback (most recent call last):
File "D:\Dan\Python\Xarphus\xarphus\subclass_master_data_load_data_item.py", line 140, in populate_item
self.populate_item_signal.emit(next(self._element))
File "D:\Dan\Python\Xarphus\xarphus\core\manage_data_manipulation_master_data.py", line 205, in select_all
for record in dict_store_session_query[category]():
File "D:\Dan\Python\Xarphus\xarphus\core\manage_data_manipulation_master_data.py", line 191, in <lambda>
'person_gender': lambda: self._session.query(PERSON_GENDER),
File "C:\Python27\lib\site-packages\sqlalchemy\orm\session.py", line 1362, in query
return self._query_cls(entities, self, **kwargs)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 139, in __init__
self._set_entities(entities)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 150, in _set_entities
self._set_entity_selectables(self._entities)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 180, in _set_entity_selectables
ent.setup_entity(*d[entity])
File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 3585, in setup_entity
self._with_polymorphic = ext_info.with_polymorphic_mappers
File "C:\Python27\lib\site-packages\sqlalchemy\util\langhelpers.py", line 764, in __get__
obj.__dict__[self.__name__] = result = self.fget(obj)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 1948, in _with_polymorphic_mappers
configure_mappers()
File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 2869, in configure_mappers
raise e
InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Triggering mapper: 'Mapper|FAMILY|family'. Original exception was: Could not determine join condition between parent/child tables on relationship FAMILY.person - there are multiple foreign key paths linking the tables. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table.

Traceback (most recent call last):
File "D:\Dan\Python\Xarphus\xarphus\subclass_master_data_load_data_item.py", line 140, in populate_item
self.populate_item_signal.emit(next(self._element))
File "D:\Dan\Python\Xarphus\xarphus\core\manage_data_manipulation_master_data.py", line 205, in select_all
for record in dict_store_session_query[category]():
File "D:\Dan\Python\Xarphus\xarphus\core\manage_data_manipulation_master_data.py", line 194, in <lambda>
'person_title': lambda: self._session.query(PERSON_TITLE),
File "C:\Python27\lib\site-packages\sqlalchemy\orm\session.py", line 1362, in query
return self._query_cls(entities, self, **kwargs)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 139, in __init__
self._set_entities(entities)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 150, in _set_entities
self._set_entity_selectables(self._entities)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 180, in _set_entity_selectables
ent.setup_entity(*d[entity])
File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 3585, in setup_entity
self._with_polymorphic = ext_info.with_polymorphic_mappers
File "C:\Python27\lib\site-packages\sqlalchemy\util\langhelpers.py", line 764, in __get__
obj.__dict__[self.__name__] = result = self.fget(obj)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 1948, in _with_polymorphic_mappers
configure_mappers()
File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 2872, in configure_mappers
mapper._post_configure_properties()
File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 1765, in _post_configure_properties
prop.init()
File "C:\Python27\lib\site-packages\sqlalchemy\orm\interfaces.py", line 184, in init
self.do_init()
File "C:\Python27\lib\site-packages\sqlalchemy\orm\relationships.py", line 1654, in do_init
self._setup_join_conditions()
File "C:\Python27\lib\site-packages\sqlalchemy\orm\relationships.py", line 1729, in _setup_join_conditions
can_be_synced_fn=self._columns_are_mapped
File "C:\Python27\lib\site-packages\sqlalchemy\orm\relationships.py", line 1987, in __init__
self._determine_joins()
File "C:\Python27\lib\site-packages\sqlalchemy\orm\relationships.py", line 2114, in _determine_joins
% self.prop)
AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship FAMILY.person - there are multiple foreign key paths linking the tables. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table.
Und da wären wir. SQLALchey hat anscheinend ein Problem mit den beiden Fremdschlüsseln, die aus einer und derselben Tabelle stammen? Frage an euch: Wie sollte man die Modellierung am besten machen, um das Beziehungsgeflecht zwischen den Personen darstellen zu können? Denn nach meiner Überlegung brauche ich mindestens zwei Fremdschlüssel aus der Person-Tabelle, um unter den Personen eine Beziehung herstellen zu können.
BlackJack

@Sophus: Du musst halt bei den beiden `relationship()`\s explizit sagen auf welchen der beiden Fremdschlüssel die sich jeweils beziehen. Das mag *Dir* und jedem anderen Leser intuitiv klar sein, weil die die schön untereinander stehen und ähnlich heissen, das versteht der Rechner/SQLAlchemy aber nicht.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@BlackJack: Ich weiss gerade nicht was du meinst. Die Beziehungen zu den beiden Tabellen sind so definiert, dass die beiden Fremdschlüssel (hier aus Person-Tabelle) in der Family-Tabelle hinterlegt werden sollen?
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@BlackJack: Meintest du etwa so?

Code: Alles auswählen

class FAMILY(Base):

    __tablename__ = "family"

    id = Column(Integer, primary_key=True, unique=True, autoincrement=True)
    status = Column(String(255), nullable=False)

    person_id = Column(Integer, ForeignKey('person.id'))
    person = relationship("PERSON", backref='family', lazy='dynamic')

    family_person_id = Column(Integer, ForeignKey('person.id'))
    family_person = relationship("PERSON", backref='family', lazy='dynamic')


class PERSON(Base):

    __tablename__ = "person"

    id = Column(Integer, primary_key=True, unique=True, autoincrement=True)
    nickname = Column(String(255))
    alias_name  = Column(String (255))
    name_normally_used = Column(String(50), nullable=False)
    first_middle_name = Column(String(255))
    last_name = Column(String(100))
Wenn ich das so mache, bekomme ich die gleiche Meldung wie bereits oben erwähnt.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Ich glaube, ich konnte das Problem, auf BlackJacks Rat hin, beheben. Da ich auf Anhieb nicht genau wusste, was BlackJack meinte, zog ich mir diese Dokumentation, mit dem Titel Handling Multiple Join Paths heran. Auf dieser Seite wird im ersten Beispiel gezeigt, wie der Fehler zustande kommt, den ich hatte. Im zweiten Beispiel wird das foreign_keys -Argument erwähnt. In meinem Beispiel übergebe ich den beiden foreign_keys-Argumenten je eine Liste mit je einer Spalte. Und diese Spalten sollen als fremd betrachtet werden.

Code: Alles auswählen

class FAMILY(Base):

    __tablename__ = "family"

    id = Column(Integer, primary_key=True, unique=True, autoincrement=True)#, index=True)
    status = Column(String(255), nullable=False)#, index=True)

    person_id = Column(Integer, ForeignKey('person.id'))
    person = relationship("PERSON", foreign_keys=[person_id])

    family_person_id = Column(Integer, ForeignKey('person.id'))
    family_person = relationship("PERSON", foreign_keys=[family_person_id])

class PERSON(Base):

    __tablename__ = "person"

    id = Column(Integer, primary_key=True, unique=True, autoincrement=True)#, index=True)
    nickname = Column(String(255))
    alias_name  = Column(String (255))
    name_normally_used = Column(String(50), nullable=False)
    first_middle_name = Column(String(255))
    last_name = Column(String(100))
    birth_name = Column(String(100))
    body_height = Column(String(10))
    wedding_anniversary = Column(Date)
    birthday = Column(Date)
    day_of_death = Column(Date)
    notice = Column(Text())
Antworten