Оформление класса модели в django с сохранением в декораторе

#python #django #django-models #decorator

#python #django #django-модели #декоратор

Вопрос:

У меня есть несколько моделей, которые сохраняют данные журнала в моей базе данных. У меня также есть приложение «недавние события», и я хотел бы выбрать, какие модели отправляют данные в приложение events. Я подумал, что для этого подойдет декоратор, поэтому я мог бы просто добавить его к нужным моделям:

 @logger
class TemperatureLog(models.Model):
    Date = models.DateTimeField(auto_now_add=True)
    Device = models.ForeignKey(TemperatureDevice)
    Data = models.PositiveIntegerField()
  

Вот модель событий, я использую общие внешние ключи:

 class Event(models.Model):
    Active = models.BooleanField()
    Queue = models.BooleanField()
    ContentType = models.ForeignKey(ContentType)
    ObjectID = models.PositiveIntegerField()
    Event = generic.GenericForeignKey('ContentType', 'ObjectID')
  

И вот декоратор:

 def logger(event):
    def wrap(*args, **kwargs):
        from toolbox.event.models import Event
        event(*args, **kwargs).save()
        myid = event(*args, **kwargs).id
        new = Event(Event=event.objects.get(id=myid))

        if Event.objects.all().filter(Active=True).count() >= 25:
            new.Queue = True
            new.save()

        else:
            new.Active = True
            new.save()

            for item in Event.objects.all().filter(Queue=True):
                item.Queue = False
                item.Active = True
                item.save()

                if  Event.objects.all().filter(Active=True).count() >= 25:
                    break
        return event(*args, **kwargs)

    return wrap
  

Он работает так, как должен, он создает экземпляр события и сохраняет его. Проблема, с которой я сталкиваюсь, заключается в том, что save() будет вызываться дважды. Один в декораторе, а второй в фактическом коде, который собирает журналы температуры (поскольку я не буду заранее знать, какие приложения будут отправлять события, а какие нет, или если они могут измениться в будущем). Поэтому мне интересно, есть ли более элегантный способ сделать это. Мне нравится подход декоратора, поскольку все, что мне нужно сделать, это добавить его в класс модели, но я не очень уверен в том, что save вызывается дважды.

Ответ №1:

Ответ «в принципе» на ваш вопрос заключается в том, чтобы рассмотреть возможность использования pre_save сигнала, встроенного в Django.

По сути, вы подключаете функцию прослушивателя к pre_save сигналу, полностью документированную по ссылке выше, и вы можете изменять нужные свойства в своем экземпляре модели. Только после завершения выполнения вашего слушателя (а также любых других слушателей, подключенных к pre_save этой модели) экземпляр модели будет сохранен в базе данных.

Если я правильно понимаю ваш код, вы хотите, чтобы ваша Queue переменная была установлена True тогда и только тогда, когда в базе данных есть 25 или более записей активных событий, и False в противном случае (с противоположным для Active — зачем вам нужны два логических значения, я не понимаю). Вы могли бы сделать это с помощью сигналов, выполнив что-то вроде этого…

 from django.db.signals import pre_save

def update_event_active_queue_status(sender, instance=None, **kwargs):
    if Event.objects.filter(Active=True).count() >= 25:
        instance.Queue = True
    else:
        instance.Active = True
pre_save.connect(update_event_active_queue_status, sender=Event)
  

Отдельная проблема, которую вы пытаетесь решить, и я не думаю, что это подходящее место для нее, — это перемещение событий в очереди обратно в режим active, когда количество активных событий падает ниже 25. Я не знаю ваших точных потребностей, но я бы, вероятно, сделал это на задании cron или каким-либо другим менеджером событий, а не рассматривал здесь. Прямо сейчас, если в систему не добавлены события (или не изменены каким-либо другим способом), элементы никогда не будут извлечены из очереди. Вероятно, это не то, что вы хотите.

Конечно, вы знаете свои потребности лучше, чем я, поэтому отнеситесь к моим рекомендациям с недоверием.

Ответ №2:

Как насчет использования сигнала post_save для всех моделей

 def log_saved_event(sender, instance, signal, *args, **kwargs):
    # handle Event class
    pass

from django.db.models import signals
from django.db import models

for m in models.get_models():
    signals.post_save.connect(log_saved_event, sender=m)