دستورات زیر رو مرتب تر بنویس
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
from django.urls import reverse

# region ==> version app
class AppVersions(models.Model):
    version_id = models.CharField(max_length=10, verbose_name='آیدی ورژن')
    apk_file = models.FileField(upload_to='app_account/app_versions_apk/', verbose_name='فایل نصب برنامه')
    discription = models.TextField(blank=True, null=True, verbose_name='توضیحات')
    publication_date = models.DateField(blank=True, null=True, verbose_name='تاریخ انتشار')
    
    def __str__(self) -> str:
        return f"apk version-{self.version_id} - {self.publication_date}"
    
    class Meta:
        db_table = 'tbl_app_version'
        verbose_name_plural = 'ورژن های اپلیکیشن'

    def get_download_url(self):
        url_path = reverse('app_account:download_file', args=[self.id])
        if url_path.startswith('/'):
            url_path = url_path[1:]
        return url_path
# endregion

# region ==> custom user model
class CustomUser(AbstractUser):
    username = models.CharField(max_length=11, 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='نام خانوادگی')
    birth_date = models.DateField(blank=True, null=True, verbose_name='تاریخ تولد')
    gender = models.CharField(max_length=10, blank=True, null=True, verbose_name='جنسیت')
    device_id = models.CharField(max_length=50,  blank=True, null=True, verbose_name='آیدی دستگاه')
    app_version = models.CharField(max_length=10,  blank=True, null=True, verbose_name='ورژن برنامه مورد استفاده')
    avatar = models.ImageField(
        upload_to='app_account/avatar/', 
        blank=True, 
        null=True, 
        verbose_name='آواتار پروفایل'
    )
    
    def __str__(self) -> str:
        return f"{self.first_name} {self.last_name} / {self.username}"
    class Meta:
        db_table = 'tbl_user_info'
        verbose_name_plural = 'مشخصات کاربران'
# endregion

# region ==> OTP 
class OtpRequestQuerySet(models.QuerySet):
    def is_valid(self, phone_num, request, password, app_version, device_id):
        current_time = timezone.now()
        return self.filter(
            phone_num = phone_num,
            request_id=request,
            password=password,
            device_id=device_id,
            app_version=app_version,
            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, phone_num, request, password, app_version, device_id):
        return self.get_queryset().is_valid( phone_num, request, password, app_version, device_id)
    
    def generate(self, data):
        otp = self.model(
            channel=data['channel'], 
            phone_num=data['phone_num'], 
            app_version=data['app_version'],
            device_id=data['device_id'], 
            )
        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='نوع احراز هویت')
    phone_num = models.CharField(max_length=11, verbose_name='شماره همراه')
    password = models.CharField(max_length=4, default=generate_otp, verbose_name='کد تایید')
    device_id = models.CharField(max_length=50, verbose_name='آیدی دستگاه')
    app_version = models.CharField(max_length=10, verbose_name='ورژن برنامه مورد استفاده')
    created = models.DateTimeField(auto_now_add=True, editable=False)
    objects = OTPManager()
    def __str__(self) -> str:
        return f"{self.phone_num}"
    class Meta:
        db_table = 'tbl_otp_request'
        verbose_name_plural = 'جدول پیامک تایید'
# endregion