From 29ed73a618c6dcb83d4cba348bedc5982a5cd6d3 Mon Sep 17 00:00:00 2001 From: Karadumk Date: Sat, 1 Mar 2025 15:32:00 +0100 Subject: [PATCH 1/4] feat(blog):add dynamic sorting, filtering, and pagination with validation --- api/v1/services/blog.py | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/api/v1/services/blog.py b/api/v1/services/blog.py index 46c02b147..e405346f6 100644 --- a/api/v1/services/blog.py +++ b/api/v1/services/blog.py @@ -26,11 +26,35 @@ def create(self, db: Session, schema: BlogCreate, author_id: str): db.refresh(new_blogpost) return new_blogpost - def fetch_all(self): - """Fetch all blog posts""" + def fetch_all( + self, + sort_by: Optional[str] = "created_at", + sort_order: Optional[str] = "desc", + author_id: Optional[str] = None, + category: Optional[str] = None, + limit: int = 10, + offset: int = 0 + ): + """Fetch all blog posts with sorting, filtering, and pagination""" + + query = self.db.query(Blog).filter(Blog.is_deleted.is_(False)) + + # Apply filters + if author_id: + query = query.filter(Blog.author_id == author_id) + if category: + query = query.filter(Blog.category == category) + + # Apply sorting + if sort_order == "desc": + query = query.order_by(desc(getattr(Blog, sort_by, Blog.created_at))) + else: + query = query.order_by(asc(getattr(Blog, sort_by, Blog.created_at))) + + # Apply pagination + blogs = query.offset(offset).limit(limit).all() - blogs = self.db.query(Blog).filter(Blog.is_deleted == False).all() - return blogs + return blogs def fetch(self, blog_id: str): """Fetch a blog post by its ID""" From 1ee5367320bf890a936bd8eab91f037619d9e2aa Mon Sep 17 00:00:00 2001 From: Karadumk Date: Sun, 2 Mar 2025 11:25:12 +0100 Subject: [PATCH 2/4] feat(blog): include total count in paginated blog retrieval response --- api/v1/services/blog.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/api/v1/services/blog.py b/api/v1/services/blog.py index e405346f6..2f877f5d6 100644 --- a/api/v1/services/blog.py +++ b/api/v1/services/blog.py @@ -27,7 +27,7 @@ def create(self, db: Session, schema: BlogCreate, author_id: str): return new_blogpost def fetch_all( - self, + self, sort_by: Optional[str] = "created_at", sort_order: Optional[str] = "desc", author_id: Optional[str] = None, @@ -45,6 +45,8 @@ def fetch_all( if category: query = query.filter(Blog.category == category) + total_count = query.count() + # Apply sorting if sort_order == "desc": query = query.order_by(desc(getattr(Blog, sort_by, Blog.created_at))) @@ -54,7 +56,7 @@ def fetch_all( # Apply pagination blogs = query.offset(offset).limit(limit).all() - return blogs + return {"total_count": total_count, "blogs": blogs} def fetch(self, blog_id: str): """Fetch a blog post by its ID""" From 7c3ebbe68806c29b636e33377c46c13ba0dc9586 Mon Sep 17 00:00:00 2001 From: Karadumk Date: Sun, 2 Mar 2025 11:32:12 +0100 Subject: [PATCH 3/4] fix(blog): make category filter case-insensitive using ilike --- api/v1/services/blog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/v1/services/blog.py b/api/v1/services/blog.py index 2f877f5d6..f03ab389b 100644 --- a/api/v1/services/blog.py +++ b/api/v1/services/blog.py @@ -43,7 +43,7 @@ def fetch_all( if author_id: query = query.filter(Blog.author_id == author_id) if category: - query = query.filter(Blog.category == category) + query = query.filter(Blog.category.ilike(f"%{category}%")) total_count = query.count() From b73193875f3b1364fdaa530064ec3044cb4a4699 Mon Sep 17 00:00:00 2001 From: Karadumk Date: Sun, 2 Mar 2025 11:37:31 +0100 Subject: [PATCH 4/4] fix(blog): validate sort_by parameter against model fields and prevent invalid sorting --- api/v1/services/blog.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/api/v1/services/blog.py b/api/v1/services/blog.py index f03ab389b..e40f0693f 100644 --- a/api/v1/services/blog.py +++ b/api/v1/services/blog.py @@ -39,6 +39,11 @@ def fetch_all( query = self.db.query(Blog).filter(Blog.is_deleted.is_(False)) + # Validate sort_by field + valid_columns = {column.key for column in inspect(Blog).columns} + if sort_by not in valid_columns: + sort_by = "created_at" # Default to a safe column + # Apply filters if author_id: query = query.filter(Blog.author_id == author_id) @@ -49,11 +54,12 @@ def fetch_all( # Apply sorting if sort_order == "desc": - query = query.order_by(desc(getattr(Blog, sort_by, Blog.created_at))) + query = query.order_by(desc(getattr(Blog, sort_by))) else: - query = query.order_by(asc(getattr(Blog, sort_by, Blog.created_at))) + query = query.order_by(asc(getattr(Blog, sort_by))) # Apply pagination + total_count = query.count() blogs = query.offset(offset).limit(limit).all() return {"total_count": total_count, "blogs": blogs}