forked from HSE_team/BetterCallPraskovia
db created
This commit is contained in:
parent
39bcd1c16e
commit
ae252a796c
4
backend/src/domain/entities/__init__.py
Normal file
4
backend/src/domain/entities/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
"""
|
||||
Domain entities
|
||||
"""
|
||||
|
||||
26
backend/src/domain/entities/collection.py
Normal file
26
backend/src/domain/entities/collection.py
Normal file
@ -0,0 +1,26 @@
|
||||
"""
|
||||
Доменная сущность Collection
|
||||
"""
|
||||
from datetime import datetime
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
|
||||
class Collection:
|
||||
"""Каталог документов"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
owner_id: UUID,
|
||||
description: str = "",
|
||||
is_public: bool = False,
|
||||
collection_id: UUID | None = None,
|
||||
created_at: datetime | None = None
|
||||
):
|
||||
self.collection_id = collection_id or uuid4()
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.owner_id = owner_id
|
||||
self.is_public = is_public
|
||||
self.created_at = created_at or datetime.utcnow()
|
||||
|
||||
22
backend/src/domain/entities/collection_access.py
Normal file
22
backend/src/domain/entities/collection_access.py
Normal file
@ -0,0 +1,22 @@
|
||||
"""
|
||||
Доменная сущность CollectionAccess
|
||||
"""
|
||||
from datetime import datetime
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
|
||||
class CollectionAccess:
|
||||
"""Доступ пользователя к коллекции"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
user_id: UUID,
|
||||
collection_id: UUID,
|
||||
access_id: UUID | None = None,
|
||||
created_at: datetime | None = None
|
||||
):
|
||||
self.access_id = access_id or uuid4()
|
||||
self.user_id = user_id
|
||||
self.collection_id = collection_id
|
||||
self.created_at = created_at or datetime.utcnow()
|
||||
|
||||
28
backend/src/domain/entities/conversation.py
Normal file
28
backend/src/domain/entities/conversation.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""
|
||||
Доменная сущность Conversation
|
||||
"""
|
||||
from datetime import datetime
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
|
||||
class Conversation:
|
||||
"""Беседа пользователя с ИИ"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
user_id: UUID,
|
||||
collection_id: UUID,
|
||||
conversation_id: UUID | None = None,
|
||||
created_at: datetime | None = None,
|
||||
updated_at: datetime | None = None
|
||||
):
|
||||
self.conversation_id = conversation_id or uuid4()
|
||||
self.user_id = user_id
|
||||
self.collection_id = collection_id
|
||||
self.created_at = created_at or datetime.utcnow()
|
||||
self.updated_at = updated_at or datetime.utcnow()
|
||||
|
||||
def update_timestamp(self):
|
||||
"""Обновить время последнего изменения"""
|
||||
self.updated_at = datetime.utcnow()
|
||||
|
||||
27
backend/src/domain/entities/document.py
Normal file
27
backend/src/domain/entities/document.py
Normal file
@ -0,0 +1,27 @@
|
||||
"""
|
||||
Доменная сущность Document
|
||||
"""
|
||||
from datetime import datetime
|
||||
from uuid import UUID, uuid4
|
||||
from typing import Any
|
||||
|
||||
|
||||
class Document:
|
||||
"""Документ в коллекции"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
collection_id: UUID,
|
||||
title: str,
|
||||
content: str,
|
||||
metadata: dict[str, Any] | None = None,
|
||||
document_id: UUID | None = None,
|
||||
created_at: datetime | None = None
|
||||
):
|
||||
self.document_id = document_id or uuid4()
|
||||
self.collection_id = collection_id
|
||||
self.title = title
|
||||
self.content = content
|
||||
self.metadata = metadata or {}
|
||||
self.created_at = created_at or datetime.utcnow()
|
||||
|
||||
25
backend/src/domain/entities/embedding.py
Normal file
25
backend/src/domain/entities/embedding.py
Normal file
@ -0,0 +1,25 @@
|
||||
"""
|
||||
Доменная сущность Embedding
|
||||
"""
|
||||
from datetime import datetime
|
||||
from uuid import UUID, uuid4
|
||||
from typing import Any
|
||||
|
||||
|
||||
class Embedding:
|
||||
"""Эмбеддинг документа"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
document_id: UUID,
|
||||
embedding: list[float] | None = None,
|
||||
model_version: str = "",
|
||||
embedding_id: UUID | None = None,
|
||||
created_at: datetime | None = None
|
||||
):
|
||||
self.embedding_id = embedding_id or uuid4()
|
||||
self.document_id = document_id
|
||||
self.embedding = embedding or []
|
||||
self.model_version = model_version
|
||||
self.created_at = created_at or datetime.utcnow()
|
||||
|
||||
35
backend/src/domain/entities/message.py
Normal file
35
backend/src/domain/entities/message.py
Normal file
@ -0,0 +1,35 @@
|
||||
"""
|
||||
Доменная сущность Message
|
||||
"""
|
||||
from datetime import datetime
|
||||
from uuid import UUID, uuid4
|
||||
from typing import Any
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class MessageRole(str, Enum):
|
||||
"""Роли сообщений"""
|
||||
USER = "user"
|
||||
ASSISTANT = "assistant"
|
||||
SYSTEM = "system"
|
||||
|
||||
|
||||
class Message:
|
||||
"""Сообщение в беседе"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
conversation_id: UUID,
|
||||
content: str,
|
||||
role: MessageRole,
|
||||
sources: dict[str, Any] | None = None,
|
||||
message_id: UUID | None = None,
|
||||
created_at: datetime | None = None
|
||||
):
|
||||
self.message_id = message_id or uuid4()
|
||||
self.conversation_id = conversation_id
|
||||
self.content = content
|
||||
self.role = role
|
||||
self.sources = sources or {}
|
||||
self.created_at = created_at or datetime.utcnow()
|
||||
|
||||
33
backend/src/domain/entities/user.py
Normal file
33
backend/src/domain/entities/user.py
Normal file
@ -0,0 +1,33 @@
|
||||
"""
|
||||
Доменная сущность User
|
||||
"""
|
||||
from datetime import datetime
|
||||
from uuid import UUID, uuid4
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class UserRole(str, Enum):
|
||||
"""Роли пользователей"""
|
||||
USER = "user"
|
||||
ADMIN = "admin"
|
||||
|
||||
|
||||
class User:
|
||||
"""Пользователь системы"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
telegram_id: str,
|
||||
role: UserRole = UserRole.USER,
|
||||
user_id: UUID | None = None,
|
||||
created_at: datetime | None = None
|
||||
):
|
||||
self.user_id = user_id or uuid4()
|
||||
self.telegram_id = telegram_id
|
||||
self.role = role
|
||||
self.created_at = created_at or datetime.utcnow()
|
||||
|
||||
def is_admin(self) -> bool:
|
||||
"""проверка, является ли пользователь администратором"""
|
||||
return self.role == UserRole.ADMIN
|
||||
|
||||
4
backend/src/domain/repositories/__init__.py
Normal file
4
backend/src/domain/repositories/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
"""
|
||||
Domain repositories interfaces
|
||||
"""
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
"""
|
||||
Интерфейс репозитория для CollectionAccess
|
||||
"""
|
||||
from abc import ABC, abstractmethod
|
||||
from uuid import UUID
|
||||
from typing import Optional
|
||||
from src.domain.entities.collection_access import CollectionAccess
|
||||
|
||||
|
||||
class ICollectionAccessRepository(ABC):
|
||||
"""Интерфейс репозитория доступа к коллекциям"""
|
||||
|
||||
@abstractmethod
|
||||
async def create(self, access: CollectionAccess) -> CollectionAccess:
|
||||
"""Создать доступ"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_by_id(self, access_id: UUID) -> Optional[CollectionAccess]:
|
||||
"""Получить доступ по ID"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def delete(self, access_id: UUID) -> bool:
|
||||
"""Удалить доступ"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def delete_by_user_and_collection(self, user_id: UUID, collection_id: UUID) -> bool:
|
||||
"""Удалить доступ пользователя к коллекции"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_by_user_and_collection(self, user_id: UUID, collection_id: UUID) -> Optional[CollectionAccess]:
|
||||
"""Получить доступ пользователя к коллекции"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def list_by_user(self, user_id: UUID) -> list[CollectionAccess]:
|
||||
"""Получить доступы пользователя"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def list_by_collection(self, collection_id: UUID) -> list[CollectionAccess]:
|
||||
"""Получить доступы к коллекции"""
|
||||
pass
|
||||
|
||||
42
backend/src/domain/repositories/collection_repository.py
Normal file
42
backend/src/domain/repositories/collection_repository.py
Normal file
@ -0,0 +1,42 @@
|
||||
"""
|
||||
Интерфейс репозитория для Collection
|
||||
"""
|
||||
from abc import ABC, abstractmethod
|
||||
from uuid import UUID
|
||||
from typing import Optional
|
||||
from src.domain.entities.collection import Collection
|
||||
|
||||
|
||||
class ICollectionRepository(ABC):
|
||||
"""Интерфейс репозитория коллекций"""
|
||||
|
||||
@abstractmethod
|
||||
async def create(self, collection: Collection) -> Collection:
|
||||
"""Создать коллекцию"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_by_id(self, collection_id: UUID) -> Optional[Collection]:
|
||||
"""Получить коллекцию по ID"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def update(self, collection: Collection) -> Collection:
|
||||
"""Обновить коллекцию"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def delete(self, collection_id: UUID) -> bool:
|
||||
"""Удалить коллекцию"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def list_by_owner(self, owner_id: UUID, skip: int = 0, limit: int = 100) -> list[Collection]:
|
||||
"""Получить коллекции владельца"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def list_public(self, skip: int = 0, limit: int = 100) -> list[Collection]:
|
||||
"""Получить публичные коллекции"""
|
||||
pass
|
||||
|
||||
42
backend/src/domain/repositories/conversation_repository.py
Normal file
42
backend/src/domain/repositories/conversation_repository.py
Normal file
@ -0,0 +1,42 @@
|
||||
"""
|
||||
Интерфейс репозитория для Conversation
|
||||
"""
|
||||
from abc import ABC, abstractmethod
|
||||
from uuid import UUID
|
||||
from typing import Optional
|
||||
from src.domain.entities.conversation import Conversation
|
||||
|
||||
|
||||
class IConversationRepository(ABC):
|
||||
"""Интерфейс репозитория бесед"""
|
||||
|
||||
@abstractmethod
|
||||
async def create(self, conversation: Conversation) -> Conversation:
|
||||
"""Создать беседу"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_by_id(self, conversation_id: UUID) -> Optional[Conversation]:
|
||||
"""Получить беседу по ID"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def update(self, conversation: Conversation) -> Conversation:
|
||||
"""Обновить беседу"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def delete(self, conversation_id: UUID) -> bool:
|
||||
"""Удалить беседу"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def list_by_user(self, user_id: UUID, skip: int = 0, limit: int = 100) -> list[Conversation]:
|
||||
"""Получить беседы пользователя"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def list_by_collection(self, collection_id: UUID, skip: int = 0, limit: int = 100) -> list[Conversation]:
|
||||
"""Получить беседы по коллекции"""
|
||||
pass
|
||||
|
||||
37
backend/src/domain/repositories/document_repository.py
Normal file
37
backend/src/domain/repositories/document_repository.py
Normal file
@ -0,0 +1,37 @@
|
||||
"""
|
||||
Интерфейс репозитория для Document
|
||||
"""
|
||||
from abc import ABC, abstractmethod
|
||||
from uuid import UUID
|
||||
from typing import Optional
|
||||
from src.domain.entities.document import Document
|
||||
|
||||
|
||||
class IDocumentRepository(ABC):
|
||||
"""Интерфейс репозитория документов"""
|
||||
|
||||
@abstractmethod
|
||||
async def create(self, document: Document) -> Document:
|
||||
"""Создать документ"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_by_id(self, document_id: UUID) -> Optional[Document]:
|
||||
"""Получить документ по ID"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def update(self, document: Document) -> Document:
|
||||
"""Обновить документ"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def delete(self, document_id: UUID) -> bool:
|
||||
"""Удалить документ"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def list_by_collection(self, collection_id: UUID, skip: int = 0, limit: int = 100) -> list[Document]:
|
||||
"""Получить документы коллекции"""
|
||||
pass
|
||||
|
||||
37
backend/src/domain/repositories/message_repository.py
Normal file
37
backend/src/domain/repositories/message_repository.py
Normal file
@ -0,0 +1,37 @@
|
||||
"""
|
||||
Интерфейс репозитория для Message
|
||||
"""
|
||||
from abc import ABC, abstractmethod
|
||||
from uuid import UUID
|
||||
from typing import Optional
|
||||
from src.domain.entities.message import Message
|
||||
|
||||
|
||||
class IMessageRepository(ABC):
|
||||
"""Интерфейс репозитория сообщений"""
|
||||
|
||||
@abstractmethod
|
||||
async def create(self, message: Message) -> Message:
|
||||
"""Создать сообщение"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_by_id(self, message_id: UUID) -> Optional[Message]:
|
||||
"""Получить сообщение по ID"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def update(self, message: Message) -> Message:
|
||||
"""Обновить сообщение"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def delete(self, message_id: UUID) -> bool:
|
||||
"""Удалить сообщение"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def list_by_conversation(self, conversation_id: UUID, skip: int = 0, limit: int = 100) -> list[Message]:
|
||||
"""Получить сообщения беседы"""
|
||||
pass
|
||||
|
||||
42
backend/src/domain/repositories/user_repository.py
Normal file
42
backend/src/domain/repositories/user_repository.py
Normal file
@ -0,0 +1,42 @@
|
||||
"""
|
||||
Интерфейс репозитория для User
|
||||
"""
|
||||
from abc import ABC, abstractmethod
|
||||
from uuid import UUID
|
||||
from typing import Optional
|
||||
from src.domain.entities.user import User
|
||||
|
||||
|
||||
class IUserRepository(ABC):
|
||||
"""Интерфейс репозитория пользователей"""
|
||||
|
||||
@abstractmethod
|
||||
async def create(self, user: User) -> User:
|
||||
"""Создать пользователя"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_by_id(self, user_id: UUID) -> Optional[User]:
|
||||
"""Получить пользователя по ID"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_by_telegram_id(self, telegram_id: str) -> Optional[User]:
|
||||
"""Получить пользователя по Telegram ID"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def update(self, user: User) -> User:
|
||||
"""Обновить пользователя"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def delete(self, user_id: UUID) -> bool:
|
||||
"""Удалить пользователя"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def list_all(self, skip: int = 0, limit: int = 100) -> list[User]:
|
||||
"""Получить список всех пользователей"""
|
||||
pass
|
||||
|
||||
4
backend/src/infrastructure/database/__init__.py
Normal file
4
backend/src/infrastructure/database/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
"""
|
||||
Database infrastructure
|
||||
"""
|
||||
|
||||
32
backend/src/infrastructure/database/base.py
Normal file
32
backend/src/infrastructure/database/base.py
Normal file
@ -0,0 +1,32 @@
|
||||
"""
|
||||
Базовые настройки базы данных
|
||||
"""
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
|
||||
from sqlalchemy.orm import declarative_base
|
||||
from src.shared.config import settings
|
||||
|
||||
engine = create_async_engine(
|
||||
settings.database_url.replace("postgresql://", "postgresql+asyncpg://"),
|
||||
echo=settings.DEBUG,
|
||||
future=True
|
||||
)
|
||||
|
||||
AsyncSessionLocal = async_sessionmaker(
|
||||
engine,
|
||||
class_=AsyncSession,
|
||||
expire_on_commit=False,
|
||||
autocommit=False,
|
||||
autoflush=False
|
||||
)
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
async def get_db() -> AsyncSession:
|
||||
"""Dependency для получения сессии БД"""
|
||||
async with AsyncSessionLocal() as session:
|
||||
try:
|
||||
yield session
|
||||
finally:
|
||||
await session.close()
|
||||
|
||||
109
backend/src/infrastructure/database/models.py
Normal file
109
backend/src/infrastructure/database/models.py
Normal file
@ -0,0 +1,109 @@
|
||||
"""
|
||||
SQLAlchemy модели для базы данных
|
||||
"""
|
||||
from sqlalchemy import Column, String, Text, Boolean, DateTime, ForeignKey, JSON, Integer
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import relationship
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
from src.infrastructure.database.base import Base
|
||||
|
||||
|
||||
class UserModel(Base):
|
||||
"""Модель пользователя"""
|
||||
__tablename__ = "users"
|
||||
|
||||
user_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
telegram_id = Column(String, unique=True, nullable=False, index=True)
|
||||
role = Column(String, nullable=False, default="user")
|
||||
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
|
||||
collections = relationship("CollectionModel", back_populates="owner", cascade="all, delete-orphan")
|
||||
conversations = relationship("ConversationModel", back_populates="user", cascade="all, delete-orphan")
|
||||
collection_accesses = relationship("CollectionAccessModel", back_populates="user", cascade="all, delete-orphan")
|
||||
|
||||
|
||||
class CollectionModel(Base):
|
||||
"""Модель коллекции"""
|
||||
__tablename__ = "collections"
|
||||
|
||||
collection_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
name = Column(String, nullable=False)
|
||||
description = Column(Text, nullable=True)
|
||||
owner_id = Column(UUID(as_uuid=True), ForeignKey("users.user_id"), nullable=False)
|
||||
is_public = Column(Boolean, nullable=False, default=False)
|
||||
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
|
||||
owner = relationship("UserModel", back_populates="collections")
|
||||
documents = relationship("DocumentModel", back_populates="collection", cascade="all, delete-orphan")
|
||||
conversations = relationship("ConversationModel", back_populates="collection", cascade="all, delete-orphan")
|
||||
accesses = relationship("CollectionAccessModel", back_populates="collection", cascade="all, delete-orphan")
|
||||
|
||||
|
||||
class DocumentModel(Base):
|
||||
"""Модель документа"""
|
||||
__tablename__ = "documents"
|
||||
|
||||
document_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
collection_id = Column(UUID(as_uuid=True), ForeignKey("collections.collection_id"), nullable=False)
|
||||
title = Column(String, nullable=False)
|
||||
content = Column(Text, nullable=False)
|
||||
document_metadata = Column("metadata", JSON, nullable=True, default={})
|
||||
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
|
||||
collection = relationship("CollectionModel", back_populates="documents")
|
||||
embeddings = relationship("EmbeddingModel", back_populates="document", cascade="all, delete-orphan")
|
||||
|
||||
|
||||
class EmbeddingModel(Base):
|
||||
"""Модель эмбеддинга (заглушка)"""
|
||||
__tablename__ = "embeddings"
|
||||
|
||||
embedding_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
document_id = Column(UUID(as_uuid=True), ForeignKey("documents.document_id"), nullable=False)
|
||||
embedding = Column(JSON, nullable=True)
|
||||
model_version = Column(String, nullable=True)
|
||||
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
|
||||
document = relationship("DocumentModel", back_populates="embeddings")
|
||||
|
||||
|
||||
class ConversationModel(Base):
|
||||
"""Модель беседы"""
|
||||
__tablename__ = "conversations"
|
||||
|
||||
conversation_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
user_id = Column(UUID(as_uuid=True), ForeignKey("users.user_id"), nullable=False)
|
||||
collection_id = Column(UUID(as_uuid=True), ForeignKey("collections.collection_id"), nullable=False)
|
||||
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
|
||||
updated_at = Column(DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
user = relationship("UserModel", back_populates="conversations")
|
||||
collection = relationship("CollectionModel", back_populates="conversations")
|
||||
messages = relationship("MessageModel", back_populates="conversation", cascade="all, delete-orphan")
|
||||
|
||||
|
||||
class MessageModel(Base):
|
||||
"""Модель сообщения"""
|
||||
__tablename__ = "messages"
|
||||
|
||||
message_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
conversation_id = Column(UUID(as_uuid=True), ForeignKey("conversations.conversation_id"), nullable=False)
|
||||
content = Column(Text, nullable=False)
|
||||
role = Column(String, nullable=False)
|
||||
sources = Column(JSON, nullable=True, default={})
|
||||
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
|
||||
conversation = relationship("ConversationModel", back_populates="messages")
|
||||
|
||||
|
||||
class CollectionAccessModel(Base):
|
||||
"""Модель доступа к коллекции"""
|
||||
__tablename__ = "collection_access"
|
||||
|
||||
access_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
user_id = Column(UUID(as_uuid=True), ForeignKey("users.user_id"), nullable=False)
|
||||
collection_id = Column(UUID(as_uuid=True), ForeignKey("collections.collection_id"), nullable=False)
|
||||
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
|
||||
user = relationship("UserModel", back_populates="collection_accesses")
|
||||
collection = relationship("CollectionModel", back_populates="accesses")
|
||||
|
||||
__table_args__ = (
|
||||
{"comment": "Уникальный доступ пользователя к коллекции"},
|
||||
)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user