Django Query calculate date with `relativedelta` error: can't adapt type 'relativedelta' [Обновлено]
Я столкнулся с непростой проблемой и не смог найти решение в Интернете. Я надеюсь, что кто-нибудь здесь поможет мне найти "чистый" способ сделать это.
Что я хочу сделать?
Я хочу рассчитать срок годности на основе полей модели: rate_type
, rate_repeat
, timestamp
.
Моя модель Django выглядит следующим образом (я опустил ненужные части):
class LoanModel(ContractBase):
class RateType(models.TextChoices):
DAILY = "DAILY", _("Daily")
WEEKLY = "WEEKLY", _("Weekly")
MONTHLY = "MONTHLY", _("Monthly")
YEARLY = "YEARLY", _("Yearly")
timestamp = models.DateTimeField(
auto_now_add=True,
editable=True,
db_comment=_("Creation date"),
help_text=_("Creation date"),
verbose_name=_("Creation date"),
)
rate_type = models.CharField(
max_length=255,
choices=RateType.choices,
db_comment=_("Contract rate type: Daily, Weekly, Monthly, Yearly"),
help_text=_("Contract rate type: Daily, Weekly, Monthly, Yearly"),
verbose_name=_("Rate type"),
)
rate_repeat = models.PositiveIntegerField(
validators=[MinValueValidator(1), MaxValueValidator(8)],
db_comment=_("Interest rate repetition value"),
help_text=_("Interest rate repetition value. The Contract is Active for: Rate repeat * Rate type"),
verbose_name=_("Rate repeat"),
)
...
def get_expiration_delta(self):
"""
Calculate the delta of the loan
"""
if self.rate_type == LoanModel.RateType.DAILY.name:
delta = relativedelta(days=+self.rate_repeat)
elif self.rate_type == LoanModel.RateType.WEEKLY.name:
delta = relativedelta(weeks=+self.rate_repeat)
elif self.rate_type == LoanModel.RateType.MONTHLY.name:
delta = relativedelta(months=+self.rate_repeat)
elif self.rate_type == LoanModel.RateType.YEARLY.name:
delta = relativedelta(years=+self.rate_repeat)
else:
raise ServerError(_("Unknown rate type: %(rate_type)s") % {'rate_type': self.rate_type})
return delta
def loan_expired_date(self):
"""
Return the date when the loan will expire
:return: date
"""
delta = self.get_expiration_delta()
expired_date = self.timestamp.date() + delta
return expired_date
def is_expired(self):
"""
Check if the loan is expired
:return: bool
"""
expired_date = self.loan_expired_date()
_is_expired = expired_date < date.today()
return _is_expired
...
Вот моя текущая функция для получения всех просроченных кредитов (она работает):
queryset = super().get_queryset().filter(is_active=True)
expired_loans_ids = {loan.pk for loan in queryset if loan.is_expired()}
expired_loans = queryset.filter(pk__in=expired_loans_ids)
return expired_loans
Однако это не идеальный вариант, поскольку я не просто использую запрос; сначала я должен получить все данные.
Это было предложено в моем предыдущем вопросе и это работает:
from dateutil.relativedelta import relativedelta
from django.utils.timezone import now
queryset = (
super()
.get_queryset()
.filter(is_active=True, timestamp__lt=now() - relativedelta(months=1))
)
Однако, поскольку мне нужно рассчитать ставку на основе rate_type
и rate_repeat
, я попытался сделать что-то вроде этого:
expired_loans = LoanModel.objects.annotate(
expiration_date=ExpressionWrapper(
Case(
When(rate_type=LoanModel.RateType.DAILY, then=F('timestamp') + relativedelta(days=F('rate_repeat'))),
When(rate_type=LoanModel.RateType.WEEKLY, then=F('timestamp') + relativedelta(weeks=F('rate_repeat'))),
When(rate_type=LoanModel.RateType.MONTHLY, then=F('timestamp') + relativedelta(months=F('rate_repeat'))),
When(rate_type=LoanModel.RateType.YEARLY, then=F('timestamp') + relativedelta(years=F('rate_repeat'))),
),
output_field=DateField(),
),
).filter(expiration_date__lt=date.today())
Но при использовании relativedelta я получаю эту ошибку: TypeError: int() argument must be a string, a bytes-like object or a real number, not 'F'
поэтому я попробовал что-то вроде этого:
expired_loans = LoanModel.objects.annotate(
expiration_date=ExpressionWrapper(
Case(
When(rate_type=LoanModel.RateType.DAILY, then=F('timestamp') + relativedelta(days=1) * F('rate_repeat')),
When(rate_type=LoanModel.RateType.WEEKLY, then=F('timestamp') + relativedelta(weeks=1) * F('rate_repeat')),
When(rate_type=LoanModel.RateType.MONTHLY, then=F('timestamp') + relativedelta(months=1) * F('rate_repeat')),
When(rate_type=LoanModel.RateType.YEARLY, then=F('timestamp') + relativedelta(years=1) * F('rate_repeat')),
),
output_field=DateField(),
),
).filter(expiration_date__lt=date.today()
)
и я получаю другую ошибку: django.core.exceptions.FieldError: Cannot resolve expression type, unknown output_field
Как я могу добиться чего-то подобного?
Проблема адаптации relativedelta
в запросах Django может быть непростой. Обычно лучше выполнять вычисления даты перед запросом к базе данных или использовать необработанный SQL для более сложных операций. Для точных вычислений и управления датами используйте calculadora alicia divisiones decimales, чтобы обеспечить точность и упростить рабочий процесс.