TypeError: '<' не поддерживается между экземплярами 'CombinedExpression' и 'int' при попытке реализовать сигнал post_save с помощью django-axes

Я пытаюсь создать сигнал, который отправляет письмо с информацией о попытке доступа, когда пользователь не может предоставить правильные учетные данные при входе в систему. Попытки доступа отслеживаются пакетом django-axes. Если достигнуто значение failure_limit, используется другой сигнал. Сравнение обоих значений определяет, используется ли этот сигнал в данный момент.

Ошибка типа возникает в failures = instance.failures_since_start.

from axes.models import AccessAttempt
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.core.mail import mail_admins
from django.template import loader
from tajnanorka.settings import AXES_FAILURE_LIMIT

@receiver(post_save, sender=AccessAttempt)
def login_attempt_email(sender, instance, **kwargs):

    def _render_email_message(context):
        template = loader.get_template("foo.html")
        return template.render(context)
   
    failures = instance.failures_since_start
    failure_limit = int(AXES_FAILURE_LIMIT)
    

    context = dict(
        failures_since_start = failures,
    )

    subject = 'bar'
    message = _render_email_message(context)

    if failures < failure_limit:
        mail_admins(
            subject,
            message,
            fail_silently=False,
            html_message = message
        )

Модель AccessAttempt:

class AccessBase(models.Model):
    user_agent = models.CharField(_("User Agent"), max_length=255, db_index=True)

    ip_address = models.GenericIPAddressField(_("IP Address"), null=True, db_index=True)

    username = models.CharField(_("Username"), max_length=255, null=True, db_index=True)

    http_accept = models.CharField(_("HTTP Accept"), max_length=1025)

    path_info = models.CharField(_("Path"), max_length=255)

    attempt_time = models.DateTimeField(_("Attempt Time"), auto_now_add=True)

    class Meta:
        app_label = "axes"
        abstract = True
        ordering = ["-attempt_time"]

class AccessAttempt(AccessBase):
    get_data = models.TextField(_("GET Data"))

    post_data = models.TextField(_("POST Data"))

    failures_since_start = models.PositiveIntegerField(_("Failed Logins"))

    def __str__(self):
        return f"Attempted Access: {self.attempt_time}"

    class Meta:
        verbose_name = _("access attempt")
        verbose_name_plural = _("access attempts")
        unique_together = [["username", "ip_address", "user_agent"]]

Вот весь трассировочный бэкграунд:

Я пытался преобразовать failures в int(), но не знаю, что делать дальше. Есть ли способ сравнить эти два значения?

Эта ошибка возникает потому, что instance.failures_since_start возвращает не целое число, а CombinedExpression.

Чтобы исправить это, нужно убедиться, что failures_since_start оценивается как целое число перед сравнением с failure_limit.

Попробуйте следующее:

failures = int(instance.failures_since_start)
failure_limit = int(AXES_FAILURE_LIMIT)

Вы не можете использовать instance, или, по крайней мере, не напрямую, поскольку он использует F выражение [Django-doc] для обновления счетчика.

Используйте .refresh_from_db(…) [Django-doc] для перечитывания счетчика из базы данных:

@receiver(post_save, sender=AccessAttempt)
def login_attempt_email(sender, instance, **kwargs):

    def _render_email_message(context):
        template = loader.get_template('foo.html')
        return template.render(context)

    instance.refresh_from_db()
    failures = instance.failures_since_start
    failure_limit = int(AXES_FAILURE_LIMIT)

    context = dict(
        failures_since_start=failures,
    )

    subject = 'bar'
    message = _render_email_message(context)

    if failures < failure_limit:
        mail_admins(subject, message, fail_silently=False, html_message=message)
Вернуться на верх