Сигнал Джанго на основе значения поля даты и времени - PullRequest
1 голос
/ 24 марта 2019

Я борюсь со следующим. Я пытаюсь создать собственный сигнал, который будет срабатывать, когда текущее время будет равно значению notify_on DateTimeField.

моей модели.

Примерно так:

class Notification(models.Model):
    ...
    notify_on = models.DateTimeField()



def send_email(*args, **kwargs):
    # send email


signals.when_its_time.connect(send_email, sender=User)

После того, как я прочитал все документы и не нашел информации о том, как реализовать такой сигнал.

Есть идеи?

UPDATE: Менее наивный подход с возможностью отбрасывать неактуальные задачи: https://stackoverflow.com/a/55337663/9631956

Ответы [ 2 ]

2 голосов
/ 24 марта 2019

В документации django есть два интересных сигнала, которые могут помочь вам в этой задаче: pre_save и post_save .Это зависит от ваших потребностей, но, скажем, вы хотите проверить, совпадает ли notify_on вашей модели с текущей датой после сохранения модели (фактически после вызова метода save() или create()).Если это ваш случай, вы можете сделать:

from datetime import datetime
from django.contrib.auth.models import User
from django.db import models
from django.dispatch import receiver
from django.db.models.signals import post_save


class Notification(models.Model):
    ...
    # Every notification is related to a user
    # It depends on your model, but i guess you're doing something similar
    user = models.ForeignKey(User, related_name='notify', on_delete=models.DO_NOTHING)
    notify_on = models.DateTimeField()
    ...
    def send_email(self, *args, **kwargs):
        """A model method to send email notification"""
        ...


@receiver(post_save, sender=User)
def create_notification(sender, instance, created, **kwargs):
    # check if the user instance is created
    if created:
        obj = Notification.objects.create(user=instance, date=datetime.now().date())
        if obj.notify_on == datetime.now().date():
            obj.send_email()

И вы должны знать, что сигналы django не будут работать сами по себе, только если есть действие, которое их запускает.Это означает, что сигналы Django не будут зацикливаться на экземплярах вашей модели и выполнять операции, но сигналы django будут срабатывать, когда ваше приложение выполняет действие с моделью, подключенной к сигналу.

Bonus: Для выполнения цикла над вашими экземплярами и обработки регулярных действий вам может потребоваться async работник с базой данных Queue (в основном, Celery с Redis or RabbitMQ).

1 голос
/ 24 марта 2019

Хорошо, благодаря комментариям @SergeyPugach, я сделал следующее:

Добавлен сигнал post_save, который вызывает функцию, которая добавляет задачу к сельдерею. apply_async давайте вам передадут eta - расчетное время прибытия, которое может принять DateTimeField напрямую, это очень удобно.

# models.py
from django.db.models import signals
from django.db import models
from .tasks import send_notification

class Notification(models.Model):
    ...
    notify_on = models.DateTimeField()


def notification_post_save(instance, *args, **kwargs):
    send_notification.apply_async((instance,), eta=instance.notify_on)


signals.post_save.connect(notification_post_save, sender=Notification)

И актуальная задача в tasks.py

import logging
from user_api.celery import app
from django.core.mail import send_mail
from django.template.loader import render_to_string


@app.task
def send_notification(self, instance):
    try:
        mail_subject = 'Your notification.'
        message = render_to_string('notify.html', {
            'title': instance.title,
            'content': instance.content
        })
        send_mail(mail_subject, message, recipient_list=[instance.user.email], from_email=None)
    except instance.DoesNotExist:
        logging.warning("Notification does not exist anymore")

Я не буду вдаваться в подробности настройки сельдерея, там много информации.

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

...