начало БД

This commit is contained in:
2025-11-29 14:51:45 +03:00
parent c48d1ee383
commit c59db5819f
23 changed files with 713 additions and 1 deletions
+115
View File
@@ -0,0 +1,115 @@
from sqlalchemy import delete, update
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
from sqlalchemy.exc import InvalidRequestError
from sqlalchemy.pool import NullPool
import config
from utils import loggerDB
DATABASE_URL = f"postgresql+asyncpg://{config.DB_USER}:{config.DB_PASS}@{config.DB_HOST}:{config.DB_PORT}/{config.DB_NAME}"
engine = create_async_engine(DATABASE_URL, poolclass=NullPool)
SessionLocal = async_sessionmaker(engine)
Base = declarative_base()
class CRUD:
async def create(db_data, refresh: bool = False):
try:
is_lst = isinstance(db_data, list)
async with SessionLocal() as db:
if is_lst:
loggerDB.info(f"Создаю {len(db_data)} записей")
try:
db.add_all(db_data)
except InvalidRequestError:
for data in db_data:
await db.merge(data)
else:
loggerDB.info("Создаю запись")
db.add(db_data)
await db.commit()
if refresh:
if is_lst:
loggerDB.info(f"Обновляю {len(db_data)} записей")
for data in db_data:
await db.refresh(data)
else:
loggerDB.info("Обновляю запись")
await db.refresh(db_data)
loggerDB.info("Запись создана")
return db_data if refresh else None
except Exception as e:
loggerDB.error(f"Ошибка создания: {str(e)}", exc_info=True)
return None
async def read(query, all: bool = False):
try:
async with SessionLocal() as db:
loggerDB.info(f"Чтение записей. Все: {all}")
results = await db.execute(query)
loggerDB.info(f"Чтение завершено")
return (
results.unique().scalars().all()
if all
else results.unique().scalars().first()
)
except Exception as e:
loggerDB.error(f"Ошибка чтения: {str(e)}", exc_info=True)
return None
async def delete(db_data) -> bool:
def itemInfo(instance):
from sqlalchemy import inspect
state = inspect(instance)
if state.identity is None:
pKey = None
pValue = None
else:
mapper = state.mapper
pKey = mapper.primary_key[0].name
pValue = getattr(instance, pKey)
return {"key": pKey, "value": pValue, "class": instance.__class__}
async def deleteFromDB(data, db):
itemData = itemInfo(data)
query = delete(itemData["class"]).where(
getattr(itemData["class"], itemData["key"]) == itemData["value"]
)
await db.execute(query)
async with SessionLocal() as db:
try:
if isinstance(db_data, list):
loggerDB.info(f"Удаляю записей: {len(db_data)}")
for data in db_data:
await deleteFromDB(data, db)
else:
loggerDB.info("Удаляю запись")
await deleteFromDB(db_data, db)
await db.commit()
loggerDB.info("Запись удалена")
return True
except Exception as e:
await db.rollback()
loggerDB.error(f"Ошибка удаления: {str(e)}", exc_info=True)
return False
async def update(db_data, id, **kwargs):
async with SessionLocal() as db:
try:
query = update(db_data).where(db_data.id == id).values(**kwargs)
item = await db.execute(query)
await db.commit()
loggerDB.info("Запись обновлена")
return item
except Exception as e:
await db.rollback()
loggerDB.error(f"Ошибка обновления: {str(e)}", exc_info=True)
return None
+26
View File
@@ -0,0 +1,26 @@
from sqlalchemy import select
from utils import logger
from db import CRUD
from db.schemas import AccessLevel
async def getAccessdata(accessId: int) -> dict:
query = select(AccessLevel).where(AccessLevel.id == accessId)
accessData = await CRUD.read(query)
if not accessData:
logger.error("Уровень доступа не найден")
return {}
return accessData.toDict()
async def editAccessData(accessId: int, **kwargs):
query = select(AccessLevel).where(AccessLevel.id == accessId)
accessData = await CRUD.read(query)
if not accessData:
logger.error("Уровень доступа не найден")
return {}
editedAccessData = await accessData.edit(**kwargs)
logger.info(
f"Уровень доступа {editedAccessData.title} успешно обновлен, изменены данные: {kwargs.keys()}"
)
return editedAccessData.toDict()
+19
View File
@@ -0,0 +1,19 @@
from utils import logger
from db import CRUD
from db.schemas import Toolbox
from sqlalchemy import select
async def addNewToolbox(toolboxData: dict):
title = toolboxData.get("title", None)
if not title:
logger.error("Не указано Назавание тулбокса")
return {}
query = select(Toolbox).where(Toolbox.title == title)
toolbox = await CRUD.read(query)
if toolbox:
logger.error("Тулбокс с таким названием уже существует")
return {}
newToolbox = await Toolbox(**toolboxData).save()
logger.info(f"Тулбокс {newToolbox.title} успешно создан")
return newToolbox.toDict()
+81
View File
@@ -0,0 +1,81 @@
from sqlalchemy import or_, select
from db import CRUD
from db.handlers.toolbox import addNewToolbox
from db.schemas import User
from utils import logger, pwd_hash
async def addNewUser(userData: dict) -> dict:
login = userData.get("login", None)
if not login:
logger.error("Не указан логин")
return {}
userName = userData.get("username")
if not userName:
logger.error("Не указано имя пользователя")
return {}
query = select(User).where(or_(User.login == login, User.username == userName))
user = await CRUD.read(query)
if user:
logger.error("Пользователь с таким логином или именем уже существует")
return {}
if "access_level_id" not in userData:
logger.error("Не указан уровень доступа")
return {}
if "password" not in userData:
logger.error("Не указан пароль")
return {}
userData["hashed_password"] = pwd_hash(userData.pop("password"))
newUser = await User(**userData).save()
if not newUser:
logger.error("Ошибка сохранения пользователя")
return {}
logger.info(f"Пользователь {newUser.username} успешно добавлен, id: {newUser.id}")
if newUser.available_own_toolbox:
newToolboxData = {
"title": f"Тулбокс {newUser.username}",
"description": f"Оборудование, полученное сотрудником {newUser.username}, под личную материальную ответственность",
"owner_id": newUser.id,
}
newToolbox = await addNewToolbox(newToolboxData)
logger.info(
f"Тулбокс {newToolbox['title']} успешно создан и закреплен за пользователем {newUser.username}"
)
return newUser.toDict()
async def editUser(userData: dict) -> dict:
id = userData.get("id", None)
if not id:
logger.error("Не указан id пользователя")
return {}
query = select(User).where(User.id == id)
user = await CRUD.read(query)
if not user:
logger.error("Пользователь с таким id не найден")
return {}
changedUserData = userData.get("changedUserData", {})
if len(changedUserData.keys()) == 0:
logger.error("Не указаны изменяемые данные")
return {}
if "password" in changedUserData:
userData["hashed_password"] = pwd_hash(changedUserData.pop("password"))
editedUser = await user.edit(**changedUserData)
if not editedUser:
logger.error("Ошибка обновления пользователя")
return {}
logger.info(
f"Пользователь {editedUser.username} успешно обновлен, изменены данные: {changedUserData.keys()}"
)
if not user.available_own_toolbox:
if editedUser.available_own_toolbox:
newToolboxData = {
"title": f"Тулбокс {editedUser.username}",
"description": f"Оборудование, полученное сотрудником {editedUser.username}, под личную материальную ответственность",
"owner_id": editedUser.id,
}
newToolbox = await addNewToolbox(newToolboxData)
logger.info(
f"Тулбокс {newToolbox['title']} успешно создан и закреплен за пользователем {editedUser.username}"
)
return editedUser.toDict()
+6
View File
@@ -0,0 +1,6 @@
from user import *
from access import *
from toolkit import *
from categories import *
from db.schemas.toolbox import *
from stock import *
+53
View File
@@ -0,0 +1,53 @@
from datetime import datetime
from sqlalchemy import Boolean, Column, DateTime, Integer, String, Text
from db import Base
import utils
class AccessLevel(Base):
__tablename__ = "access_level"
id = Column(Integer, primary_key=True, index=True)
title = Column(String, unique=True, index=True)
description = Column(Text)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
# permissions
receiving_edit = Column(Boolean, default=False)
refund_request_edit = Column(Boolean, default=False)
refund_request_confirm = Column(Boolean, default=False)
debit_request_edit = Column(Boolean, default=False)
debit_request_confirm = Column(Boolean, default=False)
tools_creation = Column(Boolean, default=False)
tools_registration = Column(Boolean, default=False)
tools_registration_edit = Column(Boolean, default=False)
tools_edit = Column(Boolean, default=False)
tools_achievement = Column(Boolean, default=False)
users_creation = Column(Boolean, default=False)
users_edit = Column(Boolean, default=False)
users_disabling = Column(Boolean, default=False)
users_view = Column(Boolean, default=False)
available_own_toolbox = Column(Boolean, default=False)
view_all_toolboxes = Column(Boolean, default=False)
view_requests = Column(Boolean, default=False)
access_level_view = Column(Boolean, default=False)
access_level_edit = Column(Boolean, default=False)
manage_toolboxes = Column(Boolean, default=False)
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
def toDict(self):
return utils.toDict(self)
async def save(self):
from db import CRUD
return await CRUD.create(self, refresh=True)
async def edit(self, **kwargs):
from db import CRUD
return await CRUD.update(AccessLevel, self.id, **kwargs)
+31
View File
@@ -0,0 +1,31 @@
from datetime import datetime
from sqlalchemy import Column, DateTime, Integer, String, Text
from db import Base
import utils
class Category(Base):
__tablename__ = "categories"
id = Column(Integer, primary_key=True, index=True)
title = Column(String, unique=True, index=True)
description = Column(Text)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
def toDict(self):
return utils.toDict(self)
async def save(self):
from db import CRUD
return await CRUD.create(self, refresh=True)
async def edit(id: int, **kwargs):
from db import CRUD
return await CRUD.update(Category, id, **kwargs)
+45
View File
@@ -0,0 +1,45 @@
from datetime import datetime
from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer
from sqlalchemy.orm import relationship
from db import Base
import utils
class Stock(Base):
__tablename__ = "stocks"
id = Column(Integer, primary_key=True, index=True)
toolkit_id = Column(
Integer, ForeignKey("toolkits.id", ondelete="CASCADE"), nullable=False
)
toolkit_data = relationship(
"Toolkit", cascade="all, delete-orphan", lazy="joined", uselist=False
)
toolbox_id = Column(
Integer, ForeignKey("toolboxes.id", ondelete="CASCADE"), nullable=False
)
toolbox_data = relationship(
"Toolbox", cascade="all, delete-orphan", lazy="joined", uselist=False
)
quantity = Column(Integer, nullable=False)
price = Column(Float, nullable=False)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
def toDict(self):
return utils.toDict(self)
async def save(self):
from db import CRUD
return await CRUD.create(self, refresh=True)
async def edit(self, **kwargs):
from db import CRUD
return await CRUD.update(Stock, self.id, **kwargs)
+34
View File
@@ -0,0 +1,34 @@
from datetime import datetime
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, Text
from db import Base
import utils
class Toolbox(Base):
__tablename__ = "toolboxes"
id = Column(Integer, primary_key=True, index=True)
title = Column(String, unique=True, index=True)
description = Column(Text)
owner_id = Column(
Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True
)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
def toDict(self):
return utils.toDict(self)
async def save(self):
from db import CRUD
return await CRUD.create(self, refresh=True)
async def edit(id: int, **kwargs):
from db import CRUD
return await CRUD.update(Toolbox, id, **kwargs)
+45
View File
@@ -0,0 +1,45 @@
from datetime import datetime
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, Text
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import relationship
from db import Base
import utils
class Toolkit(Base):
__tablename__ = "toolkits"
id = Column(Integer, primary_key=True, index=True)
title = Column(String, unique=True, index=True)
description = Column(Text)
category_id = Column(Integer, ForeignKey("categories.id", ondelete="CASCADE"))
category_data = relationship(
"Category", cascade="all, delete-orphan", lazy="joined", uselist=False
)
image = Column(
JSONB, default={"main": "images/tools/default.png", "additional": []}
)
quantity_min = Column(Integer, nullable=True)
quantity_min_extra = Column(Integer, nullable=True)
external_link = Column(String, nullable=True)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
refilled_at = Column(DateTime, default=datetime.now)
moved_at = Column(DateTime, default=datetime.now)
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
def toDict(self):
return utils.toDict(self)
async def save(self):
from db import CRUD
return await CRUD.create(self, refresh=True)
async def edit(id: int, **kwargs):
from db import CRUD
return await CRUD.update(Toolkit, id, **kwargs)
+35
View File
@@ -0,0 +1,35 @@
from datetime import datetime
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String
from db import Base
import utils
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
login = Column(String, unique=True, index=True)
username = Column(String, unique=True, index=True)
photo = Column(String, default="images/users/default.png")
hashed_password = Column(String)
access_level_id = Column(Integer, ForeignKey("access_level.id", ondelete="CASCADE"))
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
def toDict(self):
return utils.toDict(self)
async def save(self) -> "User":
from db import CRUD
return await CRUD.create(self, refresh=True)
async def edit(self, **kwargs) -> "User":
from db import CRUD
return await CRUD.update(User, self.id, **kwargs)