mirror of
https://github.com/wowlikon/LiB.git
synced 2026-02-04 12:31:09 +00:00
Динамическое создание er-диаграммы по моделям
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
"""Модуль DTO-моделей авторов"""
|
||||
|
||||
from typing import List
|
||||
|
||||
from pydantic import ConfigDict
|
||||
from sqlmodel import SQLModel
|
||||
from sqlmodel import SQLModel, Field
|
||||
|
||||
|
||||
class AuthorBase(SQLModel):
|
||||
"""Базовая модель автора"""
|
||||
name: str
|
||||
|
||||
name: str = Field(description="Псевдоним")
|
||||
|
||||
model_config = ConfigDict( # pyright: ignore
|
||||
json_schema_extra={"example": {"name": "author_name"}}
|
||||
@@ -16,20 +18,24 @@ class AuthorBase(SQLModel):
|
||||
|
||||
class AuthorCreate(AuthorBase):
|
||||
"""Модель автора для создания"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class AuthorUpdate(SQLModel):
|
||||
"""Модель автора для обновления"""
|
||||
name: str | None = None
|
||||
|
||||
name: str | None = Field(None, description="Псевдоним")
|
||||
|
||||
|
||||
class AuthorRead(AuthorBase):
|
||||
"""Модель автора для чтения"""
|
||||
id: int
|
||||
|
||||
id: int = Field(description="Идентификатор")
|
||||
|
||||
|
||||
class AuthorList(SQLModel):
|
||||
"""Список авторов"""
|
||||
authors: List[AuthorRead]
|
||||
total: int
|
||||
|
||||
authors: List[AuthorRead] = Field(description="Список авторов")
|
||||
total: int = Field(description="Количество авторов")
|
||||
|
||||
@@ -11,9 +11,9 @@ from library_service.models.enums import BookStatus
|
||||
class BookBase(SQLModel):
|
||||
"""Базовая модель книги"""
|
||||
|
||||
title: str
|
||||
description: str
|
||||
page_count: int = Field(gt=0)
|
||||
title: str = Field(description="Название")
|
||||
description: str = Field(description="Описание")
|
||||
page_count: int = Field(gt=0, description="Количество страниц")
|
||||
|
||||
model_config = ConfigDict( # pyright: ignore
|
||||
json_schema_extra={
|
||||
@@ -35,21 +35,21 @@ class BookCreate(BookBase):
|
||||
class BookUpdate(SQLModel):
|
||||
"""Модель книги для обновления"""
|
||||
|
||||
title: str | None = None
|
||||
description: str | None = None
|
||||
page_count: int | None = None
|
||||
status: BookStatus | None = None
|
||||
title: str | None = Field(None, description="Название")
|
||||
description: str | None = Field(None, description="Описание")
|
||||
page_count: int | None = Field(None, description="Количество страниц")
|
||||
status: BookStatus | None = Field(None, description="Статус")
|
||||
|
||||
|
||||
class BookRead(BookBase):
|
||||
"""Модель книги для чтения"""
|
||||
|
||||
id: int
|
||||
status: BookStatus
|
||||
id: int = Field(description="Идентификатор")
|
||||
status: BookStatus = Field(description="Статус")
|
||||
|
||||
|
||||
class BookList(SQLModel):
|
||||
"""Список книг"""
|
||||
|
||||
books: List[BookRead]
|
||||
total: int
|
||||
books: List[BookRead] = Field(description="Список книг")
|
||||
total: int = Field(description="Количество книг")
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
"""Модуль DTO-моделей жанров"""
|
||||
|
||||
from typing import List
|
||||
|
||||
from pydantic import ConfigDict
|
||||
from sqlmodel import SQLModel
|
||||
from sqlmodel import SQLModel, Field
|
||||
|
||||
|
||||
class GenreBase(SQLModel):
|
||||
"""Базовая модель жанра"""
|
||||
name: str
|
||||
|
||||
name: str = Field(description="Название")
|
||||
|
||||
model_config = ConfigDict( # pyright: ignore
|
||||
json_schema_extra={"example": {"name": "genre_name"}}
|
||||
@@ -16,20 +18,24 @@ class GenreBase(SQLModel):
|
||||
|
||||
class GenreCreate(GenreBase):
|
||||
"""Модель жанра для создания"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class GenreUpdate(SQLModel):
|
||||
"""Модель жанра для обновления"""
|
||||
name: str | None = None
|
||||
|
||||
name: str | None = Field(None, description="Название")
|
||||
|
||||
|
||||
class GenreRead(GenreBase):
|
||||
"""Модель жанра для чтения"""
|
||||
id: int
|
||||
|
||||
id: int = Field(description="Идентификатор")
|
||||
|
||||
|
||||
class GenreList(SQLModel):
|
||||
"""Списко жанров"""
|
||||
genres: List[GenreRead]
|
||||
total: int
|
||||
|
||||
genres: List[GenreRead] = Field(description="Список жанров")
|
||||
total: int = Field(description="Количество жанров")
|
||||
|
||||
@@ -1,37 +1,49 @@
|
||||
"""Модуль DTO-моделей для выдачи книг"""
|
||||
|
||||
from typing import List
|
||||
|
||||
from datetime import datetime
|
||||
from sqlmodel import SQLModel
|
||||
from sqlmodel import SQLModel, Field
|
||||
|
||||
|
||||
class LoanBase(SQLModel):
|
||||
"""Базовая модель выдачи"""
|
||||
book_id: int
|
||||
user_id: int
|
||||
due_date: datetime
|
||||
|
||||
book_id: int = Field(description="Идентификатор книги")
|
||||
user_id: int = Field(description="Идентификатор пользователя")
|
||||
due_date: datetime = Field(description="Дата и время планируемого возврата")
|
||||
|
||||
|
||||
class LoanCreate(LoanBase):
|
||||
"""Модель для создания записи о выдаче"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class LoanUpdate(SQLModel):
|
||||
"""Модель для обновления записи о выдаче"""
|
||||
user_id: int | None = None
|
||||
due_date: datetime | None = None
|
||||
returned_at: datetime | None = None
|
||||
|
||||
user_id: int | None = Field(None, description="Идентификатор пользователя")
|
||||
due_date: datetime | None = Field(
|
||||
None, description="дата и время планируемого возврата"
|
||||
)
|
||||
returned_at: datetime | None = Field(
|
||||
None, description="Дата и время фактического возврата"
|
||||
)
|
||||
|
||||
|
||||
class LoanRead(LoanBase):
|
||||
"""Модель чтения записи о выдаче"""
|
||||
id: int
|
||||
borrowed_at: datetime
|
||||
returned_at: datetime | None = None
|
||||
|
||||
id: int = Field(description="Идентификатор")
|
||||
borrowed_at: datetime = Field(description="Дата и время выдачи")
|
||||
returned_at: datetime | None = Field(
|
||||
None, description="Дата и время фактического возврата"
|
||||
)
|
||||
|
||||
|
||||
class LoanList(SQLModel):
|
||||
"""Список выдач"""
|
||||
loans: List[LoanRead]
|
||||
total: int
|
||||
|
||||
loans: List[LoanRead] = Field(description="Список выдач")
|
||||
total: int = Field(description="Количество выдач")
|
||||
|
||||
@@ -18,130 +18,142 @@ from .recovery import RecoveryCodesResponse
|
||||
class AuthorWithBooks(SQLModel):
|
||||
"""Модель автора с книгами"""
|
||||
|
||||
id: int
|
||||
name: str
|
||||
books: List[BookRead] = Field(default_factory=list)
|
||||
id: int = Field(description="Идентификатор")
|
||||
name: str = Field(description="Псевдоним")
|
||||
books: List[BookRead] = Field(default_factory=list, description="Список книг")
|
||||
|
||||
|
||||
class GenreWithBooks(SQLModel):
|
||||
"""Модель жанра с книгами"""
|
||||
|
||||
id: int
|
||||
name: str
|
||||
books: List[BookRead] = Field(default_factory=list)
|
||||
id: int = Field(description="Идентификатор")
|
||||
name: str = Field(description="Название")
|
||||
books: List[BookRead] = Field(default_factory=list, description="Список книг")
|
||||
|
||||
|
||||
class BookWithAuthors(SQLModel):
|
||||
"""Модель книги с авторами"""
|
||||
|
||||
id: int
|
||||
title: str
|
||||
description: str
|
||||
page_count: int
|
||||
authors: List[AuthorRead] = Field(default_factory=list)
|
||||
id: int = Field(description="Идентификатор")
|
||||
title: str = Field(description="Название")
|
||||
description: str = Field(description="Описание")
|
||||
page_count: int = Field(description="Количество страниц")
|
||||
status: BookStatus | None = Field(None, description="Статус")
|
||||
authors: List[AuthorRead] = Field(
|
||||
default_factory=list, description="Список авторов"
|
||||
)
|
||||
|
||||
|
||||
class BookWithGenres(SQLModel):
|
||||
"""Модель книги с жанрами"""
|
||||
|
||||
id: int
|
||||
title: str
|
||||
description: str
|
||||
page_count: int
|
||||
status: BookStatus | None = None
|
||||
genres: List[GenreRead] = Field(default_factory=list)
|
||||
id: int = Field(description="Идентификатор")
|
||||
title: str = Field(description="Название")
|
||||
description: str = Field(description="Описание")
|
||||
page_count: int = Field(description="Количество страниц")
|
||||
status: BookStatus | None = Field(None, description="Статус")
|
||||
genres: List[GenreRead] = Field(default_factory=list, description="Список жанров")
|
||||
|
||||
|
||||
class BookWithAuthorsAndGenres(SQLModel):
|
||||
"""Модель с авторами и жанрами"""
|
||||
|
||||
id: int
|
||||
title: str
|
||||
description: str
|
||||
page_count: int
|
||||
status: BookStatus | None = None
|
||||
authors: List[AuthorRead] = Field(default_factory=list)
|
||||
genres: List[GenreRead] = Field(default_factory=list)
|
||||
id: int = Field(description="Идентификатор")
|
||||
title: str = Field(description="Название")
|
||||
description: str = Field(description="Описание")
|
||||
page_count: int = Field(description="Количество страниц")
|
||||
status: BookStatus | None = Field(None, description="Статус")
|
||||
authors: List[AuthorRead] = Field(
|
||||
default_factory=list, description="Список авторов"
|
||||
)
|
||||
genres: List[GenreRead] = Field(default_factory=list, description="Список жанров")
|
||||
|
||||
|
||||
class BookFilteredList(SQLModel):
|
||||
"""Список книг с фильтрацией"""
|
||||
|
||||
books: List[BookWithAuthorsAndGenres]
|
||||
total: int
|
||||
books: List[BookWithAuthorsAndGenres] = Field(
|
||||
description="Список отфильтрованных книг"
|
||||
)
|
||||
total: int = Field(description="Количество книг")
|
||||
|
||||
|
||||
class LoanWithBook(LoanRead):
|
||||
"""Модель выдачи, включающая данные о книге"""
|
||||
|
||||
book: BookRead
|
||||
book: BookRead = Field(description="Книга")
|
||||
|
||||
|
||||
class BookStatusUpdate(SQLModel):
|
||||
"""Модель для ручного изменения статуса библиотекарем"""
|
||||
|
||||
status: str
|
||||
status: str = Field(description="Статус книги")
|
||||
|
||||
|
||||
class UserCreateByAdmin(UserCreate):
|
||||
"""Создание пользователя администратором"""
|
||||
|
||||
is_active: bool = True
|
||||
roles: list[str] | None = None
|
||||
is_active: bool = Field(True, description="Не является ли заблокированным")
|
||||
roles: list[str] | None = Field(None, description="Роли")
|
||||
|
||||
|
||||
class UserUpdateByAdmin(UserUpdate):
|
||||
"""Обновление пользователя администратором"""
|
||||
|
||||
is_active: bool | None = None
|
||||
roles: list[str] | None = None
|
||||
is_active: bool = Field(True, description="Не является ли заблокированным")
|
||||
roles: list[str] | None = Field(None, description="Роли")
|
||||
|
||||
|
||||
class LoginResponse(SQLModel):
|
||||
"""Модель для авторизации пользователя"""
|
||||
|
||||
access_token: str | None = None
|
||||
partial_token: str | None = None
|
||||
refresh_token: str | None = None
|
||||
token_type: str = "bearer"
|
||||
requires_2fa: bool = False
|
||||
access_token: str | None = Field(None, description="Токен доступа")
|
||||
partial_token: str | None = Field(None, description="Частичный токен")
|
||||
refresh_token: str | None = Field(None, description="Токен обновления")
|
||||
token_type: str = Field("bearer", description="Тип токена")
|
||||
requires_2fa: bool = Field(False, description="Требуется ли TOTP=код")
|
||||
|
||||
|
||||
class RegisterResponse(SQLModel):
|
||||
"""Модель для регистрации пользователя"""
|
||||
|
||||
user: UserRead
|
||||
recovery_codes: RecoveryCodesResponse
|
||||
user: UserRead = Field(description="Пользователь")
|
||||
recovery_codes: RecoveryCodesResponse = Field(description="Коды восстановления")
|
||||
|
||||
|
||||
class PasswordResetResponse(SQLModel):
|
||||
"""Модель для сброса пароля"""
|
||||
|
||||
total: int
|
||||
remaining: int
|
||||
used_codes: list[bool]
|
||||
generated_at: datetime | None
|
||||
should_regenerate: bool
|
||||
total: int = Field(description="Общее количество кодов")
|
||||
remaining: int = Field(description="Количество оставшихся кодов")
|
||||
used_codes: list[bool] = Field(description="Количество использованых кодов")
|
||||
generated_at: datetime | None = Field(description="Дата и время генерации")
|
||||
should_regenerate: bool = Field(description="Нужно ли пересоздать коды")
|
||||
|
||||
|
||||
class TOTPSetupResponse(SQLModel):
|
||||
"""Модель для генерации данных для настройки TOTP"""
|
||||
|
||||
secret: str
|
||||
username: str
|
||||
issuer: str
|
||||
size: int
|
||||
padding: int
|
||||
bitmap_b64: str
|
||||
secret: str = Field(description="Секрет TOTP")
|
||||
username: str = Field(description="Имя пользователя")
|
||||
issuer: str = Field(description="Запрашивающий сервис")
|
||||
size: int = Field(description="Размер кода")
|
||||
padding: int = Field(description="Отступ")
|
||||
bitmap_b64: str = Field(description="QR-код")
|
||||
|
||||
|
||||
class TOTPVerifyRequest(SQLModel):
|
||||
"""Модель для проверки TOTP кода"""
|
||||
|
||||
code: str = Field(min_length=6, max_length=6, regex=r"^\d{6}$")
|
||||
code: str = Field(
|
||||
min_length=6,
|
||||
max_length=6,
|
||||
regex=r"^\d{6}$",
|
||||
description="Шестизначный TOTP-код",
|
||||
)
|
||||
|
||||
|
||||
class TOTPDisableRequest(SQLModel):
|
||||
"""Модель для отключения TOTP 2FA"""
|
||||
|
||||
password: str
|
||||
password: str = Field(description="Пароль")
|
||||
|
||||
@@ -10,26 +10,28 @@ from sqlmodel import SQLModel, Field
|
||||
class RecoveryCodesResponse(SQLModel):
|
||||
"""Ответ при генерации резервных кодов"""
|
||||
|
||||
codes: list[str]
|
||||
generated_at: datetime
|
||||
codes: list[str] = Field(description="Список кодов восстановления")
|
||||
generated_at: datetime = Field(description="Дата и время генерации")
|
||||
|
||||
|
||||
class RecoveryCodesStatus(SQLModel):
|
||||
"""Статус резервных кодов пользователя"""
|
||||
|
||||
total: int
|
||||
remaining: int
|
||||
used_codes: list[bool]
|
||||
generated_at: datetime | None
|
||||
should_regenerate: bool
|
||||
total: int = Field(description="Общее количество кодов")
|
||||
remaining: int = Field(description="Количество оставшихся кодов")
|
||||
used_codes: list[bool] = Field(description="Количество использованых кодов")
|
||||
generated_at: datetime | None = Field(description="Дата и время генерации")
|
||||
should_regenerate: bool = Field(description="Нужно ли пересоздать коды")
|
||||
|
||||
|
||||
class RecoveryCodeUse(SQLModel):
|
||||
"""Запрос на сброс пароля через резервный код"""
|
||||
|
||||
username: str
|
||||
recovery_code: str = Field(min_length=19, max_length=19)
|
||||
new_password: str = Field(min_length=8, max_length=100)
|
||||
username: str = Field(description="Имя пользователя")
|
||||
recovery_code: str = Field(
|
||||
min_length=19, max_length=19, description="Код восстановления"
|
||||
)
|
||||
new_password: str = Field(min_length=8, max_length=100, description="Новый пароль")
|
||||
|
||||
@field_validator("recovery_code")
|
||||
@classmethod
|
||||
|
||||
@@ -1,32 +1,38 @@
|
||||
"""Модуль DTO-моделей ролей"""
|
||||
|
||||
from typing import List
|
||||
|
||||
from sqlmodel import SQLModel
|
||||
from sqlmodel import SQLModel, Field
|
||||
|
||||
|
||||
class RoleBase(SQLModel):
|
||||
"""Базовая модель роли"""
|
||||
name: str
|
||||
description: str | None = None
|
||||
payroll: int = 0
|
||||
|
||||
name: str = Field(description="Название")
|
||||
description: str | None = Field(None, description="Описание")
|
||||
payroll: int = Field(0, description="Оплата")
|
||||
|
||||
|
||||
class RoleCreate(RoleBase):
|
||||
"""Модель роли для создания"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class RoleUpdate(SQLModel):
|
||||
"""Модель роли для обновления"""
|
||||
name: str | None = None
|
||||
|
||||
name: str | None = Field(None, description="Название")
|
||||
|
||||
|
||||
class RoleRead(RoleBase):
|
||||
"""Модель роли для чтения"""
|
||||
id: int
|
||||
|
||||
id: int = Field(description="Идентификатор")
|
||||
|
||||
|
||||
class RoleList(SQLModel):
|
||||
"""Список ролей"""
|
||||
roles: List[RoleRead]
|
||||
total: int
|
||||
|
||||
roles: List[RoleRead] = Field(description="Список ролей")
|
||||
total: int = Field(description="Количество ролей")
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
"""Модуль DTO-моделей токенов"""
|
||||
|
||||
from sqlmodel import SQLModel
|
||||
from sqlmodel import SQLModel, Field
|
||||
|
||||
|
||||
class Token(SQLModel):
|
||||
"""Модель токена"""
|
||||
|
||||
access_token: str
|
||||
token_type: str = "bearer"
|
||||
refresh_token: str | None = None
|
||||
access_token: str = Field(description="Токен доступа")
|
||||
token_type: str = Field("bearer", description="Тип токена")
|
||||
refresh_token: str | None = Field(None, description="Токен обновления")
|
||||
|
||||
|
||||
class PartialToken(SQLModel):
|
||||
"""Частичный токен — для подтверждения 2FA"""
|
||||
|
||||
partial_token: str
|
||||
token_type: str = "partial"
|
||||
requires_2fa: bool = True
|
||||
partial_token: str = Field(description="Частичный токен")
|
||||
token_type: str = Field("partial", description="Тип токена")
|
||||
requires_2fa: bool = Field(True, description="Требуется TOTP-код")
|
||||
|
||||
|
||||
class TokenData(SQLModel):
|
||||
"""Модель содержимого токена"""
|
||||
|
||||
username: str | None = None
|
||||
user_id: int | None = None
|
||||
is_partial: bool = False
|
||||
username: str | None = Field(None, description="Имя пользователя")
|
||||
user_id: int | None = Field(None, description="Идентификатор пользователя")
|
||||
is_partial: bool = Field(False, description="Является ли токен частичным")
|
||||
|
||||
@@ -10,9 +10,17 @@ from sqlmodel import Field, SQLModel
|
||||
class UserBase(SQLModel):
|
||||
"""Базовая модель пользователя"""
|
||||
|
||||
username: str = Field(min_length=3, max_length=50, index=True, unique=True)
|
||||
email: EmailStr = Field(index=True, unique=True)
|
||||
full_name: str | None = Field(default=None, max_length=100)
|
||||
username: str = Field(
|
||||
min_length=3,
|
||||
max_length=50,
|
||||
index=True,
|
||||
unique=True,
|
||||
description="Имя пользователя",
|
||||
)
|
||||
email: EmailStr = Field(index=True, unique=True, description="Email")
|
||||
full_name: str | None = Field(
|
||||
default=None, max_length=100, description="Полное имя"
|
||||
)
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
@@ -28,7 +36,7 @@ class UserBase(SQLModel):
|
||||
class UserCreate(UserBase):
|
||||
"""Модель пользователя для создания"""
|
||||
|
||||
password: str = Field(min_length=8, max_length=100)
|
||||
password: str = Field(min_length=8, max_length=100, description="Пароль")
|
||||
|
||||
@field_validator("password")
|
||||
@classmethod
|
||||
@@ -46,30 +54,30 @@ class UserCreate(UserBase):
|
||||
class UserLogin(SQLModel):
|
||||
"""Модель аутентификации для пользователя"""
|
||||
|
||||
username: str
|
||||
password: str
|
||||
username: str = Field(description="Имя пользователя")
|
||||
password: str = Field(description="Пароль")
|
||||
|
||||
|
||||
class UserRead(UserBase):
|
||||
"""Модель пользователя для чтения"""
|
||||
|
||||
id: int
|
||||
is_active: bool
|
||||
is_verified: bool
|
||||
is_2fa_enabled: bool
|
||||
roles: List[str] = []
|
||||
is_active: bool = Field(description="Не является ли заблокированым")
|
||||
is_verified: bool = Field(description="Является ли верифицированым")
|
||||
is_2fa_enabled: bool = Field(description="Включен ли TOTP 2FA")
|
||||
roles: List[str] = Field([], description="Роли")
|
||||
|
||||
|
||||
class UserUpdate(SQLModel):
|
||||
"""Модель пользователя для обновления"""
|
||||
|
||||
email: EmailStr | None = None
|
||||
full_name: str | None = None
|
||||
password: str | None = None
|
||||
email: EmailStr | None = Field(None, description="Email")
|
||||
full_name: str | None = Field(None, description="Полное имя")
|
||||
password: str | None = Field(None, description="Пароль")
|
||||
|
||||
|
||||
class UserList(SQLModel):
|
||||
"""Список пользователей"""
|
||||
|
||||
users: List[UserRead]
|
||||
total: int
|
||||
users: List[UserRead] = Field(description="Список пользователей")
|
||||
total: int = Field(description="Количество пользователей")
|
||||
|
||||
Reference in New Issue
Block a user