Как установить связь между моделями SQLAlchemy в соотношении один к одному по полю id?

Добрый день! У меня есть две таблицы, в которых поле `id` является зависимым ключом для отношений друг с другом. Я использую модели в declarative base:

```python
class Article(MPInsideBase):
    """Модель Article."""

    __tablename__ = "wb_ws_article"

    id = Column(BigInteger(), ForeignKey(Product.id), unique=True, primary_key=True)
    product: Mapped[Product] = Relationship(
        remote_side=Product.id, foreign_keys="Article.id",
    )

class Product(MPInsideBase):
    """Модель продукта."""

    __tablename__ = "wb_products"

    id = Column(BigInteger(), unique=True, primary_key=True)
    article: Mapped["Article"] = Relationship("Article", back_populates="product")
```

Также есть код, который использует эти модели:

```python
async with self.db.session() as session:
    print(filter_stmt.get('id'))
    stmt = select(Article, Product).join(Product).filter_by(**filter_stmt)
    if options:
        for option in options:
            stmt = stmt.options(option)

    result = await session.scalars(stmt)
    model = result.one()
    product = model.product
    print(vars(product))

    return model
```

При выполнении этого кода я получаю следующую ошибку:

```
sqlalchemy.exc.MissingGreenlet: greenlet_spawn has not been called; can't call await_only() here. Was IO attempted in an unexpected place? (Background on this error at: https://sqlalche.me/e/20/xd2s)
```

Может кто-нибудь поделиться опытом связывания таких таблиц через SQLAlchemy для совместного получения данных из `Product` при запросе данных из `Article`?
  • 29 декабря 2024 г. 14:53
Ответы на вопрос 1
Для установки связи между моделями SQLAlchemy с соотношением один к одному по полю `id` вы правильно определили модели и отношения. Однако ошибка, которую вы получаете, указывает на то, что вы используете асинхронный контекст (async context) и возможно вызываете ожидание (await) в неподходящем месте кода.

Давайте разберем и исправим вашу текущую ситуацию.

1. **Правильные объявления отношений**:
    - В вашем коде модель `Article` ссылается на `Product` через поле `id`, что корректно, но обратное отношение в `Product` должно ссылаться на `Article` правильно.
    - Также добавьте `back_populates` в отношении `Article`.

2. **Асинхронный код**:
    - Убедитесь, что вы используете `await` только внутри асинхронных функций и что сессии созданы и используются корректно.

Вот исправленный код моделей:

```python
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, BigInteger, ForeignKey
from sqlalchemy.orm import relationship

MPInsideBase = declarative_base()

class Article(MPInsideBase):
    """Модель Article."""
    
    __tablename__ = "wb_ws_article"

    id = Column(BigInteger(), ForeignKey('wb_products.id'), unique=True, primary_key=True)
    product = relationship("Product", back_populates="article", foreign_keys=[id])

class Product(MPInsideBase):
    """Модель продукта."""
    
    __tablename__ = "wb_products"

    id = Column(BigInteger(), unique=True, primary_key=True)
    article = relationship("Article", back_populates="product", uselist=False)  # uselist=False для одно-единственного отношения
```

3. **Использование сессии**:
Убедитесь, что ваш код, использующий сессии, всегда выполняется в асинхронной функции. Проверьте, что `self.db.session` — это асинхронная сессия.

Пример использования:

```python
async with self.db.session() as session:
    print(filter_stmt.get('id'))
    stmt = select(Article).join(Product).filter_by(**filter_stmt)
    if options:
        for option in options:
            stmt = stmt.options(option)

    result = await session.execute(stmt)
    model = result.scalars().one()  # используйте один из вариантов: scalars() или unique() и т.д.
    product = model.product
    print(vars(product))

    return model
```

4. **Важные моменты**:
    - Убедитесь, что вы правильно используете SQLAlchemy с библиотеками для асинхронного выполнения, такими как `asyncpg` для PostgreSQL или `aiosqlite` для SQLite.
    - Убедитесь, что `create_engine` и `sessionmaker` используются для асинхронного контекста.

Если после внесения этих изменений у вас все еще возникают ошибки, то может быть полезно посмотреть на контекст, в котором вы вызываете эти функции (например, в цикле событий).
Похожие вопросы