diff --git a/back/api.py b/back/api.py index a014046..0c571b6 100644 --- a/back/api.py +++ b/back/api.py @@ -98,7 +98,7 @@ async def put_in_db(name: Annotated[str, Form()], category: Annotated[str, Form( # создаем объект Announcement temp_ancmt = orm_models.Announcement(user_id=current_user.id, name=name, category=category, best_by=bestBy, address=address, longtitude=longtitude, latitude=latitude, description=description, metro=metro, - trashId=trashId, src=uploaded_name, booked_by=0) + trashId=trashId, src=uploaded_name, booked_counter=0) try: db.add(temp_ancmt) # добавляем в бд await db.commit() # сохраняем изменения @@ -139,19 +139,28 @@ async def change_book_status(data: pydantic_schemas.Book, current_user: Annotate if not announcement_to_change: raise HTTPException(status_code=404, detail="Item not found") # Проверяем, что объявление бронирует не владелец - if announcement_to_change.booked_by.Comparator.contains(current_user.id): + if current_user.id == announcement_to_change.user_id: raise HTTPException(status_code=403, detail="A user can't book his announcement") else: - # Инкрементируем поле booked_counter на 1 - announcement_to_change.booked_counter += 1 - # вставляем индекс забронировавшего пользователя в поле booked_by - await db.execute(mytable.insert(), data=[1,2,3]) - - announcement_to_change.booked_by - # фиксируем изменения в бд - await db.commit() - await db.refresh(announcement_to_change) - return {"Success": True} + # ищем пару с заданными id объявления и пользователя + query = await db.execute(select(orm_models.AnnouncementUser).where( + orm_models.AnnouncementUser.announcement_id == announcement_to_change.id).where( + orm_models.AnnouncementUser.booking_user_id == current_user.id)) + pair_found = query.scalars().first() + # если не найдена + if not pair_found: + # создаем новый объект таблицы AnnouncementUser + new_pair = orm_models.AnnouncementUser(announcement_to_change.id, current_user.id) + # Инкрементируем поле booked_counter на 1 + announcement_to_change.booked_counter += 1 + # вставляем индекс забронировавшего пользователя в поле booked_by + db.add(new_pair) + + # фиксируем изменения в бд + await db.commit() + await db.refresh(announcement_to_change) + return {"Success": True} + raise HTTPException(status_code=403, detail="The announcement is already booked by this user") # reginstration diff --git a/back/main.py b/back/main.py index 23d2504..7457411 100644 --- a/back/main.py +++ b/back/main.py @@ -21,7 +21,7 @@ async def main(): await init_models() - server = Server(config=uvicorn.Config(app_fastapi, workers=1, loop="asyncio", host="0.0.0.0")) + server = Server(config=uvicorn.Config(app_fastapi, workers=1, loop="asyncio", host="127.0.0.1")) api = asyncio.create_task(server.serve()) sched = asyncio.create_task(app_rocketry.serve()) diff --git a/back/orm_models.py b/back/orm_models.py index f8a2a31..68ec5bd 100644 --- a/back/orm_models.py +++ b/back/orm_models.py @@ -1,6 +1,7 @@ -from sqlalchemy import Column, Integer, String, Boolean, Float, Date, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy import Column, Integer, String, Boolean, Float, Date, ForeignKey, ForeignKeyConstraint +from sqlalchemy.orm import relationship, mapped_column, composite, Mapped from sqlalchemy.dialects import postgresql +import dataclasses from .db import Base, engine @@ -18,7 +19,7 @@ class User(Base):#класс пользователя num_of_ratings = Column(Integer, default=0) # количество оценок (т.е. то, сколько раз другие пользователи оценили текущего) reg_date = Column(Date) # дата регистрации - announcements = relationship("Announcement", back_populates="user", lazy='selectin') + announcements = relationship("Announcement", secondary="announcementuser", back_populates="user", lazy='selectin') trashboxes_chosen = relationship("Trashbox", back_populates="user", lazy='selectin') class Announcement(Base): #класс объявления @@ -36,11 +37,35 @@ class Announcement(Base): #класс объявления src = Column(String, nullable=True) #изображение продукта в объявлении metro = Column(String) #ближайщее метро от адреса нахождения продукта trashId = Column(Integer, nullable=True) - booked_by = postgresql.ARRAY(Integer, dimensions=2) #массив с id пользователей, забронировавших объявление - booked_counter = Column(Integer) #количество забронировавших (0 - никто не забронировал) + booked_by = Column(Integer, nullable=True) # id пользователя, забронировавшего объявление + booked_counter = Column(Integer, nullable=True) #количество забронировавших (0 - никто не забронировал) obsolete = Column(Boolean, default=False) # состояние объявления (по-умолчанию считаем его актуальным) - user = relationship("User", back_populates="announcements") + user = relationship("User", secondary="announcementuser", back_populates="announcements") + +# Класс пары, хранящей id объявления и id забронировавшего юзера +@dataclasses.dataclass +class UserAnouncementPair: + announcement_id: int + booking_user_id: int + +class AnnouncementUser(Base): + __tablename__ = "announcementuser" + + def __init__(self, an_id, b_u_id): + self.announcement_id = an_id + self.booking_user_id = b_u_id + + announcement_id = Column(Integer, ForeignKey("announcements.id"), primary_key=True) # id забронированного объявления + booking_user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) # id пользователя, забронировавшего объявление + + + +# class Ratings(Base): +# __tablename__ = "ratings" + +# rated_user_id = Column(Integer, primary_key=True) # id пользователя, оставившего оценку +# rating_value = Column(Integer, primary_key=True) # оценка class Trashbox(Base): #класс мусорных баков diff --git a/migrations/versions/19dbd9793f11_create_constructor_to_make_object_if_.py b/migrations/versions/19dbd9793f11_create_constructor_to_make_object_if_.py new file mode 100644 index 0000000..9c13c97 --- /dev/null +++ b/migrations/versions/19dbd9793f11_create_constructor_to_make_object_if_.py @@ -0,0 +1,30 @@ +"""create constructor to make object (if leave as is, with two primary keys, says that one argument required, but two were given). Booked_counter can be nullable + +Revision ID: 19dbd9793f11 +Revises: 945c70aa70e7 +Create Date: 2024-08-13 15:29:21.542539 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '19dbd9793f11' +down_revision: Union[str, None] = '945c70aa70e7' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/migrations/versions/945c70aa70e7_booked_by_colomn_changed_to_integer_.py b/migrations/versions/945c70aa70e7_booked_by_colomn_changed_to_integer_.py new file mode 100644 index 0000000..d1d23ff --- /dev/null +++ b/migrations/versions/945c70aa70e7_booked_by_colomn_changed_to_integer_.py @@ -0,0 +1,38 @@ +"""booked_by colomn changed to Integer, added new table AnnouncementUser implementing one to many relationships + +Revision ID: 945c70aa70e7 +Revises: 5a8105ac1a4f +Create Date: 2024-08-12 20:36:08.669574 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '945c70aa70e7' +down_revision: Union[str, None] = '5a8105ac1a4f' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('announcementuser', + sa.Column('announcement_id', sa.Integer(), nullable=False), + sa.Column('booking_user_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['announcement_id'], ['announcements.id'], ), + sa.ForeignKeyConstraint(['booking_user_id'], ['users.id'], ), + sa.PrimaryKeyConstraint('announcement_id', 'booking_user_id') + ) + op.add_column('announcements', sa.Column('booked_by', sa.Integer(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('announcements', 'booked_by') + op.drop_table('announcementuser') + # ### end Alembic commands ###