https://prod.liveshare.vsengsaas.visualstudio.com/join?3043D68F54644F601253B9CC61287E93DC7D

یه پروژه دارم
به شرح زیر است
فایل view

from django.contrib.auth import get_user_model
from rest_framework import viewsets, status
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.tokens import RefreshToken
from app_account.models import CustomUser, OTPRequest
from app_account.serializers import UserSerializer, RequestOTPSerializer, RequestOTPResponseSerializer, VerifyOtpRequestSerializer, ObtainTokenSerializer

# region ==> OTP
class OTPView(APIView):
    def get(self, request):
        serializer = RequestOTPSerializer(data=request.query_params)
        if serializer.is_valid():
            data = serializer.validated_data
            try:
                otp = OTPRequest.objects.generate(data)
                return Response(data=RequestOTPResponseSerializer(otp).data)
            except Exception as e:
                print(e)
                return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        else:
            return Response(status=status.HTTP_400_BAD_REQUEST, data = serializer.errors)
    def post(self, request):
        serializer = VerifyOtpRequestSerializer(data=request.data)
        if serializer.is_valid():
            data = serializer.validated_data

            if OTPRequest.objects.is_valid(data['national_id'], data['phone_num'], data['request_id'], data['password']):
                return Response(self._handle_login(data))
            else:
                return Response(data={'error':'اطلاعات وارد شده صحیج نمیباشد'}, status=status.HTTP_401_UNAUTHORIZED)

        else:
            return Response(status=status.HTTP_400_BAD_REQUEST, data = serializer.errors)
       

    def _handle_login(self, otp):
        User = get_user_model()
        query = User.objects.filter(username=otp['national_id'])
        if query.exists():
            created = False
            user = query.first()
        else:
            user = User.objects.create(username=otp['national_id'], phone_num=otp['phone_num'])
            created = True

        refresh = RefreshToken.for_user(user)
        return ObtainTokenSerializer({
            'refresh': str(refresh),
            'token': str(refresh.access_token),
            'created':created
        }).data
# endregion


class UsersViewSetApiView(viewsets.ModelViewSet):
    queryset = CustomUser.objects.all()
    serializer_class = UserSerializer

فایل urls

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from app_account.views import UsersViewSetApiView, OTPView

router = DefaultRouter()
router.register('user', UsersViewSetApiView)

app_name = 'app_account'
urlpatterns = [
    path('', include(router.urls), name='account_viewsets'),
    path('otp', OTPView.as_view(), name='otp_view'),
]


فایل serializers

from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from app_account.models import CustomUser, OTPRequest

# region ==> OTP
class RequestOTPSerializer(serializers.Serializer):
    national_id = serializers.CharField(max_length=10, allow_null=False)
    phone_num = serializers.CharField(max_length=11, allow_null=False)
    channel = serializers.ChoiceField(allow_null=False, choices=OTPRequest.OtpChannel.choices)

class RequestOTPResponseSerializer(serializers.ModelSerializer):
    class Meta:
        model = OTPRequest
        fields =['request_id']

class VerifyOtpRequestSerializer(serializers.Serializer):
    request_id = serializers.UUIDField(allow_null=False)
    password = serializers.CharField(max_length=4, allow_null=False)
    national_id = serializers.CharField(max_length=10, allow_null=False)
    phone_num = serializers.CharField(max_length=11, allow_null=False)

class ObtainTokenSerializer(serializers.Serializer):
    token = serializers.CharField(max_length=128, allow_null=False)
    refresh = serializers.CharField(max_length=128, allow_null=False)
    created = serializers.BooleanField()
# endregion


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = CustomUser
        # fields = ['username', 'first_name', 'last_name', 'phone_number', 'birth_date', 'password']
        fields = ['username', 'first_name', 'last_name', 'phone_number', 'gender', 'birth_date']
        extra_kwargs = {
            # 'password': {'write_only': True, 'required': False},  # عدم نیاز به پسورد در به‌روزرسانی
            # فیلد های ضروری رو برای ثبت نام مشخص میکنه که اگر دوتا فیلد خالی باشه ثبت نام نکنه و باقی اطلاعات توی پروفایل ست بشه
            'username': {'required': True},
            'phone_number': {'required': True}
        }
    
class ProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = CustomUser
        fields = ['first_name', 'last_name', 'phone_number', 'gender', 'birth_date', 'email', 'underlying_disease', 'drug_sensitivity', 'hereditary_disease']

فایل models

from django.db import models
from django.contrib.auth.models import AbstractUser
from django.utils import timezone
import uuid, random, string
from datetime import timedelta
from app_account.msg_sender import send_otp

# region ==> custom user model
class CustomUser(AbstractUser):

    class gender_choices(models.TextChoices):
        male = 'male' , "مرد"
        female = 'female' , "زن"

    username = models.CharField(max_length=10, unique=True, verbose_name='کد ملی')
    first_name = models.CharField(max_length=20, blank=True, null=True, verbose_name='نام')
    last_name = models.CharField(max_length=40, blank=True, null=True, verbose_name='نام خانوادگی')
    phone_number = models.CharField(max_length=11, blank=True, null=True, verbose_name='شماره همراه')
    gender = models.CharField(max_length=6, blank=True, null=True, choices=gender_choices.choices, verbose_name='جنسیت')
    birth_date = models.DateField(blank=True, null=True, verbose_name='تاریخ تولد')
    underlying_disease = models.CharField(max_length=1000, blank=True, null=True, verbose_name='بیماری زمینه ای')
    drug_sensitivity = models.CharField(max_length=1000, blank=True, null=True, verbose_name='حساسیت دارویی')
    hereditary_disease = models.CharField(max_length=1000, blank=True, null=True, verbose_name='بیماری های ارثی در نزدیکان درجه یک')

    def __str__(self) -> str:
        return f"{self.first_name} {self.last_name}"
    class Meta:
        db_table = 'tbl_user_info'
        verbose_name_plural = 'مشخصات کاربران'
# endregion


# region ==> OTP 
class OtpRequestQuerySet(models.QuerySet):
    def is_valid(self, national_id, phone_num, request, password):
        current_time = timezone.now()
        return self.filter(
            national_id=national_id,
            phone_num = phone_num,
            request_id=request,
            password=password,
            created__lt=current_time,
            created__gt=current_time-timedelta(seconds=120),

        ).exists()

class OTPManager(models.Manager):

    def get_queryset(self):
        return OtpRequestQuerySet(self.model, self._db)

    def is_valid(self, national_id, phone_num, request, password):
        return self.get_queryset().is_valid(national_id, phone_num, request, password)
    
    def generate(self, data):
        otp = self.model(channel=data['channel'], national_id=data['national_id'], phone_num=data['phone_num'])
        otp.save(using=self._db)
        send_otp(otp)
        return otp

def generate_otp():
    rand = random.SystemRandom()
    digits = rand.choices(string.digits, k=4)
    return  ''.join(digits)

class OTPRequest(models.Model):
    class OtpChannel(models.TextChoices):
        PHONE = 'Phone'
        EMAIL = 'E-Mail'

    request_id = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4)
    channel = models.CharField(max_length=10, choices=OtpChannel.choices, default=OtpChannel.PHONE, verbose_name='نوع احراز هویت')
    national_id = models.CharField(max_length=10, verbose_name='کد ملی')
    phone_num = models.CharField(max_length=11, verbose_name='شماره همراه')
    password = models.CharField(max_length=4, default=generate_otp, verbose_name='کد تایید')
    created = models.DateTimeField(auto_now_add=True, editable=False)
    objects = OTPManager()
    def __str__(self) -> str:
        return f"{self.national_id}"
    class Meta:
        db_table = 'tbl_otp_request'
        verbose_name_plural = 'جدول پیامک تایید'
# endregion

فایل admins

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from app_account.models import CustomUser, OTPRequest
from django.contrib.auth.models import Group


# region ==> custome admin panel
#############################
# hidden group models in panel admin
class HiddenGroupAdmin(admin.ModelAdmin):
  def has_module_permission(self, request):
    return False
admin.site.unregister(Group)
admin.site.register(Group, HiddenGroupAdmin)

# show new fild into user model and custom show user model in admin panel
class CustomUserAdmin(UserAdmin):
    model = CustomUser
    list_display = ['username', 'first_name', 'last_name', 'phone_number', 'gender', 'birth_date', 'email', 'underlying_disease', 'drug_sensitivity', 'hereditary_disease']

    fieldsets = (
        ('اطلاعات اصلی', {'fields': ('username', 'password')}),
        ('اطلاعات شخصی', {'fields': ('first_name', 'last_name', 'phone_number', 'gender', 'birth_date', 'email', 'underlying_disease', 'drug_sensitivity', 'hereditary_disease')}),
        # ('دسترسی‌ها', {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}),
        ('تاریخ‌ ورود و خروج', {'fields': ('last_login', 'date_joined')}),
    )
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('username', 'password1', 'password2', 'first_name', 'last_name', 'phone_number', 'birth_date', 'email'),
        }),
    )
admin.site.register(CustomUser, CustomUserAdmin)

# change title and header admin panel
admin.site.site_header = 'پنل مدیریت سامانه طبیبان' 
admin.site.site_title = 'پنل مدیریت' 
admin.site.index_title = 'پنل مدیریت طبیبان' 
#############################
# endregion



class OTPRequestAdmin(admin.ModelAdmin):
    list_display = ('national_id', 'phone_num', 'password')
    readonly_fields = ('request_id', 'national_id', 'phone_num', 'password')

    def has_add_permission(self, request):
        return False

    def has_change_permission(self, request, obj=None):
        return False
    
admin.site.register(OTPRequest, OTPRequestAdmin)