This repository has been archived on 2026-02-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blog/bp/post/services/post_operations.py
giles 8f7a15186c
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled
feat: initialize blog app with blueprints and templates
Extract blog-specific code from the coop monolith into a standalone
repository. Includes auth, blog, post, admin, menu_items, snippets
blueprints, associated templates, Dockerfile (APP_MODULE=app:app),
entrypoint, and Gitea CI workflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 23:15:56 +00:00

59 lines
1.6 KiB
Python

from __future__ import annotations
from typing import Optional
from sqlalchemy import select, func, update
from sqlalchemy.ext.asyncio import AsyncSession
from models.ghost_content import Post, PostLike
async def toggle_post_like(
session: AsyncSession,
user_id: int,
post_id: int,
) -> tuple[bool, Optional[str]]:
"""
Toggle a post like for a given user using soft deletes.
Returns (liked_state, error_message).
- If error_message is not None, an error occurred.
- liked_state indicates whether post is now liked (True) or unliked (False).
"""
# Verify post exists
post_exists = await session.scalar(
select(Post.id).where(Post.id == post_id, Post.deleted_at.is_(None))
)
if not post_exists:
return False, "Post not found"
# Check if like exists (not deleted)
existing = await session.scalar(
select(PostLike).where(
PostLike.user_id == user_id,
PostLike.post_id == post_id,
PostLike.deleted_at.is_(None),
)
)
if existing:
# Unlike: soft delete the like
await session.execute(
update(PostLike)
.where(
PostLike.user_id == user_id,
PostLike.post_id == post_id,
PostLike.deleted_at.is_(None),
)
.values(deleted_at=func.now())
)
return False, None
else:
# Like: add a new like
new_like = PostLike(
user_id=user_id,
post_id=post_id,
)
session.add(new_like)
return True, None