accounts: user apis done

This commit is contained in:
behruz
2025-12-11 16:31:23 +05:00
parent e3362670d7
commit 0177084e95
5 changed files with 286 additions and 1 deletions

View File

@@ -0,0 +1,46 @@
# django
from django.db import transaction
# rest framework
from rest_framework import serializers
# accounts
from core.apps.accounts.models import User, Role
class UpdateUserSerializer(serializers.Serializer):
first_name = serializers.CharField(required=False)
last_name = serializers.CharField(required=False)
role_id = serializers.IntegerField()
phone_number = serializers.CharField(required=False)
username = serializers.CharField(required=False)
password = serializers.CharField(required=False)
is_active = serializers.BooleanField(default=True)
profile_image = serializers.ImageField(required=False)
def validate(self, data):
role = Role.objects.filter(id=data['role_id']).first()
if not role:
raise serializers.ValidationError({"role": "Role not found"})
if data.get('username'):
if User.objects.filter(username=data['username']).exists():
raise serializers.ValidationError({"username": "User with this username already exist, please try another username"})
data['role'] = role
return data
def update(self, instance, validated_data):
with transaction.atomic():
instance.first_name = validated_data.get('first_name', instance.first_name)
instance.last_name = validated_data.get('last_name', instance.last_name)
instance.role = validated_data.get('role', instance.role)
instance.phone_number = validated_data.get('phone_number', instance.phone_number)
instance.username = validated_data.get('username', instance.username)
instance.is_active = validated_data.get('is_active', instance.is_active)
instance.profile_image = validated_data.get('profile_image', instance.profile_image)
if validated_data.get('password'):
instance.set_password(validated_data.get('password'))
instance.save()
return instance

View File

@@ -10,6 +10,8 @@ from rest_framework.routers import DefaultRouter
from core.apps.accounts.views.user import UserViewSet from core.apps.accounts.views.user import UserViewSet
from core.apps.accounts.views.user.create import CreateUserApiView from core.apps.accounts.views.user.create import CreateUserApiView
from core.apps.accounts.views.user.list import ListUserApiView from core.apps.accounts.views.user.list import ListUserApiView
from core.apps.accounts.views.user.update import UpdateUserApiView
from core.apps.accounts.views.user.delete import SoftDeleteUserApiView, HardDeleteUserApiView
# ------- auth ------ # ------- auth ------
from core.apps.accounts.views.auth.login import LoginApiView from core.apps.accounts.views.auth.login import LoginApiView
@@ -19,6 +21,9 @@ urlpatterns = [
[ [
path('create/', CreateUserApiView.as_view(), name='user-create-api'), path('create/', CreateUserApiView.as_view(), name='user-create-api'),
path('list/', ListUserApiView.as_view(), name='user-list-api'), path('list/', ListUserApiView.as_view(), name='user-list-api'),
path('<int:id>/update/', UpdateUserApiView.as_view(), name='user-update-api'),
path('<int:id>/soft_delete/', SoftDeleteUserApiView.as_view(), name='user-soft-delete-api'),
path('<int:id>/hard_delete/', HardDeleteUserApiView.as_view(), name='user-soft-delete-api'),
] ]
)), )),
# ------ authentication ------ # ------ authentication ------

View File

@@ -0,0 +1,100 @@
# rest framework
from rest_framework import views, permissions
# drf yasg
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
# accounts
from core.apps.accounts.models import User
# utils
from core.utils.response.mixin import ResponseMixin
class SoftDeleteUserApiView(views.APIView, ResponseMixin):
permission_classes = [permissions.IsAuthenticated]
@swagger_auto_schema(
tags=['user'],
operation_summary="Soft delete a user by ID",
operation_description="""
Mark a user as deleted without permanently removing them from the database.
Authentication:
- Requires a valid Bearer access token.
Process:
- The system retrieves the user by the provided ID.
- If the user exists, the `is_deleted` flag is set to True.
- The user record remains in the database but is considered inactive/deleted.
Response:
- 200 OK: User successfully soft deleted.
- 404 Not Found: No user found with the given ID.
- 500 Internal Server Error: Unexpected error occurred during deletion.
Notes:
- This endpoint only marks the user as deleted and does not remove related data.
- Only authenticated users with proper permissions can perform this action.
""",
)
def delete(self, request, id):
try:
user = User.objects.filter(id=id).first()
if not user:
return self.not_found_response(message="User not found with this id")
user.is_deleted = True
user.save()
return self.deleted_response(
message="User successfully deleted",
)
except Exception as e:
return self.error_response(
data=str(e)
)
class HardDeleteUserApiView(views.APIView, ResponseMixin):
permission_classes = [permissions.IsAuthenticated]
@swagger_auto_schema(
tags=['user'],
operation_summary="Permanently delete a user by ID.",
operation_description="""
Permanently remove a user from the database by their ID.
Authentication:
- Requires a valid Bearer access token.
Process:
- The system retrieves the user by the provided ID.
- If the user exists, the user record is permanently deleted from the database.
Response:
- 200 OK: User successfully deleted.
- 404 Not Found: No user found with the given ID.
- 500 Internal Server Error: Unexpected error occurred during deletion.
Notes:
- This action permanently removes the user and cannot be undone.
- Only authenticated users with proper permissions can perform this action.
- All related data handling (foreign keys, constraints) depends on the database setup.
""",
)
def delete(self, request, id):
try:
user = User.objects.filter(id=id).first()
if not user:
return self.not_found_response(message="User not found with this id")
user.delete()
return self.deleted_response(
message="User successfully deleted",
)
except Exception as e:
return self.error_response(
data=str(e)
)

View File

@@ -0,0 +1,134 @@
# rest framework
from rest_framework import generics, permissions, parsers
# drf yasg
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
# accounts
from core.apps.accounts.models import User
from core.apps.accounts.serializers.user.update import UpdateUserSerializer
from core.apps.accounts.serializers.user.user import UserSerializer
# utils
from core.utils.response.mixin import ResponseMixin
class UpdateUserApiView(generics.GenericAPIView, ResponseMixin):
serializer_class = UpdateUserSerializer
queryset = User.objects.all()
permission_classes = [permissions.IsAuthenticated]
parser_classes = [parsers.MultiPartParser, parsers.FormParser]
@swagger_auto_schema(
tags=['user'],
operation_summary="Api for update user with id",
operation_description="""
Update the information of an existing user using their ID.
Authentication:
- Requires a valid Bearer access token.
Process:
- The system retrieves the user by the provided ID.
- If the user exists, the incoming data is validated using the UpdateUserSerializer.
- Valid fields are updated and saved.
- Returns the updated user data on success.
Request:
- Accepts partial user data (PATCH).
- Request format is multipart/form-data
- Fields can include first_name, last_name, username, phone_number, profile_image, and others defined in the serializer.
Response:
- 200 OK: Successfully updates and returns the updated user information.
- 400 Bad Request: Returned when input validation fails.
- 404 Not Found: Returned when there is no user associated with the given ID.
- 500 Internal Server Error: Returned when an unexpected error occurs.
Notes:
- Only authenticated users can access this endpoint.
- Supports partial updates, meaning not all fields are required.
""",
responses={
200: openapi.Response(
schema=None,
description="Success",
examples={
"application/json": {
"status_code": 200,
"status": "success",
"message": "User successfully updated",
"data": {
"id": 0,
"first_name": "sting",
"last_name": "sting",
"phone_number": "sting",
"username": "sting",
"profile_image": "sting",
"created_at": "sting",
"updated_at": "sting",
}
}
}
),
404: openapi.Response(
schema=None,
description="Not Found",
examples={
"application/json": {
"status_code": 404,
"status": "not_found",
"message": "User not found with given id",
"data": {}
}
}
),
400: openapi.Response(
schema=None,
description="Failure",
examples={
"application/json": {
"status_code": 400,
"status": "failure",
"message": "Kiritayotgan malumotingizni tekshirib ko'ring",
"data": "string",
}
}
),
500: openapi.Response(
schema=None,
description="Error",
examples={
"application/json": {
"status_code": 500,
"status": "error",
"message": "Xatolik, Iltimos backend dasturchiga murojaat qiling",
"data": "string",
}
}
),
}
)
def patch(self, request, id):
try:
instance = User.objects.filter(id=id).first()
if not instance:
return self.not_found_response(data={}, message="User not found with given id")
serializer = self.serializer_class(
data=request.data, instance=instance, partial=True
)
if serializer.is_valid():
updated_instance = serializer.save()
return self.success_response(
data=UserSerializer(updated_instance).data,
message="User successfully updated"
)
return self.failure_response(
data=serializer.errors,
)
except Exception as e:
return self.error_response(
data=str(e)
)

View File

@@ -46,7 +46,7 @@ class ResponseMixin:
response_data["message"] = "Kiritayotgan malumotingizni tekshirib ko'ring" response_data["message"] = "Kiritayotgan malumotingizni tekshirib ko'ring"
if data is not None: if data is not None:
response_data["data"] = data response_data["data"] = data
return return Response(response_data, status=status.HTTP_400_BAD_REQUEST)
@classmethod @classmethod
def not_found_response(cls, data=None, message=None): def not_found_response(cls, data=None, message=None):