From 059b7212a96800a49513e1d3a7c3e6ce0f4462dc Mon Sep 17 00:00:00 2001 From: Degisew Date: Wed, 2 Jul 2025 13:20:33 +0300 Subject: [PATCH 1/3] fix(api): add 404 custom exception --- apps/core/exceptions.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/core/exceptions.py b/apps/core/exceptions.py index ce8b0de..1ca6fc8 100644 --- a/apps/core/exceptions.py +++ b/apps/core/exceptions.py @@ -7,6 +7,12 @@ class ServiceBaseException(APIException): default_code = "TICKETING_ERROR" +class NotFoundException(APIException): + status_code = 404 + default_detail = "Not found" + default_code = "NOT_FOUND" + + class SerializationError(APIException): status_code = 500 default_detail = "Object Serialization Error" From 3a0ba48305bdc7e9a1cb0ca8559049bbcaee106a Mon Sep 17 00:00:00 2001 From: Degisew Date: Wed, 2 Jul 2025 13:22:11 +0300 Subject: [PATCH 2/3] ref(api): refactored user profile to make RESTFUL --- apps/account/admin.py | 45 ++++++------ apps/account/models.py | 2 +- apps/account/serializers.py | 19 +---- apps/account/services.py | 14 +++- apps/account/urls.py | 4 +- apps/account/views.py | 66 +++++++++++------- .../user_profiles/signature_EjKHIq4.png | Bin 0 -> 11773 bytes 7 files changed, 82 insertions(+), 68 deletions(-) create mode 100644 mediafiles/mediafiles/user_profiles/signature_EjKHIq4.png diff --git a/apps/account/admin.py b/apps/account/admin.py index 1f274f0..db5a31e 100644 --- a/apps/account/admin.py +++ b/apps/account/admin.py @@ -1,29 +1,40 @@ from django.contrib import admin from django.contrib.auth.models import Group - -# from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from apps.account.forms import UserChangeForm, UserCreationForm from apps.account.models import Role, User, UserPreferences, UserProfile +class UserProfileInline(admin.StackedInline): + model = UserProfile + verbose_name = "Profile" + can_delete = False + extra = 0 + exclude = ["deleted_at"] + + def has_add_permission(self, request, obj=None): + return False + + def has_change_permission(self, request, obj=None): + return False + + def has_delete_permission(self, request, obj=None): + return False + + @admin.register(User) class UserAdmin(admin.ModelAdmin): # The forms to add and change user instances add_form = UserCreationForm form = UserChangeForm model = User - - list_display = ["email", "role"] - list_filter = ["role"] - fieldsets = [ - (None, {"fields": ["email", "password"]}), - ( - "Permissions", - {"fields": ["state", "role"]}, - ), - ("Important dates", {"fields": ["created_at", "updated_at", "deleted_at"]}), - ] - + inlines = [UserProfileInline] + list_display = ["email", "role", "state", "is_profile_set"] + readonly_fields = ["created_at", "updated_at", "last_login", "role", "date_joined"] + exclude = ["deleted_at", "user_permissions", "groups"] + list_filter = ["role", "is_profile_set"] + search_fields = ["email"] + ordering = ["-created_at"] + filter_horizontal = [] add_fieldsets = [ ( None, @@ -41,14 +52,8 @@ class UserAdmin(admin.ModelAdmin): ) ] - search_fields = ["email"] - ordering = ["-created_at"] - readonly_fields = ["created_at", "updated_at"] - filter_horizontal = [] - admin.site.register(Role) -admin.site.register(UserProfile) admin.site.register(UserPreferences) # since we're not using Django's built-in permissions, diff --git a/apps/account/models.py b/apps/account/models.py index d3893b3..0657828 100644 --- a/apps/account/models.py +++ b/apps/account/models.py @@ -31,7 +31,7 @@ class User(AbstractUser, AbstractBaseModel): Role, null=True, blank=True, on_delete=models.RESTRICT, related_name="+" ) - is_profile_set = models.BooleanField(default=True) + is_profile_set = models.BooleanField(default=False) state = models.ForeignKey( DataLookup, diff --git a/apps/account/serializers.py b/apps/account/serializers.py index 80da94c..7859944 100644 --- a/apps/account/serializers.py +++ b/apps/account/serializers.py @@ -115,21 +115,7 @@ def save(self, **kwargs): class UserProfileSerializer(serializers.ModelSerializer): class Meta: model = UserProfile - fields = [ - "first_name", - "last_name", - "phone", - "avatar", - "address", - "created_at", - "updated_at", - ] - - def create(self, validated_data): - user = self.context["request"].user - validated_data["user"] = user - - return super().create(validated_data) + fields = ["first_name", "last_name", "phone", "avatar", "address"] def to_representation(self, instance): return UserProfileResponseSerializer(instance, self.context).to_representation( @@ -138,8 +124,6 @@ def to_representation(self, instance): class UserProfileResponseSerializer(serializers.ModelSerializer): - user = UserSerializer() - class Meta: model = UserProfile fields = [ @@ -149,7 +133,6 @@ class Meta: "phone", "avatar", "address", - "user", "created_at", "updated_at", ] diff --git a/apps/account/services.py b/apps/account/services.py index e044882..b950bc4 100644 --- a/apps/account/services.py +++ b/apps/account/services.py @@ -1,7 +1,8 @@ from uuid import UUID from django.core.cache import cache from django.shortcuts import get_object_or_404 -from apps.account.models import Role +from apps.account.models import Role, UserProfile +from apps.core.exceptions import NotFoundException class RoleService: @@ -16,3 +17,14 @@ def get_cached_role(id: UUID): cache.set(key, result, timeout=3600) return result + + +class UserProfileService: + @staticmethod + def get_user_profile(user) -> UserProfile: + try: + return UserProfile.objects.select_related("user").get(user=user) + except UserProfile.DoesNotExist: + raise NotFoundException( + "Your profile is not not set. Please complete your profile." + ) diff --git a/apps/account/urls.py b/apps/account/urls.py index 649ba58..dbd14f1 100644 --- a/apps/account/urls.py +++ b/apps/account/urls.py @@ -2,7 +2,7 @@ from apps.account.views import ( RoleViewSet, UserViewSet, - USerProfileViewSet, + # USerProfileViewSet, PasswordChangeViewSet, ) @@ -10,7 +10,7 @@ router.register("roles", RoleViewSet, basename="roles") router.register("users", UserViewSet, "users") -router.register("profile", USerProfileViewSet, "company-profile") +# router.register("profile", USerProfileViewSet, "company-profile") router.register("change-password", PasswordChangeViewSet, "change-password") urlpatterns = router.urls diff --git a/apps/account/views.py b/apps/account/views.py index d4fc8d2..2c07e29 100644 --- a/apps/account/views.py +++ b/apps/account/views.py @@ -1,34 +1,67 @@ from rest_framework.permissions import IsAuthenticated, AllowAny -from rest_framework import viewsets, status +from rest_framework import viewsets, mixins, status +from rest_framework.decorators import action from rest_framework.response import Response from rest_framework_simplejwt.tokens import RefreshToken from django.contrib.auth import get_user_model -from apps.account.models import Role, UserProfile +from drf_spectacular.utils import extend_schema +from apps.account.services import UserProfileService +from apps.account.models import Role from apps.account.serializers import ( PasswordChangeSerializer, RoleSerializer, + UserProfileResponseSerializer, UserSerializer, UserProfileSerializer, ) -from apps.core.views import AbstractModelViewSet - User = get_user_model() -class RoleViewSet(AbstractModelViewSet): +class RoleViewSet(viewsets.ReadOnlyModelViewSet): permission_classes = [AllowAny] - http_method_names = ["get"] queryset = Role.objects.all() serializer_class = RoleSerializer -class UserViewSet(AbstractModelViewSet): +class UserViewSet( + mixins.CreateModelMixin, + mixins.ListModelMixin, + mixins.DestroyModelMixin, + viewsets.GenericViewSet, +): permission_classes = [AllowAny] serializer_class = UserSerializer queryset = User.objects.select_related("role", "state").all() + @extend_schema( + request=UserProfileSerializer, responses=UserProfileResponseSerializer + ) + @action(methods=["get", "post", "patch"], detail=False, url_path="me") + def profile(self, request, *args, **kwargs): + user = request.user + + if request.method == "GET": + profile = UserProfileService.get_user_profile(user) + serializer = UserProfileSerializer(profile) + return Response(serializer.data) + + if request.method == "POST": + serializer = UserProfileSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + serializer.save(user=user) + user.is_profile_set = True + user.save() + return Response(serializer.data, status=status.HTTP_200_OK) + + elif request.method == "PATCH": + profile = UserProfileService.get_user_profile(user) + serializer = UserProfileSerializer(profile, data=request.data, partial=True) + serializer.is_valid(raise_exception=True) + serializer.save() + return Response(serializer.data, status=status.HTTP_200_OK) + class PasswordChangeViewSet(viewsets.ViewSet): permission_classes = [IsAuthenticated] @@ -50,22 +83,3 @@ def create(self, request): }, status=status.HTTP_200_OK, ) - - -class USerProfileViewSet(AbstractModelViewSet): - permission_classes = [IsAuthenticated] - http_method_names = ["get", "post", "patch"] - serializer_class = UserProfileSerializer - - def get_queryset(self): - return UserProfile.objects.filter(user=self.request.user) - - # @property - # def access_policy(self): - # return self.permission_classes[0] - - # def get_queryset(self): - # return self.access_policy.scope_queryset( - # request=self.request, - # queryset=UserProfile.objects.all() - # ) diff --git a/mediafiles/mediafiles/user_profiles/signature_EjKHIq4.png b/mediafiles/mediafiles/user_profiles/signature_EjKHIq4.png new file mode 100644 index 0000000000000000000000000000000000000000..555df0b7dc1bf867546508446462b9ad7b92ca05 GIT binary patch literal 11773 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 Date: Wed, 2 Jul 2025 13:37:14 +0300 Subject: [PATCH 3/3] fix(db): added missed migration changes --- .../0002_alter_user_is_profile_set.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 apps/account/migrations/0002_alter_user_is_profile_set.py diff --git a/apps/account/migrations/0002_alter_user_is_profile_set.py b/apps/account/migrations/0002_alter_user_is_profile_set.py new file mode 100644 index 0000000..0927a08 --- /dev/null +++ b/apps/account/migrations/0002_alter_user_is_profile_set.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.5 on 2025-07-02 10:36 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='is_profile_set', + field=models.BooleanField(default=False), + ), + ]