mirror of
https://github.com/wowlikon/LiB.git
synced 2026-02-04 04:31:09 +00:00
Добавление авторизации и фронтэнда
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
from .dto import *
|
||||
"""Модуль моделей"""
|
||||
from .db import *
|
||||
from .dto import *
|
||||
|
||||
@@ -1,25 +1,22 @@
|
||||
"""Модуль моделей для базы данных"""
|
||||
from .author import Author
|
||||
from .book import Book
|
||||
from .genre import Genre
|
||||
from .role import Role
|
||||
from .user import User
|
||||
from .links import (
|
||||
AuthorBookLink,
|
||||
GenreBookLink,
|
||||
AuthorWithBooks,
|
||||
BookWithAuthors,
|
||||
GenreWithBooks,
|
||||
BookWithGenres,
|
||||
BookWithAuthorsAndGenres,
|
||||
UserRoleLink
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"Author",
|
||||
"Book",
|
||||
"Genre",
|
||||
"Role",
|
||||
"User",
|
||||
"AuthorBookLink",
|
||||
"AuthorWithBooks",
|
||||
"BookWithAuthors",
|
||||
"GenreBookLink",
|
||||
"GenreWithBooks",
|
||||
"BookWithGenres",
|
||||
"BookWithAuthorsAndGenres",
|
||||
"UserRoleLink",
|
||||
]
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
from typing import List, Optional, TYPE_CHECKING
|
||||
from sqlmodel import SQLModel, Field, Relationship
|
||||
from ..dto.author import AuthorBase
|
||||
from .links import AuthorBookLink
|
||||
"""Модуль DB-моделей авторов"""
|
||||
from typing import TYPE_CHECKING, List
|
||||
|
||||
from sqlmodel import Field, Relationship
|
||||
|
||||
from library_service.models.dto.author import AuthorBase
|
||||
from library_service.models.db.links import AuthorBookLink
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .book import Book
|
||||
|
||||
|
||||
class Author(AuthorBase, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True, index=True)
|
||||
"""Модель автора в базе данных"""
|
||||
id: int | None = Field(default=None, primary_key=True, index=True)
|
||||
books: List["Book"] = Relationship(
|
||||
back_populates="authors", link_model=AuthorBookLink
|
||||
)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
from typing import List, Optional, TYPE_CHECKING
|
||||
from sqlmodel import SQLModel, Field, Relationship
|
||||
from ..dto.book import BookBase
|
||||
from .links import AuthorBookLink, GenreBookLink
|
||||
"""Модуль DB-моделей книг"""
|
||||
from typing import TYPE_CHECKING, List
|
||||
|
||||
from sqlmodel import Field, Relationship
|
||||
|
||||
from library_service.models.dto.book import BookBase
|
||||
from library_service.models.db.links import AuthorBookLink, GenreBookLink
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .author import Author
|
||||
@@ -9,7 +12,8 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
class Book(BookBase, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True, index=True)
|
||||
"""Модель книги в базе данных"""
|
||||
id: int | None = Field(default=None, primary_key=True, index=True)
|
||||
authors: List["Author"] = Relationship(
|
||||
back_populates="books", link_model=AuthorBookLink
|
||||
)
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
from typing import List, Optional, TYPE_CHECKING
|
||||
from sqlmodel import SQLModel, Field, Relationship
|
||||
from ..dto.genre import GenreBase
|
||||
from .links import GenreBookLink
|
||||
"""Модуль DB-моделей жанров"""
|
||||
from typing import TYPE_CHECKING, List
|
||||
|
||||
from sqlmodel import Field, Relationship
|
||||
|
||||
from library_service.models.dto.genre import GenreBase
|
||||
from library_service.models.db.links import GenreBookLink
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .book import Book
|
||||
|
||||
|
||||
class Genre(GenreBase, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True, index=True)
|
||||
"""Модель жанра в базе данных"""
|
||||
id: int | None = Field(default=None, primary_key=True, index=True)
|
||||
books: List["Book"] = Relationship(
|
||||
back_populates="genres", link_model=GenreBookLink
|
||||
)
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
"""Модуль связей между сущностями в БД"""
|
||||
from sqlmodel import SQLModel, Field
|
||||
from typing import List
|
||||
|
||||
from library_service.models.dto.author import AuthorRead
|
||||
from library_service.models.dto.book import BookRead
|
||||
from library_service.models.dto.genre import GenreRead
|
||||
|
||||
|
||||
class AuthorBookLink(SQLModel, table=True):
|
||||
"""Модель связи автора и книги"""
|
||||
author_id: int | None = Field(
|
||||
default=None, foreign_key="author.id", primary_key=True
|
||||
)
|
||||
@@ -14,26 +11,14 @@ class AuthorBookLink(SQLModel, table=True):
|
||||
|
||||
|
||||
class GenreBookLink(SQLModel, table=True):
|
||||
"""Модель связи жанра и книги"""
|
||||
genre_id: int | None = Field(default=None, foreign_key="genre.id", primary_key=True)
|
||||
book_id: int | None = Field(default=None, foreign_key="book.id", primary_key=True)
|
||||
|
||||
|
||||
class AuthorWithBooks(AuthorRead):
|
||||
books: List[BookRead] = Field(default_factory=list)
|
||||
class UserRoleLink(SQLModel, table=True):
|
||||
"""Модель связи роли и пользователя"""
|
||||
__tablename__ = "user_roles"
|
||||
|
||||
|
||||
class BookWithAuthors(BookRead):
|
||||
authors: List[AuthorRead] = Field(default_factory=list)
|
||||
|
||||
|
||||
class BookWithGenres(BookRead):
|
||||
genres: List[GenreRead] = Field(default_factory=list)
|
||||
|
||||
|
||||
class GenreWithBooks(GenreRead):
|
||||
books: List[BookRead] = Field(default_factory=list)
|
||||
|
||||
|
||||
class BookWithAuthorsAndGenres(BookRead):
|
||||
authors: List[AuthorRead] = Field(default_factory=list)
|
||||
genres: List[GenreRead] = Field(default_factory=list)
|
||||
user_id: int | None = Field(default=None, foreign_key="users.id", primary_key=True)
|
||||
role_id: int | None = Field(default=None, foreign_key="roles.id", primary_key=True)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
"""Модуль DB-моделей ролей"""
|
||||
from typing import TYPE_CHECKING, List
|
||||
|
||||
from sqlmodel import Field, Relationship
|
||||
|
||||
from library_service.models.dto.role import RoleBase
|
||||
from library_service.models.db.links import UserRoleLink
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .user import User
|
||||
|
||||
|
||||
class Role(RoleBase, table=True):
|
||||
"""Модель роли в базе данных"""
|
||||
__tablename__ = "roles"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True, index=True)
|
||||
|
||||
# Связи
|
||||
users: List["User"] = Relationship(back_populates="roles", link_model=UserRoleLink)
|
||||
@@ -0,0 +1,28 @@
|
||||
"""Модуль DB-моделей пользователей"""
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, List
|
||||
|
||||
from sqlmodel import Field, Relationship
|
||||
|
||||
from library_service.models.dto.user import UserBase
|
||||
from library_service.models.db.links import UserRoleLink
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .role import Role
|
||||
|
||||
|
||||
class User(UserBase, table=True):
|
||||
"""Модель пользователя в базе данных"""
|
||||
__tablename__ = "users"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True, index=True)
|
||||
hashed_password: str = Field(nullable=False)
|
||||
is_active: bool = Field(default=True)
|
||||
is_verified: bool = Field(default=False)
|
||||
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
updated_at: datetime | None = Field(
|
||||
default=None, sa_column_kwargs={"onupdate": datetime.utcnow}
|
||||
)
|
||||
|
||||
# Связи
|
||||
roles: List["Role"] = Relationship(back_populates="users", link_model=UserRoleLink)
|
||||
@@ -1,7 +1,12 @@
|
||||
from .author import AuthorBase, AuthorCreate, AuthorUpdate, AuthorRead, AuthorList
|
||||
from .book import BookBase, BookCreate, BookUpdate, BookRead, BookList
|
||||
|
||||
from .genre import GenreBase, GenreCreate, GenreUpdate, GenreRead, GenreList
|
||||
"""Модуль DTO-моделей"""
|
||||
from .author import AuthorBase, AuthorCreate, AuthorList, AuthorRead, AuthorUpdate
|
||||
from .genre import GenreBase, GenreCreate, GenreList, GenreRead, GenreUpdate
|
||||
from .book import BookBase, BookCreate, BookList, BookRead, BookUpdate
|
||||
from .role import RoleBase, RoleCreate, RoleList, RoleRead, RoleUpdate
|
||||
from .user import UserBase, UserCreate, UserLogin, UserRead, UserUpdate
|
||||
from .token import Token, TokenData
|
||||
from .combined import (AuthorWithBooks, GenreWithBooks, BookWithAuthors, BookWithGenres,
|
||||
BookWithAuthorsAndGenres, BookFilteredList)
|
||||
|
||||
__all__ = [
|
||||
"AuthorBase",
|
||||
@@ -14,9 +19,22 @@ __all__ = [
|
||||
"BookUpdate",
|
||||
"BookRead",
|
||||
"BookList",
|
||||
"BookFilteredList",
|
||||
"GenreBase",
|
||||
"GenreCreate",
|
||||
"GenreUpdate",
|
||||
"GenreRead",
|
||||
"GenreList",
|
||||
"RoleBase",
|
||||
"RoleCreate",
|
||||
"RoleUpdate",
|
||||
"RoleRead",
|
||||
"RoleList",
|
||||
"Token",
|
||||
"TokenData",
|
||||
"UserBase",
|
||||
"UserCreate",
|
||||
"UserRead",
|
||||
"UserUpdate",
|
||||
"UserLogin",
|
||||
]
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
from sqlmodel import SQLModel
|
||||
"""Модуль DTO-моделей авторов"""
|
||||
from typing import List
|
||||
|
||||
from pydantic import ConfigDict
|
||||
from typing import Optional, List
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
|
||||
class AuthorBase(SQLModel):
|
||||
"""Базовая модель автора"""
|
||||
name: str
|
||||
|
||||
model_config = ConfigDict( # pyright: ignore
|
||||
@@ -12,17 +15,21 @@ class AuthorBase(SQLModel):
|
||||
|
||||
|
||||
class AuthorCreate(AuthorBase):
|
||||
"""Модель автора для создания"""
|
||||
pass
|
||||
|
||||
|
||||
class AuthorUpdate(SQLModel):
|
||||
name: Optional[str] = None
|
||||
"""Модель автора для обновления"""
|
||||
name: str | None = None
|
||||
|
||||
|
||||
class AuthorRead(AuthorBase):
|
||||
"""Модель автора для чтения"""
|
||||
id: int
|
||||
|
||||
|
||||
class AuthorList(SQLModel):
|
||||
"""Список авторов"""
|
||||
authors: List[AuthorRead]
|
||||
total: int
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
from sqlmodel import SQLModel
|
||||
"""Модуль DTO-моделей книг"""
|
||||
from typing import List, TYPE_CHECKING
|
||||
|
||||
from pydantic import ConfigDict
|
||||
from typing import Optional, List
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .combined import BookWithAuthorsAndGenres
|
||||
|
||||
|
||||
class BookBase(SQLModel):
|
||||
"""Базовая модель книги"""
|
||||
title: str
|
||||
description: str
|
||||
|
||||
@@ -15,18 +21,22 @@ class BookBase(SQLModel):
|
||||
|
||||
|
||||
class BookCreate(BookBase):
|
||||
"""Модель книги для создания"""
|
||||
pass
|
||||
|
||||
|
||||
class BookUpdate(SQLModel):
|
||||
title: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
"""Модель книги для обновления"""
|
||||
title: str | None = None
|
||||
description: str | None = None
|
||||
|
||||
|
||||
class BookRead(BookBase):
|
||||
"""Модель книги для чтения"""
|
||||
id: int
|
||||
|
||||
|
||||
class BookList(SQLModel):
|
||||
"""Список книг"""
|
||||
books: List[BookRead]
|
||||
total: int
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
"""Модуль объединёных объектов"""
|
||||
from typing import List
|
||||
from sqlmodel import SQLModel, Field
|
||||
|
||||
from .author import AuthorRead
|
||||
from .genre import GenreRead
|
||||
from .book import BookRead
|
||||
|
||||
|
||||
class AuthorWithBooks(SQLModel):
|
||||
"""Модель автора с книгами"""
|
||||
id: int
|
||||
name: str
|
||||
bio: str
|
||||
books: List[BookRead] = Field(default_factory=list)
|
||||
|
||||
|
||||
class GenreWithBooks(SQLModel):
|
||||
"""Модель жанра с книгами"""
|
||||
id: int
|
||||
name: str
|
||||
books: List[BookRead] = Field(default_factory=list)
|
||||
|
||||
|
||||
class BookWithAuthors(SQLModel):
|
||||
"""Модель книги с авторами"""
|
||||
id: int
|
||||
title: str
|
||||
description: str
|
||||
authors: List[AuthorRead] = Field(default_factory=list)
|
||||
|
||||
|
||||
class BookWithGenres(SQLModel):
|
||||
"""Модель книги с жанрами"""
|
||||
id: int
|
||||
title: str
|
||||
description: str
|
||||
genres: List[GenreRead] = Field(default_factory=list)
|
||||
|
||||
|
||||
class BookWithAuthorsAndGenres(SQLModel):
|
||||
"""Модель с авторами и жанрами"""
|
||||
id: int
|
||||
title: str
|
||||
description: str
|
||||
authors: List[AuthorRead] = Field(default_factory=list)
|
||||
genres: List[GenreRead] = Field(default_factory=list)
|
||||
|
||||
|
||||
class BookFilteredList(SQLModel):
|
||||
"""Список книг с фильтрацией"""
|
||||
books: List[BookWithAuthorsAndGenres]
|
||||
total: int
|
||||
@@ -1,9 +1,12 @@
|
||||
from sqlmodel import SQLModel
|
||||
"""Модуль DTO-моделей жанров"""
|
||||
from typing import List
|
||||
|
||||
from pydantic import ConfigDict
|
||||
from typing import Optional, List
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
|
||||
class GenreBase(SQLModel):
|
||||
"""Базовая модель жанра"""
|
||||
name: str
|
||||
|
||||
model_config = ConfigDict( # pyright: ignore
|
||||
@@ -12,17 +15,21 @@ class GenreBase(SQLModel):
|
||||
|
||||
|
||||
class GenreCreate(GenreBase):
|
||||
"""Модель жанра для создания"""
|
||||
pass
|
||||
|
||||
|
||||
class GenreUpdate(SQLModel):
|
||||
name: Optional[str] = None
|
||||
"""Модель жанра для обновления"""
|
||||
name: str | None = None
|
||||
|
||||
|
||||
class GenreRead(GenreBase):
|
||||
"""Модель жанра для чтения"""
|
||||
id: int
|
||||
|
||||
|
||||
class GenreList(SQLModel):
|
||||
"""Списко жанров"""
|
||||
genres: List[GenreRead]
|
||||
total: int
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
"""Модуль DTO-моделей ролей"""
|
||||
from typing import List
|
||||
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
|
||||
class RoleBase(SQLModel):
|
||||
"""Базовая модель роли"""
|
||||
name: str
|
||||
description: str | None = None
|
||||
|
||||
|
||||
class RoleCreate(RoleBase):
|
||||
"""Модель роли для создания"""
|
||||
pass
|
||||
|
||||
|
||||
class RoleUpdate(SQLModel):
|
||||
"""Модель роли для обновления"""
|
||||
name: str | None = None
|
||||
|
||||
|
||||
class RoleRead(RoleBase):
|
||||
"""Модель роли для чтения"""
|
||||
id: int
|
||||
|
||||
|
||||
class RoleList(SQLModel):
|
||||
"""Список ролей"""
|
||||
roles: List[RoleRead]
|
||||
total: int
|
||||
@@ -0,0 +1,15 @@
|
||||
"""Модуль DTO-моделей токенов"""
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
|
||||
class Token(SQLModel):
|
||||
"""Модель токена"""
|
||||
access_token: str
|
||||
token_type: str = "bearer"
|
||||
refresh_token: str | None = None
|
||||
|
||||
|
||||
class TokenData(SQLModel):
|
||||
"""Модель содержимого токена"""
|
||||
username: str | None = None
|
||||
user_id: int | None = None
|
||||
@@ -0,0 +1,61 @@
|
||||
"""Модуль DTO-моделей пользователей"""
|
||||
import re
|
||||
from typing import List
|
||||
|
||||
from pydantic import ConfigDict, EmailStr, field_validator
|
||||
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)
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"username": "johndoe",
|
||||
"email": "john@example.com",
|
||||
"full_name": "John Doe",
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class UserCreate(UserBase):
|
||||
"""Модель пользователя для создания"""
|
||||
password: str = Field(min_length=8, max_length=100)
|
||||
|
||||
@field_validator("password")
|
||||
@classmethod
|
||||
def validate_password(cls, v: str) -> str:
|
||||
"""Валидация пароля"""
|
||||
if not re.search(r"[A-Z]", v):
|
||||
raise ValueError("Пароль должен содержать символы в верхнем регистре")
|
||||
if not re.search(r"[a-z]", v):
|
||||
raise ValueError("Пароль должен содержать символы в нижнем регистре")
|
||||
if not re.search(r"\d", v):
|
||||
raise ValueError("пароль должен содержать цифры")
|
||||
return v
|
||||
|
||||
|
||||
class UserLogin(SQLModel):
|
||||
"""Модель аутентификации для пользователя"""
|
||||
username: str
|
||||
password: str
|
||||
|
||||
|
||||
class UserRead(UserBase):
|
||||
"""Модель пользователя для чтения"""
|
||||
id: int
|
||||
is_active: bool
|
||||
is_verified: bool
|
||||
roles: List[str] = []
|
||||
|
||||
|
||||
class UserUpdate(SQLModel):
|
||||
"""Модель пользователя для обновления"""
|
||||
email: EmailStr | None = None
|
||||
full_name: str | None = None
|
||||
password: str | None = None
|
||||
Reference in New Issue
Block a user