Files
blog.pyaqa.ru/app/application/use_cases/toggle_comment_like.py
Sergey Vanyushkin 7ff3fa0992
All checks were successful
ci/woodpecker/pr/pipeline Pipeline was successful
feat: add comments feature with nested replies and recursive rendering
Implement full comments system: domain entities (Comment, CommentLike),
value objects (CommentContent), use cases (CRUD, like toggle), SQLAlchemy
repository, API v1 endpoints, web UI with comment form and nested replies,
i18n translations (EN/RU/FR/DE), and E2E tests.

Fix nested reply (reply-to-reply) not displaying — the flat reply_comments
dict was only queried for top-level comment IDs, so deeply nested replies
were saved to DB (incrementing comment count) but never rendered. Switch
to a recursive Jinja2 macro that renders any nesting depth.
2026-05-11 15:34:20 +03:00

97 lines
3.2 KiB
Python

"""Toggle comment like use case.
This module implements the use case for toggling likes on comments.
If the user already liked the comment, the like is removed (unlike).
If not, a new like is added.
"""
from uuid import UUID
from app.application.dtos.comment import CommentResponseDTO
from app.application.interfaces import TransactionManager
from app.domain.entities.comment import Comment
from app.domain.entities.comment_like import CommentLike
from app.domain.exceptions import NotFoundException
from app.domain.repositories import CommentRepository
class ToggleCommentLikeUseCase:
"""Use case for toggling a like on a comment.
Handles like/unlike toggle logic. If the user has already liked
the comment, the like is removed. Otherwise, a new like is created.
Attributes:
_comment_repo: Repository for comment and like data access.
_tx_manager: Transaction manager for commit control.
"""
def __init__(
self,
comment_repo: CommentRepository,
tx_manager: TransactionManager,
) -> None:
"""Initialize use case with dependencies.
Args:
comment_repo: Repository for comment and like operations.
tx_manager: Transaction manager instance.
"""
self._comment_repo = comment_repo
self._tx_manager = tx_manager
async def execute(self, comment_id: UUID, liked_by: str) -> CommentResponseDTO:
"""Toggle like on a comment.
If the user already liked the comment, remove the like.
Otherwise, add a new like.
Args:
comment_id: UUID of the comment to toggle like on.
liked_by: User ID.
Returns:
CommentResponseDTO with updated like_count.
Raises:
NotFoundException: If comment with given ID does not exist.
"""
comment = await self._comment_repo.get_by_id(comment_id)
if not comment:
raise NotFoundException(f"Comment with id '{comment_id}' not found")
existing_like = await self._comment_repo.get_like(comment_id, liked_by)
if existing_like:
await self._comment_repo.remove_like(comment_id, liked_by)
comment.like_count = max(0, comment.like_count - 1)
else:
new_like = CommentLike(comment_id=comment_id, liked_by=liked_by)
await self._comment_repo.add_like(new_like)
comment.like_count += 1
await self._comment_repo.update(comment)
await self._tx_manager.commit()
return self._map_to_dto(comment)
def _map_to_dto(self, comment: Comment) -> CommentResponseDTO:
"""Map domain entity to response DTO.
Args:
comment: Domain Comment entity.
Returns:
CommentResponseDTO with all comment attributes including like_count.
"""
return CommentResponseDTO(
id=comment.id,
post_id=comment.post_id,
author_id=comment.author_id,
content=comment.content.value,
parent_id=comment.parent_id,
like_count=comment.like_count,
created_at=comment.created_at,
updated_at=comment.updated_at,
)