bimka
@bimka
Осваиваю Питон

Как в SQLAlchemy посчитать количество дочерних элементов?

Есть подменю, которое содержит в себе блюда. Неоходимо при вызове подменю вернуть количество блюд.

Не хочу каждый раз при вызове подменю пересчитывать количество блюд, поэтому, при создании нового блюда запись с количеством блюд в подменю увеличивается на один.

При реализации возникла проблема: при создании блюда не могу вытащить id родительского подменю.

# schemas.py
class SubmenuBase(BaseModel):
    title: str
    description: str
    dishes_count: int = 0

class Submenu(SubmenuBase):
    id: uuid.UUID

    class Config:
        orm_mode = True

class SubmenuCreate(SubmenuBase):
    pass

class DishBase(BaseModel):
    title: str
    description: str
    price: str

class Dish(DishBase):
    id: uuid.UUID
    submenu_id: uuid.UUID

    class Config:
        orm_mode = True

class DishCreate(DishBase):
    pass

# models.py
class Submenu(Base):
    __tablename__ = "submenus"

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    title = Column(String, unique=True)
    description = Column(String)
    dishes_count = Column(Integer, default=0)

    dishes = relationship("Dish",
                          back_populates="submenu",
                          cascade="all, delete")


class Dish(Base):
    __tablename__ = "dishes"

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    title = Column(String, unique=True)
    description = Column(String)
    price = Column(Float, index=True)
    submenu_id = Column(UUID(as_uuid=True), ForeignKey("submenus.id"))
    submenu = relationship("Submenu", back_populates="dishes")

# router.py
@dish_router.post("/", response_model=schemas.Dish,
                  status_code=status.HTTP_201_CREATED)
def create_dish(
        dish: schemas.DishCreate,
        db: Session = Depends(get_db)):
    new_dish = service.create_dish(dish, db)
    if new_dish is None:
        raise HTTPException(status_code=201, detail="Dish already exists.")
    return new_dish

# service.py 
def create_dish(dish: schemas.DishCreate, db: Session):
    dish_in_db = db_requests.get_dish_by_title(dish.title, db)
    if dish_in_db:
        return None
    return db_requests.create_dish(dish, db)

# db_requests.py
def create_dish(dish: schemas.DishCreate, db: Session):
    new_dish = models.Dish(title=dish.title,
                           description=dish.description,
                           price=dish.price)
    db.add(new_dish)
    db.flush()
    submenus.db_requests.add_one_dish_to_the_quantity(new_dish.submenu_id, db)
    db.commit()
    return new_dish

# submenus.db_requests.py
def add_one_dish_to_the_quantity(submenu_id: uuid.UUID, db: Session):
    new_count = db.query(models.Submenu.dishes_count) \
        .filter(models.Submenu.id == submenu_id) \
        .first() + 1
    print(new_count)
    db.query(models.Submenu) \
        .filter(models.Submenu.id == submenu_id) \
        .update({"dishes_count": new_count})


При попытке создания нового блюда возвращает ошибку:
pydantic.error_wrappers.ValidationError: 1 validation error for Dish
response -> submenu_id
none is not an allowed value (type=type_error.none.not_allowed)


submenu_id - None. Как мне получить submenu_id?
  • Вопрос задан
  • 132 просмотра
Решения вопроса 1
@5465
Вы можете передать submenu_id в функцию create_dish в качестве параметра. Вы можете найти submenu_id на основе submenu_title, который вы также можете передать в функцию create_dish. Для этого вам необходимо сначала выполнить запрос к базе данных для получения объекта Submenu по его названию, а затем использовать его id в качестве submenu_id для создания нового блюда.

Пример реализации:

# service.py 
def create_dish(dish: schemas.DishCreate, submenu_title: str, db: Session):
    submenu = db_requests.get_submenu_by_title(submenu_title, db)
    if not submenu:
        raise HTTPException(status_code=404, detail="Submenu not found.")
    dish_in_db = db_requests.get_dish_by_title(dish.title, db)
    if dish_in_db:
        raise HTTPException(status_code=409, detail="Dish already exists.")
    return db_requests.create_dish(dish, submenu.id, db)

# db_requests.py
def create_dish(dish: schemas.DishCreate, submenu_id: uuid.UUID, db: Session):
    new_dish = models.Dish(title=dish.title,
                           description=dish.description,
                           price=dish.price,
                           submenu_id=submenu_id)
    db.add(new_dish)
    db.flush()
    add_one_dish_to_the_quantity(submenu_id, db)
    db.commit()
    return new_dish

def get_submenu_by_title(submenu_title: str, db: Session):
    return db.query(models.Submenu).filter(models.Submenu.title == submenu_title).first()

def get_dish_by_title(dish_title: str, db: Session):
    return db.query(models.Dish).filter(models.Dish.title == dish_title).first()

def add_one_dish_to_the_quantity(submenu_id: uuid.UUID, db: Session):
    db.query(models.Submenu) \
        .filter(models.Submenu.id == submenu_id) \
        .update({"dishes_count": models.Submenu.dishes_count + 1})


Теперь вы можете передать название подменю при создании нового блюда и использовать его для поиска submenu_id.

# router.py
@dish_router.post("/", response_model=schemas.Dish,
                  status_code=status.HTTP_201_CREATED)
def create_dish(
        dish: schemas.DishCreate,
        submenu_title: str,
        db: Session = Depends(get_db)):
    new_dish = service.create_dish(dish, submenu_title, db)
    return new_dish


# forms.py
self.helper.form_action = reverse('games_detail', kwargs={'currency_pk': submenu_id})
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы