From 598170300cbd1612b165d21ab2f83c5e7e63da8d Mon Sep 17 00:00:00 2001 From: ankushchk Date: Wed, 4 Mar 2026 14:19:44 +0530 Subject: [PATCH 1/3] fix: harden API endpoints and fix model serialization in JSON responses --- web/views.py | 49 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/web/views.py b/web/views.py index b4d485749..f5cbd3618 100644 --- a/web/views.py +++ b/web/views.py @@ -2293,7 +2293,7 @@ def api_course_list(request): "description": course.description, "teacher": course.teacher.username, "price": str(course.price), - "subject": course.subject, + "subject": course.subject.name, "level": course.level, "slug": course.slug, } @@ -2309,7 +2309,22 @@ def api_course_create(request): if request.method != "POST": return JsonResponse({"error": "Only POST method is allowed"}, status=405) - data = json.loads(request.body) + try: + data = json.loads(request.body) + except json.JSONDecodeError: + return JsonResponse({"error": "Invalid JSON"}, status=400) + + # Required fields check + required_fields = ["title", "description", "learning_objectives", "price", "max_students", "subject"] + for field in required_fields: + if field not in data: + return JsonResponse({"error": f"Missing required field: {field}"}, status=400) + + try: + subject = Subject.objects.get(id=data["subject"]) + except (Subject.DoesNotExist, ValueError, TypeError): + return JsonResponse({"error": "Invalid subject ID"}, status=400) + course = Course.objects.create( teacher=request.user, title=data["title"], @@ -2318,8 +2333,8 @@ def api_course_create(request): prerequisites=data.get("prerequisites", ""), price=data["price"], max_students=data["max_students"], - subject=data["subject"], - level=data["level"], + subject=subject, + level=data.get("level", "beginner"), ) return JsonResponse( { @@ -2341,7 +2356,7 @@ def api_course_detail(request, slug): "description": course.description, "teacher": course.teacher.username, "price": str(course.price), - "subject": course.subject, + "subject": course.subject.name, "level": course.level, "prerequisites": course.prerequisites, "learning_objectives": course.learning_objectives, @@ -2438,7 +2453,17 @@ def api_forum_topic_create(request): if request.method != "POST": return JsonResponse({"error": "Only POST method is allowed"}, status=405) - data = json.loads(request.body) + try: + data = json.loads(request.body) + except json.JSONDecodeError: + return JsonResponse({"error": "Invalid JSON"}, status=400) + + # Required fields check + required_fields = ["title", "content", "category"] + for field in required_fields: + if field not in data: + return JsonResponse({"error": f"Missing required field: {field}"}, status=400) + category = get_object_or_404(ForumCategory, id=data["category"]) topic = ForumTopic.objects.create( title=data["title"], @@ -2461,7 +2486,17 @@ def api_forum_reply_create(request): if request.method != "POST": return JsonResponse({"error": "Only POST method is allowed"}, status=405) - data = json.loads(request.body) + try: + data = json.loads(request.body) + except json.JSONDecodeError: + return JsonResponse({"error": "Invalid JSON"}, status=400) + + # Required fields check + required_fields = ["topic", "content"] + for field in required_fields: + if field not in data: + return JsonResponse({"error": f"Missing required field: {field}"}, status=400) + topic = get_object_or_404(ForumTopic, id=data["topic"]) reply = ForumReply.objects.create( topic=topic, From 833e612c0571f7203be088a5e4e2f18696874c82 Mon Sep 17 00:00:00 2001 From: ankushchk Date: Wed, 4 Mar 2026 16:47:49 +0530 Subject: [PATCH 2/3] fix: harden required-field validation to reject null or empty values --- web/views.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/web/views.py b/web/views.py index f5cbd3618..ea52858d0 100644 --- a/web/views.py +++ b/web/views.py @@ -2314,11 +2314,12 @@ def api_course_create(request): except json.JSONDecodeError: return JsonResponse({"error": "Invalid JSON"}, status=400) - # Required fields check + # Required fields check (must be present and not empty) required_fields = ["title", "description", "learning_objectives", "price", "max_students", "subject"] for field in required_fields: - if field not in data: - return JsonResponse({"error": f"Missing required field: {field}"}, status=400) + val = data.get(field) + if val is None or (isinstance(val, str) and not val.strip()): + return JsonResponse({"error": f"Missing or empty required field: {field}"}, status=400) try: subject = Subject.objects.get(id=data["subject"]) @@ -2458,11 +2459,12 @@ def api_forum_topic_create(request): except json.JSONDecodeError: return JsonResponse({"error": "Invalid JSON"}, status=400) - # Required fields check + # Required fields check (must be present and not empty) required_fields = ["title", "content", "category"] for field in required_fields: - if field not in data: - return JsonResponse({"error": f"Missing required field: {field}"}, status=400) + val = data.get(field) + if val is None or (isinstance(val, str) and not val.strip()): + return JsonResponse({"error": f"Missing or empty required field: {field}"}, status=400) category = get_object_or_404(ForumCategory, id=data["category"]) topic = ForumTopic.objects.create( @@ -2491,11 +2493,12 @@ def api_forum_reply_create(request): except json.JSONDecodeError: return JsonResponse({"error": "Invalid JSON"}, status=400) - # Required fields check + # Required fields check (must be present and not empty) required_fields = ["topic", "content"] for field in required_fields: - if field not in data: - return JsonResponse({"error": f"Missing required field: {field}"}, status=400) + val = data.get(field) + if val is None or (isinstance(val, str) and not val.strip()): + return JsonResponse({"error": f"Missing or empty required field: {field}"}, status=400) topic = get_object_or_404(ForumTopic, id=data["topic"]) reply = ForumReply.objects.create( From 6463348d47626665031e831ece7dedb771372981 Mon Sep 17 00:00:00 2001 From: ankushchk Date: Wed, 4 Mar 2026 16:54:39 +0530 Subject: [PATCH 3/3] fix: normalize and validate level field in api_course_create --- web/views.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/web/views.py b/web/views.py index ea52858d0..9f9f049e7 100644 --- a/web/views.py +++ b/web/views.py @@ -2326,6 +2326,17 @@ def api_course_create(request): except (Subject.DoesNotExist, ValueError, TypeError): return JsonResponse({"error": "Invalid subject ID"}, status=400) + # Normalize and validate level + level = data.get("level") + if level is None or (isinstance(level, str) and not level.strip()): + level = "beginner" + else: + level = str(level).strip().lower() + + valid_levels = dict(Course._meta.get_field("level").choices).keys() + if level not in valid_levels: + return JsonResponse({"error": f"Invalid level. Must be one of: {', '.join(valid_levels)}"}, status=400) + course = Course.objects.create( teacher=request.user, title=data["title"], @@ -2335,7 +2346,7 @@ def api_course_create(request): price=data["price"], max_students=data["max_students"], subject=subject, - level=data.get("level", "beginner"), + level=level, ) return JsonResponse( {