Мне кажется, что нарезка - это не столько проблема, которая отличается от того, когда ваши собственные классы и программы плохо архивируются / разработаны.
Если я передаю объект подкласса в качестве параметра для метод, который принимает параметр суперкласса типа, я должен, конечно, знать об этом и знать внутренне, вызываемый метод будет работать только с объектом суперкласса (aka baseclass).
Мне кажется, необоснованное ожидание того, что предоставление подкласса, где запрашивается базовый класс, каким-то образом приведет к конкретным результатам подкласса, вызовет проблему разрезания. Его либо плохой дизайн при использовании метода, либо недостаточная реализация подкласса. Я предполагаю, что это обычно результат жертвовать хорошим дизайном ООП в пользу целесообразности или повышения производительности.
В документации 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 будут срабатывать, когда ваше приложение выполняет действие с моделью, подключенной к сигналу.
Бонус: Для выполнения цикла над вашими экземплярами и обработки регулярного действия вам может потребоваться async
работник с базой данных Queue
(в основном, Celery
с Redis or RabbitMQ
). [ 1112]
Хорошо, благодаря комментариям @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")
Я не буду вдаваться в подробности установки сельдерея, там много информации.
Теперь я попытаюсь выяснить, как обновить задачу после обновления экземпляра уведомления, но это совсем другая история.