Как создать внешний ключ для таблицы, которая не является частью SQLAlchemy ORM?

Вопрос или проблема

Я пытаюсь создать внешний ключ к таблице, которая управляется Django ORM, однако SQLA, похоже, не любит это.

class SomeSAModel(DeclarativeBase):
    user_id: Mapped[int] = mapped_column(
        sa.ForeignKey("users_customuser.id")  # это ссылка на таблицу Django
    )  

И это ошибка, которую я получаю, когда запускаю alembic revision --autogenerate

  File "/home/dev/Desktop/t5hob/Backend/alembic/env.py", line 137, in run_migrations_online
    context.run_migrations()
  File "<string>", line 8, in run_migrations
  File "/home/dev/Desktop/t5hob/Backend/.venv/lib/python3.12/site-packages/alembic/runtime/environment.py", line 946, in run_migrations
    self.get_context().run_migrations(**kw)
  File "/home/dev/Desktop/t5hob/Backend/.venv/lib/python3.12/site-packages/alembic/runtime/migration.py", line 616, in run_migrations
    for step in self._migrations_fn(heads, self):
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/dev/Desktop/t5hob/Backend/.venv/lib/python3.12/site-packages/alembic/command.py", line 212, in retrieve_migrations
    revision_context.run_autogenerate(rev, context)
  File "/home/dev/Desktop/t5hob/Backend/.venv/lib/python3.12/site-packages/alembic/autogenerate/api.py", line 570, in run_autogenerate
    self._run_environment(rev, migration_context, True)
  File "/home/dev/Desktop/t5hob/Backend/.venv/lib/python3.12/site-packages/alembic/autogenerate/api.py", line 617, in _run_environment
    compare._populate_migration_script(
  File "/home/dev/Desktop/t5hob/Backend/.venv/lib/python3.12/site-packages/alembic/autogenerate/compare.py", line 65, in _populate_migration_script
    _produce_net_changes(autogen_context, upgrade_ops)
  File "/home/dev/Desktop/t5hob/Backend/.venv/lib/python3.12/site-packages/alembic/autogenerate/compare.py", line 98, in _produce_net_changes
    comparators.dispatch("schema", autogen_context.dialect.name)(
  File "/home/dev/Desktop/t5hob/Backend/.venv/lib/python3.12/site-packages/alembic/util/langhelpers.py", line 310, in go
    fn(*arg, **kw)
  File "/home/dev/Desktop/t5hob/Backend/.venv/lib/python3.12/site-packages/alembic/autogenerate/compare.py", line 134, in _autogen_for_tables
    [(table.schema, table.name) for table in autogen_context.sorted_tables]
                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/dev/Desktop/t5hob/Backend/.venv/lib/python3.12/site-packages/sqlalchemy/util/langhelpers.py", line 1141, in __get__
    obj.__dict__[self.__name__] = result = self.fget(obj)
                                           ^^^^^^^^^^^^^^
  File "/home/dev/Desktop/t5hob/Backend/.venv/lib/python3.12/site-packages/alembic/autogenerate/api.py", line 482, in sorted_tables
    result.extend(m.sorted_tables)
                  ^^^^^^^^^^^^^^^
  File "/home/dev/Desktop/t5hob/Backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/schema.py", line 5626, in sorted_tables
    return ddl.sort_tables(
           ^^^^^^^^^^^^^^^^
  File "/home/dev/Desktop/t5hob/Backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/ddl.py", line 1252, in sort_tables
    for (t, fkcs) in sort_tables_and_constraints(
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/dev/Desktop/t5hob/Backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/ddl.py", line 1328, in sort_tables_and_constraints
    dependent_on = fkc.referred_table
                   ^^^^^^^^^^^^^^^^^^
  File "/home/dev/Desktop/t5hob/Backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/schema.py", line 4753, in referred_table
    return self.elements[0].column.table
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/dev/Desktop/t5hob/Backend/.venv/lib/python3.12/site-packages/sqlalchemy/util/langhelpers.py", line 1141, in __get__
    obj.__dict__[self.__name__] = result = self.fget(obj)
                                           ^^^^^^^^^^^^^^
  File "/home/dev/Desktop/t5hob/Backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/schema.py", line 3170, in column
    return self._resolve_column()
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/dev/Desktop/t5hob/Backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/schema.py", line 3193, in _resolve_column
    raise exc.NoReferencedTableError(
sqlalchemy.exc.NoReferencedTableError: Внешний ключ, связанный со столбцом 'incidents__incident.ack_by', не может найти таблицу 'users_customuser', с которой нужно сгенерировать внешний ключ к целевому столбцу 'id'

Ответ или решение

Чтобы создать внешний ключ (Foreign Key) для таблицы, управляемой не SQLAlchemy ORM, а Django ORM, необходимо учесть несколько важных аспектов. При этом нужно понимать, что SQLAlchemy может не распознавать некоторые таблицы, если они созданы или управляются другими системами, такими как Django. Ниже представлен подробный алгоритм действий, который поможет решить описанную вами проблему.

Шаг 1: Поиск решения проблемы с миграцией

Вы сталкиваетесь с ошибкой NoReferencedTableError, что указывает на то, что SQLAlchemy не может найти таблицу users_customuser, к которой вы пытаетесь создать внешний ключ. Это может произойти, если таблица создана, но SQLAlchemy не знает о ней. Чтобы решить эту проблему, можно использовать некоторые обходные пути.

Шаг 2: Объявление таблицы в SQLAlchemy

Для начала вам нужно вручную определить метаданные таблицы users_customuser в SQLAlchemy. Это позволяет SQLAlchemy узнать о ее существовании и правильно работать с ней. Вы можете сделать это следующим образом:

from sqlalchemy import Table, Column, Integer, MetaData

metadata = MetaData()

users_custom_user = Table(
    'users_customuser', metadata,
    Column('id', Integer, primary_key=True),
    # Добавьте другие поля, если необходимо
)

Шаг 3: Определение модели с внешним ключом

Теперь, когда у вас есть определение таблицы, вы можете настроить модель SomeSAModel с использованием этого внешнего ключа. Убедитесь, что вы ссылаетесь на определение таблицы, которое вы создали на предыдущем шаге.

from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.ext.declarative import declarative_base

DeclarativeBase = declarative_base()

class SomeSAModel(DeclarativeBase):
    __tablename__ = 'incidents__incident'

    id: Mapped[int] = mapped_column(primary_key=True)
    user_id: Mapped[int] = mapped_column(sa.ForeignKey('users_customuser.id'))

Шаг 4: Применение миграции с Alembic

После того, как вы скорректировали ваши определения, попробуйте повторно выполнить команду для миграции:

alembic revision --autogenerate

Шаг 5: Убедитесь в наличии таблицы

Прежде чем выполнять миграции, убедитесь, что таблица users_customuser действительно существует в вашей базе данных, иначе вы всё равно столкнётесь с ошибками.

Заключение

В результате, правильное управление зависимостями таблиц между SQLAlchemy и Django ORM требует ручного определения таблиц, чтобы ORM могла правильно работать с миграциями и настройками базы данных. Если вы следуете описанным шагам, ваша проблема с созданием внешнего ключа должна быть успешно решена.

Важно понимать, что такой подход к интеграции требует внимательного планирования структуры базы данных и, возможно, использования дополнительных инструментов для синхронизации миграций между двумя ORM.

Оцените материал
Добавить комментарий

Капча загружается...