من مدل هام به شرح زیر است

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
class DeviceFingerprint(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='device_fingerprints')
fingerprints = models.CharField(max_length=50, blank=True, null=True, verbose_name='دستگاه')


def __str__(self) -> str:
    return f"{self.user} {self.fingerprints}"

class Meta:
    db_table = 'tbl_user_devices_info'
    verbose_name_plural = 'آیدی دستگاه های کاربران'
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
سریالایزر هام به شرح زیر

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)
uniq_id_device = serializers.CharField(max_length=32, 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
ویو هام به شرح زیر

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 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
from .models import DeviceFingerprint
from rest_framework.exceptions import ValidationError

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):


@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

        # جستجوی کاربر با کد ملی
        existing_user = CustomUser.objects.filter(username=data['national_id']).first()
        if existing_user and existing_user.phone_number != data['phone_num']:
            raise ValidationError({
                'status': 'PHONE_NUMBER_DIFFERENT',
                'message': 'این کد ملی قبلاً با یک شماره تلفن دیگر ثبت‌نام شده است.',
            })
        
        # ادامه روند
        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)
        print(data['phone_num'], 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']:
            user, created = self._get_or_create_user(data['national_id'], data['phone_num'])

            # اعتبارسنجی فینگرپرینت دستگاه
            if data['uniq_id_device'] in user.device_fingerprints.all():
                token_data = self._generate_tokens_for_user(user, created)
                
                return Response({
                    'status': 'SUCCESS',
                    'message': 'کد تأیید صحیح است و شما با موفقیت وارد سیستم شدید.',
                    'user_data': token_data
                }, status=status.HTTP_200_OK)
            
            elif len(user.device_fingerprints.all()) < 3:
                
                fingerprint, created = DeviceFingerprint.objects.get_or_create(
                user=user,
                fingerprints=data['uniq_id_device']
                )

                if created:
                    fingerprint.save()
                    
                token_data = self._generate_tokens_for_user(user, created)

                return Response({
                    'status': 'SUCCESS_WITH_NEW_DEVICE',
                    'message': 'ورود موفق بود و دستگاه جدید اضافه شد.',
                    'user_data': token_data
                }, status=status.HTTP_200_OK)

            else:
                return Response({
                    'status': 'DEVICE_LIMIT_REACHED',
                    'message': 'شما به محدودیت دستگاه‌ رسیده‌اید. لطفاً ابتدا دستگاه دیگری را حذف کنید.',
                }, status=status.HTTP_403_FORBIDDEN)

        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 _get_or_create_user(self, national_id, phone_num):
    User = get_user_model()
    user, created = User.objects.get_or_create(username=national_id)
    if created and user.phone_number != phone_num:
        user.phone_number = phone_num
        user.save()
    return user, created

def _generate_tokens_for_user(self, user, created):
    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))
و urls به شرح زیر

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from app_account.views import OTPViewSet
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView

router = DefaultRouter()
router.register('otp', OTPViewSet, basename='otp')

app_name = 'app_account'
urlpatterns = [
path('', include(router.urls), name='account_viewsets'),
########
# path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]

و این کد اچ تی ام ال رو دارم


                            <div class="card-body">
                                <div class="d-flex align-items-start align-items-sm-center gap-4">
                                    <img alt="کاربر-آواتار" class="d-block w-px-100 h-px-100 rounded" id="uploadedAvatar" src="../../assets/img/avatars/14.png">
                                    <div class="button-wrapper">
                                        <label class="btn btn-primary me-2 mb-3 waves-effect waves-light" for="upload" tabindex="0">
                                            <span class="d-none d-sm-block">آپلود عکس جدید</span>
                                            <i class="ti ti-upload d-block d-sm-none"></i>
                                            <input accept="image/png, image/jpeg" class="account-file-input" hidden="" id="upload" type="file">
                                        </label>
                                        <button class="btn btn-label-secondary account-image-reset mb-3 waves-effect" type="button">
                                            <i class="ti ti-refresh-dot d-block d-sm-none"></i>
                                            <span class="d-none d-sm-block">حذف عکس</span>
                                        </button>
                                        <div class="text-muted">حداکثر 5 مگابایت و با فرمت png , jpeg , jpg</div>
                                    </div>
                                </div>
                            </div>
                            <hr class="my-0">
                            <div class="card-body">
                                    <div class="row">
                                        <div class="mb-3 col-md-6 fv-plugins-icon-container">
                                            <label class="form-label" for="firstName">نام</label>
                                            <input autofocus="" class="form-control" id="firstName" name="firstName" type="text" placeholder="سامان">
                                        <div class="fv-plugins-message-container fv-plugins-message-container--enabled invalid-feedback"></div></div>
                                        <div class="mb-3 col-md-6 fv-plugins-icon-container">
                                            <label class="form-label" for="lastName">نام خانوادگی</label>
                                            <input class="form-control" id="lastName" name="lastName" type="text" placeholder="پاک سرشت">
                                        <div class="fv-plugins-message-container fv-plugins-message-container--enabled invalid-feedback"></div></div>
                                        <div class="mb-3 col-md-6">
                                            <label class="form-label" for="national-id">کد ملی</label>
                                            <input class="form-control ltr" id="national-id" name="national-id" placeholder="0012345678" type="text">
                                        </div>
                                        <div class="mb-3 col-md-6">
                                            <label class="form-label" for="phoneNumber">شماره تلفن</label>
                                            <div class="input-group input-group-merge">
                                                <input class="form-control ltr" id="phoneNumber" name="phoneNumber" placeholder="0910 000 0000" type="text">
                                            </div>
                                        </div>
                                        <div class="mb-3 col-md-6">
                                            <label class="form-label" for="email">ایمیل</label>
                                            <input class="form-control ltr" id="email" name="email" placeholder="john.doe@example.com" type="text">
                                        </div>
                                        <div class="mb-3 col-md-6">
                                            <label class="form-label" for="state">استان</label>
                                            <input class="form-control" id="state" name="state" placeholder="تهران" type="text">
                                        </div>
                                        <div class="mb-3 col-md-6">
                                            <label class="form-label" for="state">استان</label>
                                            <input class="form-control" id="state" name="state" placeholder="تهران" type="text">
                                        </div>
                                        <div class="mb-3 col-md-6">
                                            <label class="form-label" for="city">شهر</label>
                                            <input class="form-control" id="city" name="city" placeholder="تهران" type="text">
                                        </div>
                                        <div class="mb-3 col-md-6">
                                            <label class="form-label" for="address">آدرس</label>
                                            <input class="form-control" id="address" name="address" placeholder="ایران ، تهران ..." type="text">
                                        </div>
                                        <div class="mb-3 col-md-6">
                                            <label class="form-label" for="zipCode">کد پستی</label>
                                            <input class="form-control" id="zipCode" maxlength="6" name="zipCode" placeholder="231465" type="text">
                                        </div>
                                    </div>
                                    <div class="mt-2">
                                        <button class="btn btn-primary me-2 waves-effect waves-light" type="submit">ذخیره تغییرات</button>
                                        <button class="btn btn-label-secondary waves-effect" type="reset">لغو</button>
                                    </div>
                                <input type="hidden">
                            </div>
                            <!-- /Account -->
در زمان ثبت نام شماره تلفن و کد ملی شخص ثبت میشه و باقی اطلاعات قراره از طریق پنل پروفایل ذخیره بشه و هر بار که کاربر به پنل پروفایلش مراجعه میکنه اطلاعاتی که از قبل ذخیره کرده رو بهش نشون بده
چطور باید بخش رست ای پی آی رو طراحی کنم ؟