From 060f4301e9259b9ed4bf70df2478b4d938d94236 Mon Sep 17 00:00:00 2001 From: Ahmed Nagi <144544047+mindfreq@users.noreply.github.com> Date: Mon, 20 Jan 2025 08:13:07 +0000 Subject: [PATCH] u --- config/settings/base.py | 1 + lms/accounts/serializers.py | 2 +- lms/accounts/validation_error.py | 24 --------------------- lms/accounts/views.py | 7 +++--- lms/app/serializers.py | 12 ++++++++--- lms/app/views.py | 20 ++++++++--------- lms/utils/__init__.py | 0 lms/utils/exception_handler.py | 37 ++++++++++++++++++++++++++++++++ 8 files changed, 61 insertions(+), 42 deletions(-) delete mode 100644 lms/accounts/validation_error.py create mode 100644 lms/utils/__init__.py create mode 100644 lms/utils/exception_handler.py diff --git a/config/settings/base.py b/config/settings/base.py index 501b774..f0c5a72 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -378,6 +378,7 @@ REST_FRAMEWORK = { "rest_framework.permissions.IsAuthenticated", ), "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", + 'EXCEPTION_HANDLER': 'utils.exception_handler.custom_exception_handler', } REST_AUTH = { 'LOGIN_SERIALIZER': 'lms.accounts.serializers.CustomLoginSerializer', diff --git a/lms/accounts/serializers.py b/lms/accounts/serializers.py index c27ca9b..1bad08e 100644 --- a/lms/accounts/serializers.py +++ b/lms/accounts/serializers.py @@ -9,7 +9,7 @@ from django.contrib.auth import get_user_model from rest_framework.exceptions import ValidationError from allauth.account.utils import send_email_confirmation from rest_framework.response import Response -from .validation_error import CustomValidationError +from lms.utils.exception_handler import CustomValidationError User = get_user_model() diff --git a/lms/accounts/validation_error.py b/lms/accounts/validation_error.py deleted file mode 100644 index 1e4656b..0000000 --- a/lms/accounts/validation_error.py +++ /dev/null @@ -1,24 +0,0 @@ -from rest_framework.exceptions import APIException -from rest_framework.response import Response -from rest_framework import status - - -class CustomSuccessResponse(Response): - def __init__(self, detail=None, code=status.HTTP_200_OK): - data = {"success": True} - if detail is not None: - data["detail"] = detail - super().__init__(data, status=code) - - - -class CustomValidationError(APIException): - status_code = 400 - default_detail = 'A validation error occurred.' - default_code = 'validation_error' - - def __init__(self, detail=None, code=None): - if detail is not None: - self.detail = detail - if code is not None: - self.status_code = code diff --git a/lms/accounts/views.py b/lms/accounts/views.py index 6634df5..f9ba14f 100644 --- a/lms/accounts/views.py +++ b/lms/accounts/views.py @@ -7,7 +7,6 @@ from rest_framework.permissions import AllowAny, IsAuthenticated from .serializers import ChangeEmailSerializer from asgiref.sync import sync_to_async from django.contrib.auth import get_user_model -from .validation_error import CustomSuccessResponse, CustomValidationError User = get_user_model() @@ -17,7 +16,7 @@ class UserView(APIView): def get(self, request): user = request.user image_url = request.build_absolute_uri(user.image.url) if user.image else None - return CustomSuccessResponse({ + return Response({ "name": user.full_name, "image": image_url }) @@ -36,9 +35,9 @@ class UserView(APIView): print("Ok") user.save() - return CustomSuccessResponse( + return Response( {"ok"}, - code=status.HTTP_200_OK + status=status.HTTP_200_OK ) diff --git a/lms/app/serializers.py b/lms/app/serializers.py index 1796dad..5ebc5b0 100644 --- a/lms/app/serializers.py +++ b/lms/app/serializers.py @@ -9,15 +9,21 @@ from dj_rest_auth.registration.serializers import RegisterSerializer - class CourseSerializer(serializers.ModelSerializer): - owner_name = serializers.CharField(source='owner.username', read_only=True) + owner_name = serializers.CharField(source='owner.full_name', read_only=True) + owner_image = serializers.SerializerMethodField() students_in_course = serializers.SerializerMethodField() class Meta: model = Course - fields = ['id', 'title', 'description', 'is_paid', 'price', 'image', 'owner_name', 'students_in_course', 'created_at', 'updated_at'] + fields = ['id', 'title', 'description', 'is_paid', 'price', 'image', 'owner_name', 'owner_image', 'students_in_course', 'created_at', 'updated_at'] read_only_fields = ['created_at', 'updated_at'] + def get_owner_image(self, obj): + request = self.context.get('request') + if obj.owner.image: + return request.build_absolute_uri(obj.owner.image.url) + return None + def get_students_in_course(self, obj): return Enrollment.objects.filter(course=obj).values('student').distinct().count() diff --git a/lms/app/views.py b/lms/app/views.py index b96ccc2..f5c5878 100644 --- a/lms/app/views.py +++ b/lms/app/views.py @@ -10,7 +10,7 @@ from .permissions import IsOwnerOrReadOnly, IsAdmin from rest_framework.decorators import action from rest_framework.exceptions import PermissionDenied from django.contrib.auth import get_user_model -from accounts.validation_error import CustomSuccessResponse, CustomValidationError +from lms.utils.exception_handler import CustomValidationError User = get_user_model() @@ -271,22 +271,22 @@ class EnrollmentViewSet(ModelViewSet): student = User.objects.filter(email=student_email).first() if not student: - raise CustomValidationError("User not found", code=status.HTTP_404_NOT_FOUND) + raise CustomValidationError("User not found", status=status.HTTP_404_NOT_FOUND) if student_email == request.user.email: - raise CustomValidationError("You can't add yourself", code=status.HTTP_400_BAD_REQUEST) + raise CustomValidationError("You can't add yourself", status=status.HTTP_400_BAD_REQUEST) if Enrollment.objects.filter(student__email=student_email, course=course).exists(): - raise CustomValidationError("This user already exists", code=status.HTTP_400_BAD_REQUEST) + raise CustomValidationError("This user already exists", status=status.HTTP_400_BAD_REQUEST) if not course.is_paid: - raise CustomValidationError("Course is not paid", code=status.HTTP_400_BAD_REQUEST) + raise CustomValidationError("Course is not paid", status=status.HTTP_400_BAD_REQUEST) # Allow only the course owner to enroll students if course.owner != request.user: raise CustomValidationError("You do not have permission to enroll students in this course", - code=status.HTTP_403_FORBIDDEN) + status=status.HTTP_403_FORBIDDEN) # Validate the data before saving enrollment_data = { @@ -296,10 +296,10 @@ class EnrollmentViewSet(ModelViewSet): serializer = PrivateEnrollmentSerializer(data=enrollment_data) if serializer.is_valid(): serializer.save() - return CustomSuccessResponse(f"Student {student.full_name} has been added", - code=status.HTTP_201_CREATED) + return Response(f"Student {student.full_name} has been added", + status=status.HTTP_201_CREATED) - raise CustomValidationError(serializer.errors, code=status.HTTP_400_BAD_REQUEST) + raise CustomValidationError(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @action(detail=False, methods=['get'], url_path='get-my-students') def get_my_students(self, request): @@ -314,7 +314,7 @@ class EnrollmentViewSet(ModelViewSet): .distinct() ) - return CustomSuccessResponse(list(my_students), code=200) + return Response(list(my_students), status=200) diff --git a/lms/utils/__init__.py b/lms/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lms/utils/exception_handler.py b/lms/utils/exception_handler.py new file mode 100644 index 0000000..e4c3d6c --- /dev/null +++ b/lms/utils/exception_handler.py @@ -0,0 +1,37 @@ +from rest_framework.exceptions import APIException +from rest_framework import status +from rest_framework.views import exception_handler +from rest_framework.response import Response + + +class CustomValidationError(APIException): + status_code = status.HTTP_400_BAD_REQUEST + default_detail = 'A validation error occurred.' + + def __init__(self, detail=None, status=None): + self.detail = detail if detail is not None else self.default_detail + self.status = status if status is not None else self.status + + def __str__(self): + return f"{self.detail} (Status: {self.status})" + + + + + +def custom_exception_handler(exc, context): + # Call REST framework's default exception handler first + response = exception_handler(exc, context) + + # Handle CustomValidationError + if isinstance(exc, CustomValidationError): + return Response( + { + 'error': exc.detail, + 'status_code': exc.status_code, + }, + status=exc.status_code, + ) + + # Return the default response for other exceptions + return response \ No newline at end of file