Django Отправка электронного письма при создании нового объекта модели

Я хочу отправлять электронное письмо, если пользователь разместил новый заказ. Я хочу сделать функцию отправки электронной почты из моего models.py. Возможно ли это? Я настроил свой settings.py для электронной почты. Я ищу самый простой способ сделать это. Я не хочу трогать мои представления, так что возможно ли это?

#это моя модель заказа

class Order(models.Model):

    PENDING_PAYMENT = 'Pending Payment'
    ON_HOLD = 'On Hold'

    status_choices = [

        ('Cancel', 'Cancel'),
        ('Pending Payment', 'Pending Payment'),
        ('On Hold', 'On Hold'),
        ('Waiting For Payment', 'Waiting For Payment'),
        ('Processing', 'Processing'),
        ('Done', 'Done'),

    ]

    orderstatus_choices = [

        ('Cancel', 'Cancel'),
        ('Pending Payment', 'Pending Payment'),
        ('On Hold', 'On Hold'),
        ('Waiting For Payment', 'Waiting For Payment'),
        ('Processing', 'Processing'),
        ('Done', 'Done'),

    ]

    Ordinary = 'Ordinary'

    customer_choices = [

        ('Ordinary', 'Ordinary'),
        ('Police', 'Police'),
        ('RAB', 'RAB'),
        ('DGIF', 'DGIF'),
        ('CID', 'CID'),
        ('NAVY', 'NAVY'),
        ('Air Force', 'Air Force'),
        ('Army', 'Army'),
        ('DB', 'DB'),
        ('Administration', 'Administration'),

    ]

    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
    paymentMethod = models.CharField(max_length=200, null=True, blank=True)
    taxPrice = models.DecimalField(max_digits=11, decimal_places=2, null=True, blank=True)
    shippingPrice = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
    totalPrice = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
    isPaid = models.BooleanField(default=False)
    paidAt = models.DateTimeField(auto_now_add=False, null=True, blank=True)
    isDelivered = models.BooleanField(default=False)
    deliverAt = models.DateTimeField(auto_now_add=False, null=True, blank=True)
    createdAt = models.DateTimeField(auto_now_add=True)
    status = models.CharField(max_length=220, choices=status_choices, default=PENDING_PAYMENT)
    orderStatus= models.CharField(max_length=220, choices=orderstatus_choices, default=ON_HOLD, blank=True, null=True)
    customerType = models.CharField(max_length=200, blank=True, null=True, choices=customer_choices, default=Ordinary)
    _id = models.AutoField(primary_key=True, editable=False)


    def __str__(self):
        return str(self._id)

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

Пересмотрите подход и попробуйте отправить письмо либо через очередь задач, либо через cronjob. Если вы настаиваете на прямом подходе, вы можете вставить условие типа if not self.pk в метод save()

class Order(models.Model):

    def save(self, *args, **kwargs):
        # model does not have primary key, it's a new order then
        if not self.pk:
            self.send_order_email()

        return super().save(*args, **kwargs)

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

примечание: согласно PEP8, в именах переменных следует использовать snake_case вместо camelCase (например, is_delivered вместо isDelivered)

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

from django.db.models.signals import pre_save
from django.dispatch import receiver

@receiver(pre_save, sender=Order)
def send_email(sender, instance, **kwargs):
    obj_id = instance._id
    # your code to send email
    ...

Вы можете прочитать больше о сигналах django в официальной документации также https://docs.djangoproject.com/en/3.2/topics/signals/

Вернуться на верх