Переделал модели бд

This commit is contained in:
urec56 2024-06-01 21:16:46 +05:00
parent 9af53e4982
commit ccd1e209f1
14 changed files with 195 additions and 206 deletions

View file

@ -12,7 +12,7 @@ file_template = %%(year)d-%%(month).2d-%%(day).2d_%%(slug)s
# sys.path path, will be prepended to sys.path if present.
# defaults to the current working directory.
prepend_sys_path = .
prepend_sys_path = . app
# timezone to use when rendering the date within the migration file
# as well as the filename.

View file

@ -5,7 +5,12 @@ from app.database import async_session_maker, engine # noqa
from app.exceptions import UserAlreadyInChatException, UserAlreadyPinnedChatException
from app.chat.shemas import SMessage
from app.models.users import Users
from app.models.chat import Chats, Messages, UsersXChats, PinnedChats, PinnedMessages, Answers
from app.models.answer import Answer
from app.models.chat import Chats
from app.models.message import Message
from app.models.pinned_chat import PinnedChats
from app.models.pinned_message import PinnedMessages
from app.models.user_chat import UserChat
class ChatDAO(BaseDAO):
@ -22,13 +27,13 @@ class ChatDAO(BaseDAO):
@staticmethod
async def add_user_to_chat(user_id: int, chat_id: int) -> bool:
query = select(UsersXChats.user_id).where(UsersXChats.chat_id == chat_id)
query = select(UserChat.user_id).where(UserChat.chat_id == chat_id)
async with async_session_maker() as session:
result = await session.execute(query)
result = result.scalars().all()
if user_id in result:
raise UserAlreadyInChatException
query = insert(UsersXChats).values(user_id=user_id, chat_id=chat_id)
query = insert(UserChat).values(user_id=user_id, chat_id=chat_id)
await session.execute(query)
await session.commit()
return True
@ -36,10 +41,10 @@ class ChatDAO(BaseDAO):
@staticmethod
async def send_message(user_id: int, chat_id: int, message: str, image_url: str | None = None) -> SMessage:
inserted_image = (
insert(Messages)
insert(Message)
.values(chat_id=chat_id, user_id=user_id, message=message, image_url=image_url)
.returning(
Messages.id, Messages.message, Messages.image_url, Messages.chat_id, Messages.user_id, Messages.created_at
Message.id, Message.message, Message.image_url, Message.chat_id, Message.user_id, Message.created_at
)
.cte("inserted_image")
)
@ -55,12 +60,12 @@ class ChatDAO(BaseDAO):
Users.avatar_image,
Users.username,
Users.avatar_hex,
Answers.self_id,
Answers.answer_id,
Answer.self_id,
Answer.answer_id,
)
.select_from(inserted_image)
.join(Users, Users.id == inserted_image.c.user_id)
.join(Answers, Answers.self_id == inserted_image.c.id, isouter=True)
.join(Answer, Answer.self_id == inserted_image.c.id, isouter=True)
)
async with async_session_maker() as session:
@ -73,22 +78,22 @@ class ChatDAO(BaseDAO):
async def get_message_by_id(message_id: int):
query = (
select(
Messages.id,
Messages.message,
Messages.image_url,
Messages.chat_id,
Messages.user_id,
Messages.created_at,
Message.id,
Message.message,
Message.image_url,
Message.chat_id,
Message.user_id,
Message.created_at,
Users.avatar_image,
Users.username,
Users.avatar_hex,
Answers.self_id,
Answers.answer_id,
Answer.self_id,
Answer.answer_id,
)
.select_from(Messages)
.join(Users, Users.id == Messages.user_id)
.join(Answers, Answers.self_id == Messages.id, isouter=True)
.where(Messages.id == message_id, Messages.visibility == True) #
.select_from(Message)
.join(Users, Users.id == Message.user_id)
.join(Answer, Answer.self_id == Message.id, isouter=True)
.where(Message.id == message_id, Message.visibility == True) #
)
async with async_session_maker() as session:
result = await session.execute(query)
@ -98,7 +103,7 @@ class ChatDAO(BaseDAO):
@staticmethod
async def delete_message(message_id: int) -> bool:
query = update(Messages).where(Messages.id == message_id).values(visibility=False)
query = update(Message).where(Message.id == message_id).values(visibility=False)
async with async_session_maker() as session:
await session.execute(query)
await session.commit()
@ -120,10 +125,10 @@ class ChatDAO(BaseDAO):
LIMIT 15 OFFSET 0;
"""
messages_with_users = (
select(Messages.__table__.columns, Users.__table__.columns, Answers.__table__.columns)
.select_from(Messages)
.join(Users, Messages.user_id == Users.id)
.join(Answers, Answers.self_id == Messages.id, isouter=True)
select(Message.__table__.columns, Users.__table__.columns, Answer.__table__.columns)
.select_from(Message)
.join(Users, Message.user_id == Users.id)
.join(Answer, Answer.self_id == Message.id, isouter=True)
.cte("messages_with_users")
)
@ -155,7 +160,7 @@ class ChatDAO(BaseDAO):
@staticmethod
async def edit_message(message_id: int, new_message: str, new_image_url: str) -> bool:
query = update(Messages).where(Messages.id == message_id).values(message=new_message, image_url=new_image_url)
query = update(Message).where(Message.id == message_id).values(message=new_message, image_url=new_image_url)
async with async_session_maker() as session:
await session.execute(query)
await session.commit()
@ -164,30 +169,30 @@ class ChatDAO(BaseDAO):
@staticmethod
async def add_answer(self_id: int, answer_id: int) -> SMessage:
answer = (
insert(Answers)
insert(Answer)
.values(self_id=self_id, answer_id=answer_id)
.returning(Answers.self_id, Answers.answer_id)
.returning(Answer.self_id, Answer.answer_id)
.cte("answer")
)
query = (
select(
Messages.id,
Messages.message,
Messages.image_url,
Messages.chat_id,
Messages.user_id,
Messages.created_at,
Message.id,
Message.message,
Message.image_url,
Message.chat_id,
Message.user_id,
Message.created_at,
Users.avatar_image,
Users.username,
Users.avatar_hex,
answer.c.self_id,
answer.c.answer_id,
)
.select_from(Messages)
.join(Users, Users.id == Messages.user_id)
.join(answer, answer.c.self_id == Messages.id, isouter=True)
.where(Messages.id == self_id)
.select_from(Message)
.join(Users, Users.id == Message.user_id)
.join(answer, answer.c.self_id == Message.id, isouter=True)
.where(Message.id == self_id)
)
async with async_session_maker() as session:
@ -206,7 +211,7 @@ class ChatDAO(BaseDAO):
@staticmethod
async def delete_user(chat_id: int, user_id: int) -> bool:
query = delete(UsersXChats).where(UsersXChats.chat_id == chat_id, UsersXChats.user_id == user_id)
query = delete(UserChat).where(UserChat.chat_id == chat_id, UserChat.user_id == user_id)
async with async_session_maker() as session:
await session.execute(query)
await session.commit()
@ -236,9 +241,9 @@ class ChatDAO(BaseDAO):
@staticmethod
async def get_pinned_chats(user_id: int):
chats_with_descriptions = (
select(UsersXChats.__table__.columns, Chats.__table__.columns)
.select_from(UsersXChats)
.join(Chats, UsersXChats.chat_id == Chats.id)
select(UserChat.__table__.columns, Chats.__table__.columns)
.select_from(UserChat)
.join(Chats, UserChat.chat_id == Chats.id)
.cte("chats_with_descriptions")
)
@ -306,24 +311,24 @@ class ChatDAO(BaseDAO):
async def get_pinned_messages(chat_id: int) -> list[dict]:
query = (
select(
Messages.id,
Messages.message,
Messages.image_url,
Messages.chat_id,
Messages.user_id,
Messages.created_at,
Message.id,
Message.message,
Message.image_url,
Message.chat_id,
Message.user_id,
Message.created_at,
Users.avatar_image,
Users.username,
Users.avatar_hex,
Answers.self_id,
Answers.answer_id,
Answer.self_id,
Answer.answer_id,
)
.select_from(PinnedMessages)
.join(Messages, PinnedMessages.message_id == Messages.id, isouter=True)
.join(Message, PinnedMessages.message_id == Message.id, isouter=True)
.join(Users, PinnedMessages.user_id == Users.id, isouter=True)
.join(Answers, Answers.self_id == Messages.id, isouter=True)
.where(PinnedMessages.chat_id == chat_id, Messages.visibility == True)
.order_by(Messages.created_at.desc())
.join(Answer, Answer.self_id == Message.id, isouter=True)
.where(PinnedMessages.chat_id == chat_id, Message.visibility == True)
.order_by(Message.created_at.desc())
)
async with async_session_maker() as session:
result = await session.execute(query)

View file

@ -1,15 +1,19 @@
import sys
from os.path import abspath, dirname
from logging.config import fileConfig
from sqlalchemy import engine_from_config, pool
from alembic import context
sys.path.insert(0, dirname(dirname(abspath(__file__))))
from app.database import DATABASE_URL, Base
from app.models.chat import Users, UsersVerificationCodes # noqa
from app.models.chat import Chats, Messages, UsersXChats, PinnedMessages, PinnedChats # noqa
from app.models.users import Users # noqa
from app.models.answer import Answer # noqa
from app.models.chat import Chats # noqa
from app.models.message import Message # noqa
from app.models.pinned_chat import PinnedChats # noqa
from app.models.pinned_message import PinnedMessages # noqa
from app.models.user_chat import UserChat # noqa
from app.models.user_avatar import UserAvatar # noqa
from app.models.user_verification_code import UserVerificationCode # noqa
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.

11
app/models/answer.py Normal file
View file

@ -0,0 +1,11 @@
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column
from app.database import Base
class Answer(Base):
__tablename__ = "answers"
self_id: Mapped[int] = mapped_column(ForeignKey("messages.id"), primary_key=True)
answer_id: Mapped[int] = mapped_column(ForeignKey("messages.id"))

View file

@ -1,7 +1,5 @@
from datetime import datetime
from sqlalchemy import func, ForeignKey, DateTime
from sqlalchemy.orm import mapped_column, Mapped, relationship
from sqlalchemy import ForeignKey
from sqlalchemy.orm import mapped_column, Mapped
from app.database import Base
@ -14,82 +12,3 @@ class Chats(Base):
chat_for = mapped_column(ForeignKey("users.id"))
chat_name: Mapped[str]
visibility: Mapped[bool] = mapped_column(server_default="true")
message = relationship("Messages", back_populates="chat")
usersxchats = relationship("UsersXChats", back_populates="chat")
user_to_exclude = relationship("Users", primaryjoin="Chats.chat_for == Users.id", back_populates="chat")
user_who_create = relationship("Users", primaryjoin="Chats.created_by == Users.id", back_populates="creator")
pinned_messages = relationship("PinnedMessages", back_populates="chat")
pinned_chats = relationship("PinnedChats", back_populates="chat")
def __str__(self):
return f"Чат #{self.id}. Виден: {self.visibility}. Сделан {self.created_by} для {self.chat_for}"
class Messages(Base):
__tablename__ = "messages"
id: Mapped[int] = mapped_column(primary_key=True)
chat_id = mapped_column(ForeignKey("chats.id"))
user_id = mapped_column(ForeignKey("users.id"))
message: Mapped[str | None]
image_url: Mapped[str | None]
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
visibility: Mapped[bool] = mapped_column(server_default="true")
chat = relationship("Chats", back_populates="message")
user = relationship("Users", back_populates="message")
pinned_messages = relationship("PinnedMessages", back_populates="message")
self_id = relationship("Answers", primaryjoin="Messages.id == Answers.self_id", back_populates="self_message")
answer_id = relationship("Answers", primaryjoin="Messages.id == Answers.answer_id", back_populates="answer_message")
def __str__(self):
return f"#{self.id} {self.message} от {self.user_id}. Написано {self.created_at}"
class Answers(Base):
__tablename__ = "answers"
self_id: Mapped[int] = mapped_column(ForeignKey("messages.id"), primary_key=True)
answer_id: Mapped[int] = mapped_column(ForeignKey("messages.id"))
self_message = relationship("Messages", primaryjoin="Answers.self_id == Messages.id", back_populates="self_id")
answer_message = relationship("Messages", primaryjoin="Answers.answer_id == Messages.id", back_populates="answer_id")
class UsersXChats(Base):
__tablename__ = "usersxchats"
id: Mapped[int] = mapped_column(primary_key=True)
user_id = mapped_column(ForeignKey("users.id"))
chat_id = mapped_column(ForeignKey("chats.id"))
chat = relationship("Chats", back_populates="usersxchats")
user = relationship("Users", back_populates="usersxchats")
def __str__(self):
return f"Юзер #{self.user_id} допущен к чату {self.chat_id}"
class PinnedMessages(Base):
__tablename__ = "pinnedmessages"
id: Mapped[int] = mapped_column(primary_key=True)
chat_id = mapped_column(ForeignKey("chats.id"))
message_id = mapped_column(ForeignKey("messages.id"))
user_id = mapped_column(ForeignKey("users.id"))
chat = relationship("Chats", back_populates="pinned_messages")
message = relationship("Messages", back_populates="pinned_messages")
user = relationship("Users", back_populates="pinned_messages")
class PinnedChats(Base):
__tablename__ = "pinnedchats"
id: Mapped[int] = mapped_column(primary_key=True)
user_id = mapped_column(ForeignKey("users.id"))
chat_id = mapped_column(ForeignKey("chats.id"))
chat = relationship("Chats", back_populates="pinned_chats")
user = relationship("Users", back_populates="pinned_chats")

18
app/models/message.py Normal file
View file

@ -0,0 +1,18 @@
from datetime import datetime
from sqlalchemy import ForeignKey, func, DateTime
from sqlalchemy.orm import Mapped, mapped_column
from app.database import Base
class Message(Base):
__tablename__ = "messages"
id: Mapped[int] = mapped_column(primary_key=True)
chat_id = mapped_column(ForeignKey("chats.id"))
user_id = mapped_column(ForeignKey("users.id"))
message: Mapped[str | None]
image_url: Mapped[str | None]
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
visibility: Mapped[bool] = mapped_column(server_default="true")

12
app/models/pinned_chat.py Normal file
View file

@ -0,0 +1,12 @@
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column
from app.database import Base
class PinnedChats(Base):
__tablename__ = "pinnedchats"
id: Mapped[int] = mapped_column(primary_key=True)
user_id = mapped_column(ForeignKey("users.id"))
chat_id = mapped_column(ForeignKey("chats.id"))

View file

@ -0,0 +1,13 @@
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column
from app.database import Base
class PinnedMessages(Base):
__tablename__ = "pinnedmessages"
id: Mapped[int] = mapped_column(primary_key=True)
chat_id = mapped_column(ForeignKey("chats.id"))
message_id = mapped_column(ForeignKey("messages.id"))
user_id = mapped_column(ForeignKey("users.id"))

12
app/models/user_avatar.py Normal file
View file

@ -0,0 +1,12 @@
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column
from app.database import Base
class UserAvatar(Base):
__tablename__ = "usersavatars"
id: Mapped[int] = mapped_column(primary_key=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
avatar_image: Mapped[str]

12
app/models/user_chat.py Normal file
View file

@ -0,0 +1,12 @@
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column
from app.database import Base
class UserChat(Base):
__tablename__ = "usersxchats"
id: Mapped[int] = mapped_column(primary_key=True)
user_id = mapped_column(ForeignKey("users.id"))
chat_id = mapped_column(ForeignKey("chats.id"))

View file

@ -0,0 +1,16 @@
from datetime import datetime
from sqlalchemy import func, ForeignKey, DateTime
from sqlalchemy.orm import Mapped, mapped_column
from app.database import Base
class UserVerificationCode(Base):
__tablename__ = "usersverificationcodes"
id: Mapped[int] = mapped_column(primary_key=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
code: Mapped[str]
description: Mapped[str]
date_of_creation: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())

View file

@ -1,7 +1,7 @@
from datetime import date, datetime
from datetime import date
from sqlalchemy import func, ForeignKey, DateTime
from sqlalchemy.orm import mapped_column, Mapped, relationship
from sqlalchemy import func
from sqlalchemy.orm import mapped_column, Mapped
from app.database import Base
@ -10,51 +10,12 @@ class Users(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
email: Mapped[str]
username: Mapped[str]
email: Mapped[str] = mapped_column(unique=True)
username: Mapped[str] = mapped_column(unique=True)
hashed_password: Mapped[str]
role: Mapped[int] = mapped_column(server_default="0")
black_phoenix: Mapped[bool] = mapped_column(server_default="false")
avatar_image: Mapped[str] = mapped_column(
server_default="https://images.black-phoenix.ru/static/images/%D1%82%D1%8B%20%D1%83%D0%B6%D0%B5%20%D0%BF%D0%B5%D1%88%D0%BA%D0%B0%20BP.png" # noqa: E501
)
avatar_image: Mapped[str] = mapped_column(server_default="https://images.black-phoenix.ru/static/images/%D1%82%D1%8B%20%D1%83%D0%B6%D0%B5%20%D0%BF%D0%B5%D1%88%D0%BA%D0%B0%20BP.png") # noqa: E501
avatar_hex: Mapped[str] = mapped_column(server_default="#30293f")
date_of_birth: Mapped[date]
date_of_registration: Mapped[date] = mapped_column(server_default=func.now())
message = relationship("Messages", back_populates="user")
usersxchats = relationship("UsersXChats", back_populates="user")
chat = relationship("Chats", primaryjoin="Chats.chat_for == Users.id", back_populates="user_to_exclude")
creator = relationship("Chats", primaryjoin="Chats.created_by == Users.id", back_populates="user_who_create")
verificationcode = relationship("UsersVerificationCodes", back_populates="user")
pinned_messages = relationship("PinnedMessages", back_populates="user")
pinned_chats = relationship("PinnedChats", back_populates="user")
avatars = relationship("UsersAvatars", back_populates="user")
def __str__(self):
return f"Юзер {self.username}"
class UsersVerificationCodes(Base):
__tablename__ = "usersverificationcodes"
id: Mapped[int] = mapped_column(primary_key=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
code: Mapped[str]
description: Mapped[str]
date_of_creation: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
user = relationship("Users", back_populates="verificationcode")
def __str__(self):
return f"Код {self.code} для юзера {self.user_id}. {self.description}"
class UsersAvatars(Base):
__tablename__ = "usersavatars"
id: Mapped[int] = mapped_column(primary_key=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
avatar_image: Mapped[str]
user = relationship("Users", back_populates="avatars")

View file

@ -7,8 +7,11 @@ from httpx import AsyncClient
from app.config import settings
from app.database import Base, async_session_maker, engine
from app.models.chat import Users, UsersVerificationCodes
from app.models.chat import Chats, UsersXChats, Messages
from app.models.users import Users
from app.models.user_verification_code import UserVerificationCode
from app.models.chat import Chats
from app.models.message import Message
from app.models.user_chat import UserChat
from app.main import app as fastapi_app
@ -38,10 +41,10 @@ async def prepare_database():
async with async_session_maker() as session:
add_users = insert(Users).values(new_users)
add_users_verification_codes = insert(UsersVerificationCodes).values(users_verification_codes)
add_users_verification_codes = insert(UserVerificationCode).values(users_verification_codes)
add_chats = insert(Chats).values(chats)
add_users_x_chats = insert(UsersXChats).values(users_x_chats)
add_messages = insert(Messages).values(messages)
add_users_x_chats = insert(UserChat).values(users_x_chats)
add_messages = insert(Message).values(messages)
set_verified_user = update(Users).values(role=1).where(Users.id == 3)
await session.execute(add_users)

View file

@ -2,8 +2,11 @@ from sqlalchemy import update, select, insert, and_, func, text, delete
from app.dao.base import BaseDAO
from app.database import async_session_maker, engine # noqa
from app.models.chat import UsersXChats, Chats
from app.models.users import Users, UsersVerificationCodes, UsersAvatars
from app.models.chat import Chats
from app.models.user_avatar import UserAvatar
from app.models.users import Users
from app.models.user_verification_code import UserVerificationCode
from app.models.user_chat import UserChat
from app.users.schemas import SUser, SUserAvatars
@ -63,9 +66,9 @@ class UserDAO(BaseDAO):
WHERE user_id = 1
"""
chats_with_descriptions = (
select(UsersXChats.__table__.columns, Chats.__table__.columns)
.select_from(UsersXChats)
.join(Chats, UsersXChats.chat_id == Chats.id)
select(UserChat.__table__.columns, Chats.__table__.columns)
.select_from(UserChat)
.join(Chats, UserChat.chat_id == Chats.id)
).cte("chats_with_descriptions")
chats_with_avatars = (
@ -108,7 +111,7 @@ class UserDAO(BaseDAO):
@staticmethod
async def add_user_avatar(user_id: int, avatar: str) -> bool:
query = insert(UsersAvatars).values(user_id=user_id, avatar_image=avatar)
query = insert(UserAvatar).values(user_id=user_id, avatar_image=avatar)
async with async_session_maker() as session:
await session.execute(query)
await session.commit()
@ -121,11 +124,11 @@ class UserDAO(BaseDAO):
"user_avatars", select(
func.json_agg(
func.json_build_object(
"avatar_url", UsersAvatars.avatar_image
"avatar_url", UserAvatar.avatar_image
)
)
)
.where(UsersAvatars.user_id == user_id)
.where(UserAvatar.user_id == user_id)
.scalar_subquery()
)
)
@ -136,7 +139,7 @@ class UserDAO(BaseDAO):
@staticmethod
async def delete_user_avatar(avatar_id: int, user_id: int) -> bool:
query = delete(UsersAvatars).where(and_(UsersAvatars.id == avatar_id, UsersAvatars.user_id == user_id))
query = delete(UserAvatar).where(and_(UserAvatar.id == avatar_id, UserAvatar.user_id == user_id))
async with async_session_maker() as session:
await session.execute(query)
await session.commit()
@ -144,12 +147,12 @@ class UserDAO(BaseDAO):
class UserCodesDAO(BaseDAO):
model = UsersVerificationCodes
model = UserVerificationCode
@classmethod
async def set_user_codes(cls, user_id: int, code: str, description: str):
query = (
insert(UsersVerificationCodes)
insert(UserVerificationCode)
.values(user_id=user_id, code=code, description=description)
.returning(cls.model.code)
)
@ -173,8 +176,8 @@ class UserCodesDAO(BaseDAO):
AND now() - usersverificationcodes.date_of_creation < INTERVAL '30 minutes'
"""
query = (
select(UsersVerificationCodes.__table__.columns)
.where((func.now() - UsersVerificationCodes.date_of_creation) < text("INTERVAL '10 minutes'"))
select(UserVerificationCode.__table__.columns)
.where((func.now() - UserVerificationCode.date_of_creation) < text("INTERVAL '10 minutes'"))
.filter_by(**filter_by)
)