Comentários + avaliações¶
Comentários e avaliações de 0 a 5 estrelas sobre qualquer coisa —
produto, post, evento — sem uma tabela por tipo. O módulo
tempest_fastapi_sdk.reviews é polimórfico: cada comentário/nota aponta
para um alvo via o par (target_type, target_id).
Três peças, sobre os primitivos do SDK:
- Tabelas abstratas —
BaseCommentModel(encadeável viaparent_id) eBaseRatingModel(nota0..5, um voto por usuário) + fábricasmake_*. ReviewService— comentar, avaliar (upsert), buscar a nota do usuário e agregar (média + contagem + distribuição por estrela).make_reviews_router— os endpoints HTTP.
Sem extra
Só o núcleo do SDK. A nota usa o novo campo validado RatingField
(Annotated[int, 0..5]).
As tabelas¶
O SDK entrega a linha abstrata; o projeto entrega a concreta com a FK do autor/usuário:
from tempest_fastapi_sdk.reviews import BaseCommentModel, BaseRatingModel
from sqlalchemy import ForeignKey, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column
from uuid import UUID
class CommentModel(BaseCommentModel):
__tablename__ = "comments"
author_id: Mapped[UUID] = mapped_column(
ForeignKey("users.id", ondelete="CASCADE"), index=True
)
class RatingModel(BaseRatingModel):
__tablename__ = "ratings"
__table_args__ = (
UniqueConstraint(
"target_type", "target_id", "user_id", name="uq_rating_target_user"
),
)
user_id: Mapped[UUID] = mapped_column(
ForeignKey("users.id", ondelete="CASCADE"), index=True
)
Atalho para testes
O serviço¶
ReviewService recebe dois repositórios (comentários e notas):
from uuid import UUID
from sqlalchemy.ext.asyncio import AsyncSession
from tempest_fastapi_sdk import BaseRepository
from tempest_fastapi_sdk.reviews import ReviewService
from src.db.models import CommentModel, RatingModel
def build_review_service(session: AsyncSession) -> ReviewService:
return ReviewService(
comments=BaseRepository(session, model=CommentModel),
ratings=BaseRepository(session, model=RatingModel),
)
async def demo(session: AsyncSession, product_id: UUID, user_id: UUID) -> None:
service = build_review_service(session)
await service.add_comment("product", product_id, user_id, "Excelente!")
# Um voto por usuário — reavaliar atualiza a mesma linha.
await service.rate("product", product_id, user_id, 5)
await service.rate("product", product_id, user_id, 4) # continua 1 nota
aggregate = await service.aggregate("product", product_id)
print(aggregate.average) # 4.0
print(aggregate.count) # 1
print(aggregate.distribution) # {0: 0, 1: 0, ..., 4: 1, 5: 0}
aggregate devolve um RatingAggregateSchema com average (0.0 quando
não há notas), count e distribution (contagem por estrela, chaveada
0..5) — exatamente os números que um widget "4,3 ★ (128 avaliações)"
renderiza.
O router¶
from collections.abc import AsyncIterator
from uuid import UUID
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import AsyncSession
from tempest_fastapi_sdk.reviews import make_reviews_router
from src.api.dependencies.resources import db
from src.api.dependencies.services import build_review_service
sessionmaker = db.get_session_context
async def get_session() -> AsyncIterator[AsyncSession]:
async with sessionmaker() as session:
yield session
def current_user_id() -> UUID:
... # sua dependência de auth resolvendo o UUID do usuário
app = FastAPI()
app.include_router(
make_reviews_router(
service_factory=build_review_service,
session_factory=get_session,
current_user_id=current_user_id,
)
)
Endpoints montados:
| Método | Rota | Faz |
|---|---|---|
POST |
/api/reviews/{target_type}/{target_id}/comments |
Comenta (autor = usuário logado) |
GET |
/api/reviews/{target_type}/{target_id}/comments |
Pagina os comentários |
POST |
/api/reviews/{target_type}/{target_id}/rating |
Define a nota 0–5 (upsert) |
GET |
/api/reviews/{target_type}/{target_id} |
Agregado de estrelas |
Comentar e avaliar exigem autenticação; listar e agregar são públicos.
Uma nota fora de 0..5 é rejeitada com 422 pela validação de
RatingField.
Recapitulando¶
- Alvo polimórfico
(target_type, target_id)— uma dupla de tabelas serve todo tipo reavaliável. ratefaz upsert: um voto por usuário por alvo.aggregateentrega média + contagem + distribuição prontas para a UI.make_reviews_routermonta comentários + notas com validação0..5.