پروژه ای دارم به شرح زیر

from django.contrib.auth import get_user_model
from rest_framework import viewsets, status
from rest_framework.response import Response
from rest_framework_simplejwt.tokens import RefreshToken
from app_account.models import CustomUser
from app_account.serializers import UserSerializer, RequestOTPSerializer, VerifyOtpRequestSerializer, ObtainTokenSerializer
from rest_framework.decorators import action
import random
import string
from django.core.cache import cache
from .msg_sender import send_otp
from datetime import datetime, time, timedelta

# region ==> OTP

def get_difference_secound():
    # گرفتن زمان فعلی
    now = datetime.now()

    # تعیین زمان نیمه‌شب (ساعت 12 شب)
    midnight = datetime.combine(now.date(), time(0, 0))

    # اگر زمان فعلی بعد از نیمه‌شب است (در روز بعد) زمان نیمه‌شب روز بعد را بگیرید
    if now > midnight:
        midnight = datetime.combine(now.date() + timedelta(days=1), time(0, 0))

    # محاسبه اختلاف زمان
    difference = midnight - now
    sec_difference = int(difference.total_seconds()//1)

    return sec_difference - 10

def set_data_dic(data):

    if isinstance(data, dict):
        body_data = data
    else:
        body_data = data.dict()

    return body_data

class OTPViewSet(viewsets.ViewSet):

    # TODO: set national id exist

    @action(detail=False, methods=['post'])
    def request_otp(self, request):

        body_data = set_data_dic(request.data)

        serializer = RequestOTPSerializer(data=body_data)
        if serializer.is_valid():
            data = serializer.validated_data
            otp = self._generate_otp()
            
            # بررسی و به‌روزرسانی شمارنده در کش
            counter_otp = cache.get(f"otp_{data['phone_num']}_count")
            if counter_otp is None:
                cache.set(f"otp_{data['phone_num']}_count", 1, timeout=get_difference_secound())
            elif counter_otp is not None:
                if counter_otp <= 500:
                    cache.set(f"otp_{data['phone_num']}_count", counter_otp+1, timeout=get_difference_secound())
                else:
                    return Response({
                        'status': 'LIMIT_REACHED',
                        'message': 'شما به محدودیت تعداد کد در امروز رسیده‌اید. لطفاً فردا دوباره تلاش کنید.',
                        'suggestion': 'در صورت نیاز مبرم به دریافت کد، با پشتیبانی تماس بگیرید.',
                        'phone_num': data['phone_num']
                    }, status=status.HTTP_429_TOO_MANY_REQUESTS)
            
            # ذخیره و ارسال OTP
            cache.set(f"otp_{data['phone_num']}", otp, timeout=120)
            send_otp(phone_number=data['phone_num'], password=otp)
            
            return Response({
                'status': 'OTP_SENT',
                'message': 'کد تأیید با موفقیت ارسال شد.',
                'phone_num': data['phone_num']
            }, status=status.HTTP_200_OK)

        # نمایش خطاهای ولیدیشن سریالایزر
        return Response({
            'status': 'BAD_REQUEST',
            'errors': serializer.errors
        }, status=status.HTTP_400_BAD_REQUEST)
    
    @action(detail=False, methods=['post'])
    def verify_otp(self, request):

        body_data = set_data_dic(request.data)

        serializer = VerifyOtpRequestSerializer(data=body_data)
        if serializer.is_valid():
            data = serializer.validated_data
            cached_otp = cache.get(f"otp_{data['phone_num']}")

            if cached_otp and cached_otp == data['password']:
                # کد تایید صحیح است
                cache.delete(f"otp_{data['phone_num']}")
                return Response({
                    'status': 'SUCCESS',
                    'message': 'کد تأیید صحیح است و شما با موفقیت وارد سیستم شدید.',
                    'user_data': self._handle_login(data['national_id'])
                }, status=status.HTTP_200_OK)

            return Response({
                'status': 'INVALID_OTP',
                'message': 'کد وارد شده نادرست است. لطفاً مجدد تلاش کنید.',
            }, status=status.HTTP_401_UNAUTHORIZED)

        # نمایش خطاهای ولیدیشن سریالایزر
        return Response({
            'status': 'BAD_REQUEST',
            'message': 'درخواست نامعتبر است. لطفاً خطاها را اصلاح کرده و مجدد تلاش کنید.',
            'errors': serializer.errors
        }, status=status.HTTP_400_BAD_REQUEST)

    def _handle_login(self, national_id):
        User = get_user_model()
        query = User.objects.filter(username=national_id)
        created = False
        if query.exists():
            user = query.first()
        else:
            # ایجاد کاربر جدید
            user = User.objects.create(username=national_id)
            created = True

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

    def _generate_otp(self):
        return ''.join(random.choices(string.digits, k=4))
    
    
class UsersViewSetApiView(viewsets.ModelViewSet):
    queryset = CustomUser.objects.all()
    serializer_class = UserSerializer


from rest_framework import serializers
from app_account.models import CustomUser
from django.db import models

class RequestOTPSerializer(serializers.Serializer):
    class ChannelChoices(models.TextChoices):
        PHONE = 'phone', 'موبایل'
        EMAIL = 'email', 'ایمیل'
    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=ChannelChoices.choices)

class VerifyOtpRequestSerializer(serializers.Serializer):
    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']


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


# region ==> Custom User Model
class CustomUser(AbstractUser):
    
    class GenderChoices(models.TextChoices):
        MALE = 'male', "مرد"
        FEMALE = 'female', "زن"
        OTHER = 'other', "دیگر"

    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=GenderChoices.choices, 
        verbose_name='جنسیت'
    )
    birth_date = models.DateField(
        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()
        valid_time_range = current_time - timedelta(seconds=120)
        return self.filter(
            national_id=national_id,
            phone_num=phone_num,
            request_id=request,
            password=password,
            created__lt=current_time,
            created__gt=valid_time_range
        ).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)


# endregion


میخوام برای هرکاربر محدودیت ورود 3 تا دستگاه بزارم با یه فینگرپرینت که از سمت جاوا اسکریپت ارسال میشه باید چطور عمل کنم ؟