Buchachalo
@Buchachalo

Как задать отношения в sqlalchemy что бы свойство было доступной в обеих моделях?

class User(Base):

    __tablename__ = "user"

    id = Column(Integer, primary_key=True)
    address_first_id = Column(Integer, ForeignKey('address.id'))
    address_first = relationship("Address", foreign_keys=[address_first_id], back_populates="u_first_address", lazy='subquery')
    address_second_id = Column(Integer, ForeignKey('address.id'))
    address_second = relationship("Address", foreign_keys=[address_second _id], back_populates="u_second_address", lazy='subquery')
    is_available = Column(Boolean, unique=False, default=True)
    created_at = Column(DateTime, index=True, server_default=func.now())
    update_at = Column(DateTime, index=True, server_default=func.now(), server_onupdate=func.now())

class Address(Base):

    __tablename__ = "address"

    id = Column(Integer, primary_key=True)
    comment = Column(String(100), unique=True, nullable=False)
    u_first_address = relationship("User", lazy='subquery', back_populates="address_first", primaryjoin="User.address_first_id == Address.id")
    u_second_address = relationship("User", lazy='subquery', back_populates="address_second", primaryjoin="User.address_first_id == Address.id")
    is_available = Column(Boolean, unique=False, default=True)
    created_at = Column(DateTime, index=True, server_default=func.now())
    update_at = Column(DateTime, index=True, server_default=func.now(), server_onupdate=func.now())


Хочу реализовать свойства релейшенов в обеих моделях.

Если из модели Address убрать relationship все будет работать. Но как я понял проблема заключается в том что на модель с адресом ведут два ключа. И тут я не совсем понимаю механику взаимодействия.
sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship Address.u_first_address - 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.
Такая ошибка у меня в консоли, прогуглил, пишут что нужно идти через backref, крутил и так и сяк, видно не хватает теоретических знаний.

Нашел в офф документации такое решение тык. Все хорошо и работает, но попробовав реализовать relationship со стороны модели с адресом, все ломается.

Наведите на мысль! Спасибо большое!
  • Вопрос задан
  • 382 просмотра
Решения вопроса 1
bravebug
@bravebug
Не считаю код ниже решением, т.к. понять что требовалось я так до конца и не смог. Думаю что sqlalchemy в данной задаче нужно использовать немного иначе. И возможно код ниже окажется тем, что требовалось:
#!/usr/bin/env python

from datetime import datetime
from sqlalchemy import ForeignKey
from sqlalchemy import create_engine, delete, select, update
from sqlalchemy.orm import DeclarativeBase, Mapped
from sqlalchemy.orm import mapped_column, relationship, sessionmaker


class Base(DeclarativeBase):
    pass

class User(Base):
    __tablename__ = "user"
    id:                 Mapped[int]         = mapped_column(primary_key=True)
    address_first_id:   Mapped[int]         = mapped_column(ForeignKey('address.id'))
    address_first:      Mapped["Address"]   = relationship(foreign_keys=[address_first_id])
    address_second_id:  Mapped[int]         = mapped_column(ForeignKey('address.id'))
    address_second:     Mapped["Address"]   = relationship(foreign_keys=[address_second_id])
    is_available:       Mapped[bool]        = mapped_column(default=True)
    created_on:         Mapped[datetime]    = mapped_column(default=datetime.utcnow())
    updated_on:         Mapped[datetime]    = mapped_column(default=datetime.utcnow(), onupdate=datetime.utcnow())

class Address(Base):
    __tablename__ = "address"
    id:                 Mapped[int]         = mapped_column(primary_key=True)
    comment:            Mapped[str]         = mapped_column(unique=True, nullable=False)
    is_available:       Mapped[bool]        = mapped_column(default=True)
    created_on:         Mapped[datetime]    = mapped_column(default=datetime.utcnow())
    updated_on:         Mapped[datetime]    = mapped_column(default=datetime.utcnow(), onupdate=datetime.utcnow())


if __name__ == "__main__":
    engine = create_engine('sqlite:///:memory:', echo=True)
    Base.metadata.create_all(engine)
    Session = sessionmaker(engine)
    # добавим пользователя с двумя адресами
    with Session.begin() as session:
        address1 = Address(comment="Lenina 45")
        address2 = Address(comment="Sedina 5")
        user = User(address_first=address1, address_second=address2)
        session.add(user)
    # проверим, что пользователь был создан успешно
    with Session.begin() as session:
        user = session.query(User).first()
        print(user, user.address_first.comment)
    with Session.begin() as session:
        # находим первый адрес по части адреса
        adr = session.query(Address).where(Address.comment.like("%Sedina%")).first()
        print(adr, adr.comment)
        # находим первого пользователя у которого один из адресов соответствует найденному выше адресу
        usr = session.query(User).where(User.address_first == adr or User.address_second == adr).first()
        print(usr, usr.address_first.comment, usr.address_second.comment)

Успехов!
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы