Skip to content

Commit 0857080

Browse files
committed
posts migrations
1 parent 6efd3ba commit 0857080

File tree

3 files changed

+155
-2
lines changed

3 files changed

+155
-2
lines changed

backend/app/api/main.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1+
12
from fastapi import APIRouter
23

3-
from app.api.routes import items, login, private, users, utils
4+
from app.api.routes import items, login, private, users, utils, posts
45
from app.core.config import settings
56

67
api_router = APIRouter()
78
api_router.include_router(login.router)
89
api_router.include_router(users.router)
910
api_router.include_router(utils.router)
1011
api_router.include_router(items.router)
11-
12+
api_router.include_router(posts.router)
1213

1314
if settings.ENVIRONMENT == "local":
1415
api_router.include_router(private.router)

backend/app/api/routes/posts.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import uuid
2+
from typing import Any
3+
4+
from fastapi import APIRouter, Depends, HTTPException
5+
from sqlmodel import col, delete, func, select
6+
7+
from app import crud
8+
from app.api.deps import CurrentUser, SessionDep
9+
from app.models import (
10+
Post,
11+
PostCreate,
12+
PostPublic,
13+
PostUpdate,
14+
PostsPublic,
15+
Message,
16+
)
17+
18+
router = APIRouter(prefix="/posts", tags=["posts"])
19+
20+
21+
@router.get("/", response_model=PostsPublic)
22+
def read_posts(session: SessionDep, skip: int = 0, limit: int = 100) -> Any:
23+
"""
24+
Retrieve all posts.
25+
"""
26+
count_statement = select(func.count()).select_from(Post)
27+
count = session.exec(count_statement).one()
28+
29+
statement = select(Post).offset(skip).limit(limit)
30+
posts = session.exec(statement).all()
31+
32+
return PostsPublic(data=posts, count=count)
33+
34+
35+
@router.post("/", response_model=PostPublic)
36+
def create_post(
37+
*,
38+
session: SessionDep,
39+
post_in: PostCreate,
40+
current_user: CurrentUser
41+
) -> Any:
42+
"""
43+
Create a new post.
44+
"""
45+
new_post = Post(
46+
user_id=current_user.id,
47+
content=post_in.content,
48+
image1_url=post_in.image1_url,
49+
image2_url=post_in.image2_url,
50+
image3_url=post_in.image3_url,
51+
)
52+
session.add(new_post)
53+
session.commit()
54+
session.refresh(new_post)
55+
return new_post
56+
57+
58+
@router.get("/{post_id}", response_model=PostPublic)
59+
def read_post_by_id(post_id: uuid.UUID, session: SessionDep) -> Any:
60+
"""
61+
Get a specific post by ID.
62+
"""
63+
post = session.get(Post, post_id)
64+
if not post:
65+
raise HTTPException(status_code=404, detail="Post not found")
66+
return post
67+
68+
69+
@router.patch("/{post_id}", response_model=PostPublic)
70+
def update_post(
71+
*,
72+
session: SessionDep,
73+
post_id: uuid.UUID,
74+
post_in: PostUpdate,
75+
current_user: CurrentUser
76+
) -> Any:
77+
"""
78+
Update a post.
79+
"""
80+
db_post = session.get(Post, post_id)
81+
if not db_post:
82+
raise HTTPException(status_code=404, detail="Post not found")
83+
if db_post.user_id != current_user.id:
84+
raise HTTPException(status_code=403, detail="Not authorized to update this post")
85+
86+
post_data = post_in.model_dump(exclude_unset=True)
87+
db_post.sqlmodel_update(post_data)
88+
session.add(db_post)
89+
session.commit()
90+
session.refresh(db_post)
91+
return db_post
92+
93+
94+
@router.delete("/{post_id}", response_model=Message)
95+
def delete_post(
96+
session: SessionDep, post_id: uuid.UUID, current_user: CurrentUser
97+
) -> Any:
98+
"""
99+
Delete a post.
100+
"""
101+
post = session.get(Post, post_id)
102+
if not post:
103+
raise HTTPException(status_code=404, detail="Post not found")
104+
if post.user_id != current_user.id:
105+
raise HTTPException(status_code=403, detail="Not authorized to delete this post")
106+
107+
session.delete(post)
108+
session.commit()
109+
return Message(message="Post deleted successfully")

backend/app/models.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from pydantic import EmailStr
44
from sqlmodel import Field, Relationship, SQLModel
5+
from typing import Optional
56

67

78
# Shared properties
@@ -112,3 +113,45 @@ class TokenPayload(SQLModel):
112113
class NewPassword(SQLModel):
113114
token: str
114115
new_password: str = Field(min_length=8, max_length=40)
116+
117+
# Shared properties for posts
118+
class PostBase(SQLModel):
119+
content: str = Field(min_length=1, max_length=2000)
120+
image1_url: Optional[str] = None
121+
image2_url: Optional[str] = None
122+
image3_url: Optional[str] = None
123+
124+
125+
# Properties to receive via API on creation
126+
class PostCreate(PostBase):
127+
pass
128+
129+
130+
# Properties to receive via API on update
131+
class PostUpdate(SQLModel):
132+
content: Optional[str] = None
133+
image1_url: Optional[str] = None
134+
image2_url: Optional[str] = None
135+
image3_url: Optional[str] = None
136+
137+
138+
# Database model, infers the `posts` table
139+
class Post(PostBase, table=True):
140+
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
141+
user_id: uuid.UUID = Field(foreign_key="user.id", nullable=False, ondelete="CASCADE")
142+
user: Optional["User"] = Relationship(back_populates="posts")
143+
144+
145+
# Properties to return via API
146+
class PostPublic(PostBase):
147+
id: uuid.UUID
148+
user_id: uuid.UUID
149+
150+
151+
class PostsPublic(SQLModel):
152+
data: list[PostPublic]
153+
count: int
154+
155+
156+
# Add the relationship to the User model
157+
User.posts = Relationship(back_populates="user", sa_relationship_kwargs={"cascade": "all, delete"})

0 commit comments

Comments
 (0)