diff --git a/core/apps/accounts/admin/__init__.py b/core/apps/accounts/admin/__init__.py index 82da278..1fbe2dc 100644 --- a/core/apps/accounts/admin/__init__.py +++ b/core/apps/accounts/admin/__init__.py @@ -1 +1,2 @@ -from .user import * \ No newline at end of file +from .user import * +from .role import * \ No newline at end of file diff --git a/core/apps/accounts/admin/role.py b/core/apps/accounts/admin/role.py new file mode 100644 index 0000000..6eb3e16 --- /dev/null +++ b/core/apps/accounts/admin/role.py @@ -0,0 +1,11 @@ +# django +from django.contrib import admin + +# accounts +from core.apps.accounts.models import Role + + +@admin.register(Role) +class RoleAdmin(admin.ModelAdmin): + list_display = ['id', 'name'] + search_fields = ['name'] \ No newline at end of file diff --git a/core/apps/accounts/migrations/0002_role_user_role.py b/core/apps/accounts/migrations/0002_role_user_role.py new file mode 100644 index 0000000..b08aa2d --- /dev/null +++ b/core/apps/accounts/migrations/0002_role_user_role.py @@ -0,0 +1,32 @@ +# Generated by Django 5.2 on 2025-12-10 13:23 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Role', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('is_deleted', models.BooleanField(default=False)), + ('name', models.CharField(db_index=True, max_length=200, unique=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddField( + model_name='user', + name='role', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='users', to='accounts.role'), + ), + ] diff --git a/core/apps/accounts/models/__init__.py b/core/apps/accounts/models/__init__.py index 82da278..1fbe2dc 100644 --- a/core/apps/accounts/models/__init__.py +++ b/core/apps/accounts/models/__init__.py @@ -1 +1,2 @@ -from .user import * \ No newline at end of file +from .user import * +from .role import * \ No newline at end of file diff --git a/core/apps/accounts/models/role.py b/core/apps/accounts/models/role.py new file mode 100644 index 0000000..0b46e49 --- /dev/null +++ b/core/apps/accounts/models/role.py @@ -0,0 +1,13 @@ +# django +from django.db import models + +# shared +from core.apps.shared.models import BaseModel + + +class Role(BaseModel): + name = models.CharField(max_length=200, unique=True, db_index=True) + + def __str__(self): + return self.name + \ No newline at end of file diff --git a/core/apps/accounts/models/user.py b/core/apps/accounts/models/user.py index 7bb00d5..2e77326 100644 --- a/core/apps/accounts/models/user.py +++ b/core/apps/accounts/models/user.py @@ -9,9 +9,6 @@ from rest_framework_simplejwt.tokens import RefreshToken # shared from core.apps.shared.models import BaseModel -# customers -from core.apps.customers.models import Client - # utils from core.utils.validators.phone_number import uz_phone_validator @@ -21,6 +18,7 @@ class User(AbstractUser, BaseModel): phone_number = models.CharField( max_length=15, null=True, blank=True, validators=[uz_phone_validator] ) + role = models.ForeignKey('accounts.Role', on_delete=models.CASCADE, related_name='users', null=True) def __str__(self): return f"#{self.id}: {self.first_name} {self.last_name}" diff --git a/core/apps/accounts/serializers/user/create.py b/core/apps/accounts/serializers/user/create.py new file mode 100644 index 0000000..12d8d36 --- /dev/null +++ b/core/apps/accounts/serializers/user/create.py @@ -0,0 +1,45 @@ +# django +from django.db import transaction + +# rest framework +from rest_framework import serializers + +# accounts +from core.apps.accounts.models import User, Role + + +class CreateUserSerializer(serializers.Serializer): + profile_image = serializers.ImageField(required=False) + first_name = serializers.CharField() + last_name = serializers.CharField() + phone_number = serializers.CharField() + username = serializers.CharField() + password = serializers.CharField() + is_active = serializers.BooleanField(default=True) + role_id = serializers.IntegerField() + + def validate(self, data): + if User.objects.filter(username=data['username'], is_deleted=False).exists(): + raise serializers.ValidationError({"username": "User with this username already exists"}) + role = Role.objects.filter(id=data['role_id'], is_deleted=False).first() + if not role: + raise serializers.ValidationError({"role": "Role not found"}) + data['role'] = role + return data + + def create(self, validated_data): + with transaction.atomic(): + user = User.objects.create( + first_name=validated_data.get('first_name'), + last_name=validated_data.get('last_name'), + username=validated_data.get('username'), + phone_number=validated_data.get('phone_number'), + is_active=validated_data.get('is_active'), + profile_image=validated_data.get('profile_image'), + role=validated_data.get('role'), + ) + + user.set_password(validated_data.get('password')) + user.save() + + return user \ No newline at end of file diff --git a/core/apps/accounts/urls.py b/core/apps/accounts/urls.py index be55e8f..b3526fe 100644 --- a/core/apps/accounts/urls.py +++ b/core/apps/accounts/urls.py @@ -8,6 +8,7 @@ from rest_framework.routers import DefaultRouter # accounts # ------- user ------ from core.apps.accounts.views.user import UserViewSet +from core.apps.accounts.views.user.create import CreateUserApiView # ------- auth ------ from core.apps.accounts.views.auth.login import LoginApiView @@ -15,7 +16,7 @@ from core.apps.accounts.views.auth.login import LoginApiView urlpatterns = [ path('user/', include( [ - + path('create/', CreateUserApiView.as_view(), name='user-create-api'), ] )), # ------ authentication ------ diff --git a/core/apps/accounts/views/auth/login.py b/core/apps/accounts/views/auth/login.py index f7a3230..a11f5a3 100644 --- a/core/apps/accounts/views/auth/login.py +++ b/core/apps/accounts/views/auth/login.py @@ -20,7 +20,7 @@ class LoginApiView(generics.GenericAPIView, ResponseMixin): queryset = User.objects.all() @swagger_auto_schema( - tags=["Authentication and Authorization"], + tags=["auth"], responses={ 200: openapi.Response( description="Success", diff --git a/core/apps/accounts/views/user/create.py b/core/apps/accounts/views/user/create.py new file mode 100644 index 0000000..30d4cca --- /dev/null +++ b/core/apps/accounts/views/user/create.py @@ -0,0 +1,62 @@ +# 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.create import CreateUserSerializer +from core.apps.accounts.serializers.user.user import UserSerializer + +# utils +from core.utils.response.mixin import ResponseMixin + + +class CreateUserApiView(generics.GenericAPIView, ResponseMixin): + serializer_class = CreateUserSerializer + queryset = User.objects.all() + permission_classes = [permissions.IsAuthenticated] + parser_classes = [parsers.MultiPartParser, parsers.FormParser] + + @swagger_auto_schema( + tags=['user'], + responses={ + 201: openapi.Response( + description="Created", + schema=None, + examples={ + "application/json": { + "status": "created", + "status_code": 201, + "message": "User successfully created!", + "data": { + "id": 0, + "first_name": "string", + "last_name": "string", + "username": "string", + "phone_number": "string", + "profile_image": "string", + "created_at": "string", + "updated_at": "string", + } + } + } + ) + } + ) + def post(self, request): + try: + serializer = self.serializer_class(data=request.data) + if serializer.is_valid(raise_exception=True): + user = serializer.save() + return self.created_response( + data=UserSerializer(user).data, + message="User successfully created!" + ) + except Exception as e: + return self.error_response( + data=str(e) + ) \ No newline at end of file diff --git a/core/apps/accounts/views/user/user.py b/core/apps/accounts/views/user/user.py index dd20535..87227af 100644 --- a/core/apps/accounts/views/user/user.py +++ b/core/apps/accounts/views/user/user.py @@ -29,7 +29,7 @@ class UserViewSet(viewsets.GenericViewSet, ResponseMixin): return serializers.UserSerializer @swagger_auto_schema( - tags=['User'], + tags=['user'], operation_description="User malumotlarini olish uchun api", responses={ 200: openapi.Response(