From 93b1699253553a46ba1802c6ef6874a3f0dc6793 Mon Sep 17 00:00:00 2001 From: Amirhossein Khalili Date: Thu, 12 Mar 2026 08:30:14 +0800 Subject: [PATCH] feat(users): add search endpoint for adding new user to workspace by mobile --- apps/users/api/serializers.py | 12 ++++++++++++ apps/users/api/urls.py | 1 + apps/users/api/views.py | 23 +++++++++++++++++++++++ apps/workspaces/api/serializers.py | 29 +++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+) diff --git a/apps/users/api/serializers.py b/apps/users/api/serializers.py index 710616a..258dd5e 100644 --- a/apps/users/api/serializers.py +++ b/apps/users/api/serializers.py @@ -143,3 +143,15 @@ class UserProfileSerializer(BaseModelSerializer): "is_verified", "full_name", "age" ) read_only_fields = BaseModelSerializer.Meta.fields + ("mobile", "is_verified") + + +class UserSearchSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ( + 'id', + 'first_name', + 'last_name', + 'mobile', + 'profile_picture', + ) diff --git a/apps/users/api/urls.py b/apps/users/api/urls.py index e739b25..02e09a1 100644 --- a/apps/users/api/urls.py +++ b/apps/users/api/urls.py @@ -24,6 +24,7 @@ urlpatterns = [ 'patch': 'partial_update', 'delete': 'destroy' }), name='user-me'), + path('search/', views.UserSearchAPIView.as_view(), name='user-search'), path("token/obtain/", TokenObtainPairView.as_view(), name="token_obtain_pair"), path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"), ] diff --git a/apps/users/api/views.py b/apps/users/api/views.py index d9cd804..cc3ead8 100644 --- a/apps/users/api/views.py +++ b/apps/users/api/views.py @@ -28,6 +28,7 @@ from apps.users.api.serializers import ( TokenPairSerializer, RegisterWithPasswordSerializer, UserProfileSerializer, + UserSearchSerializer, ) from apps.users.services.auth import ( register_user_with_password, @@ -246,3 +247,25 @@ class UserProfileViewSet(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin def get_object(self): return self.request.user + + +class UserSearchAPIView(APIView): + permission_classes = [IsAuthenticated] + + def get(self, request): + mobile = request.query_params.get('mobile') + if not mobile: + return Response( + {"detail": "Mobile parameter is required."}, + status=status.HTTP_400_BAD_REQUEST + ) + + user = User.objects.filter(mobile=mobile).first() + if not user: + return Response( + {"detail": "User not found."}, + status=status.HTTP_404_NOT_FOUND + ) + + serializer = UserSearchSerializer(user, context={"request": request}) + return Response(serializer.data, status=status.HTTP_200_OK) \ No newline at end of file diff --git a/apps/workspaces/api/serializers.py b/apps/workspaces/api/serializers.py index eda58c4..5d7967f 100644 --- a/apps/workspaces/api/serializers.py +++ b/apps/workspaces/api/serializers.py @@ -4,13 +4,42 @@ from core.serializers.base import BaseModelSerializer from apps.workspaces.models import Workspace, WorkspaceMembership +class WorkspaceMemberInputSerializer(serializers.Serializer): + user_id = serializers.UUIDField() + role = serializers.ChoiceField(choices=WorkspaceMembership.Role.choices, default=WorkspaceMembership.Role.MEMBER) + + class WorkspaceSerializer(BaseModelSerializer): + members = WorkspaceMemberInputSerializer(many=True, write_only=True, required=False) + class Meta: model = Workspace fields = BaseModelSerializer.Meta.fields + ( "name", "description", + "members", ) + + def create(self, validated_data): + members_data = validated_data.pop('members', []) + + workspace = super().create(validated_data) + + memberships_to_create = [] + for member in members_data: + memberships_to_create.append( + WorkspaceMembership( + workspace=workspace, + user_id=member['user_id'], + role=member.get('role', 'member'), + is_active=True + ) + ) + + if memberships_to_create: + WorkspaceMembership.objects.bulk_create(memberships_to_create) + + return workspace class WorkspaceMembershipSerializer(BaseModelSerializer):