### 🔧 Fixed - **🧾 Database Table Creation**: Унифицирован подход к созданию таблиц БД между продакшеном и тестами - Исправлена ошибка "no such table: author" в тестах - Обновлена функция `create_all_tables()` в `storage/schema.py` для использования стандартного SQLAlchemy подхода - Улучшены фикстуры тестов с принудительным импортом всех ORM моделей - Добавлена детальная диагностика создания таблиц в тестах - Добавлены fallback механизмы для создания таблиц в проблемных окружениях ### 🧪 Testing - Все RBAC тесты теперь проходят успешно - Исправлены фикстуры `test_engine`, `db_session` и `test_session_factory` - Добавлены функции `ensure_all_tables_exist()` и `ensure_all_models_imported()` для диагностики ### 📝 Technical Details - Заменен подход `create_table_if_not_exists()` на стандартный `Base.metadata.create_all()` - Улучшена обработка ошибок при создании таблиц - Добавлена проверка регистрации всех критических таблиц в metadata
This commit is contained in:
@@ -9,9 +9,7 @@ from ariadne import (
|
||||
load_schema_from_path,
|
||||
)
|
||||
|
||||
from orm import collection, community, draft, invite, notification, reaction, shout, topic
|
||||
from orm.author import Author, AuthorBookmark, AuthorFollower, AuthorRating
|
||||
from storage.db import create_table_if_not_exists
|
||||
from storage.db import engine
|
||||
|
||||
# Создаем основные типы
|
||||
query = QueryType()
|
||||
@@ -36,52 +34,64 @@ resolvers: SchemaBindable | type[Enum] | list[SchemaBindable | type[Enum]] = [
|
||||
|
||||
|
||||
def create_all_tables() -> None:
|
||||
"""Create all database tables in the correct order."""
|
||||
# Порядок важен - сначала таблицы без внешних ключей, затем зависимые таблицы
|
||||
models_in_order = [
|
||||
# user.User, # Базовая таблица auth
|
||||
Author, # Базовая таблица
|
||||
community.Community, # Базовая таблица
|
||||
topic.Topic, # Базовая таблица
|
||||
# Связи для базовых таблиц
|
||||
AuthorFollower, # Зависит от Author
|
||||
community.CommunityFollower, # Зависит от Community
|
||||
topic.TopicFollower, # Зависит от Topic
|
||||
# Черновики (теперь без зависимости от Shout)
|
||||
draft.Draft, # Зависит только от Author
|
||||
draft.DraftAuthor, # Зависит от Draft и Author
|
||||
draft.DraftTopic, # Зависит от Draft и Topic
|
||||
# Основные таблицы контента
|
||||
shout.Shout, # Зависит от Author и Draft
|
||||
shout.ShoutAuthor, # Зависит от Shout и Author
|
||||
shout.ShoutTopic, # Зависит от Shout и Topic
|
||||
# Реакции
|
||||
reaction.Reaction, # Зависит от Author и Shout
|
||||
shout.ShoutReactionsFollower, # Зависит от Shout и Reaction
|
||||
# Дополнительные таблицы
|
||||
AuthorRating, # Зависит от Author
|
||||
AuthorBookmark, # Зависит от Author
|
||||
notification.Notification, # Зависит от Author
|
||||
notification.NotificationSeen, # Зависит от Notification
|
||||
collection.Collection, # Зависит от Author
|
||||
invite.Invite, # Зависит от Author и Shout
|
||||
collection.ShoutCollection, # Зависит от Collection и Shout
|
||||
]
|
||||
"""Create all database tables using SQLAlchemy's standard approach."""
|
||||
try:
|
||||
# Импортируем все модели чтобы они были зарегистрированы в Base.metadata
|
||||
|
||||
from storage.db import engine
|
||||
# Получаем Base с зарегистрированными моделями
|
||||
from orm.base import BaseModel as Base
|
||||
|
||||
# Используем одно соединение для всех таблиц, чтобы избежать проблем с транзакциями
|
||||
with engine.connect() as connection:
|
||||
for model in models_in_order:
|
||||
try:
|
||||
# Ensure model is a type[DeclarativeBase]
|
||||
if not hasattr(model, "__tablename__"):
|
||||
logger.warning(f"Skipping {model} - not a DeclarativeBase model")
|
||||
continue
|
||||
# Проверяем что все критические таблицы зарегистрированы
|
||||
required_tables = [
|
||||
"author",
|
||||
"community",
|
||||
"community_author",
|
||||
"community_follower",
|
||||
"draft",
|
||||
"draft_author",
|
||||
"draft_topic",
|
||||
"shout",
|
||||
"shout_author",
|
||||
"shout_topic",
|
||||
"shout_reactions_followers",
|
||||
"topic",
|
||||
"topic_followers",
|
||||
"reaction",
|
||||
"invite",
|
||||
"notification",
|
||||
"collection",
|
||||
"author_follower",
|
||||
"author_rating",
|
||||
"author_bookmark",
|
||||
]
|
||||
|
||||
create_table_if_not_exists(connection, model) # type: ignore[arg-type]
|
||||
# logger.info(f"Created or verified table: {model.__tablename__}")
|
||||
except Exception as e:
|
||||
table_name = getattr(model, "__tablename__", str(model))
|
||||
logger.error(f"Error creating table {table_name}: {e}")
|
||||
raise
|
||||
registered_tables = list(Base.metadata.tables.keys())
|
||||
missing_tables = [table for table in required_tables if table not in registered_tables]
|
||||
|
||||
if missing_tables:
|
||||
logger.warning(f"Missing tables in Base.metadata: {missing_tables}")
|
||||
logger.info(f"Available tables: {registered_tables}")
|
||||
|
||||
# Создаем все таблицы стандартным способом SQLAlchemy
|
||||
logger.info("Creating all database tables...")
|
||||
Base.metadata.create_all(bind=engine)
|
||||
|
||||
# Проверяем результат
|
||||
from sqlalchemy import inspect
|
||||
|
||||
inspector = inspect(engine)
|
||||
created_tables = inspector.get_table_names()
|
||||
logger.info(f"✅ Created tables: {created_tables}")
|
||||
|
||||
# Проверяем критически важные таблицы
|
||||
missing_created = [table for table in required_tables if table not in created_tables]
|
||||
if missing_created:
|
||||
error_msg = f"Failed to create critical tables: {missing_created}"
|
||||
logger.error(f"❌ Missing critical tables: {missing_created}")
|
||||
raise RuntimeError(error_msg)
|
||||
|
||||
logger.info("✅ All critical tables created successfully")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error creating database tables: {e}")
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user