vikkyshostak
@vikkyshostak
< This head full of dreams.

Как получить inline модель в инстанс post_save (Django Signals)?

Привет всем!

Использую Django Signals (1.11.7) для некоторых действий над только что сохранённой моделью (отправка письма на почту с инфой из модели, в основном). Всё отлично, работает (ещё бы не работало :D ). Но только я добавляю к этой модели ещё одну, связанную (ForeignKey) с основной (как inlines=[...] в admin.py) — она, как будто бы, не участвует в сохранении инстанса основной модели.

То есть, для доступа к inline модели и какого-то действия при post_save сигнале — нужно редактировать только что созданную запись + ещё раз сохранять.

Вот мой код:

# /tours/models.py

class Tours(models.Model):
    country = models.CharField(...)
    ...


class ToursHotels(models.Model):
    tour = models.ForeignKey(Tours, ...)
    cost = models.IntegerField(...)
    ...


# Signal
@receiver(post_save, sender=Tours)
def do_something(sender, **kwargs):
    tour = Tours.objects.get(id=kwargs.get('instance').id)
    hotels = ToursHotels.objects.filter(tour_id=kwargs.get('instance').id).order_by('cost')
    ...

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

И тут сразу же вопрос — как лучше сделать? Мне приходит в голову только: попробовать сделать через Celery задание, которое будет происходить уже после post_save (что логично, ведь вначале произойдёт post_save, а потом только task из очереди), когда и Tours и ToursHotels уже есть в БД. Но это ещё одна надстройка, ради которой нужно Redis/RMQ поднимать..

Может быть есть какие-либо best practice на эту тему? На SO встретил много вопросов без ответа на похожие темы с post_save, но все они 2010-2012 года и свежее нет — может быть уже всё поменялось, а я и не знаю? ;)

Буду рад толковым комментариям! Заранее спасибо.
  • Вопрос задан
  • 944 просмотра
Решения вопроса 1
vikkyshostak
@vikkyshostak Автор вопроса
< This head full of dreams.
Выражаю благодарность пользователю javedimka за участие и помощь в пояснении многих моментов. Так как я не люблю вопросы без ответов в интернете, то напишу его тут (благо, что javedimka разрешил).


Итак, лучшая практика решения такой проблемы — не использовать Django Signals совсем. Особенно, когда есть встроенные методы, типа ModelAdmin.response_add, и уйти из модели в admin.py:

# ./app/utils.py

def send_mail_to_admin(obj):
    hotels = obj.hotels.all().order_by('cost')

    message = 'Тур ID ' + obj.pk + '\n'
    message += 'Страна: ' + obj.country_name + ' Город: ' + obj.country_name + '\n'
    message += 'Отели: \n'
    for hotel in hotels:
        message += hotel.name + ' ' + hotel.star + ' ' + hotel.cost + ' руб. \n'

    send_mail(
        'From Admin',
        message,
        'no-reply@example.com',
        ['admin@example.com'],
        fail_silently=False,
    )

# ./app/admin.py

from .utils import send_mail_to_admin


class ToursAdmin(admin.ModelAdmin):
    exclude = ('created_at',)
    list_display = ('country',)
    ordering = ('created_at',)
    inlines = (HotelsInline,)

    def response_add(self, request, obj, post_url_continue=None):
        send_mail_to_admin(obj)
        return super().response_add(request, obj, post_url_continue)
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
Помоему нужно добавить 1 строку
# Signal
@receiver(post_save, sender=Tours)
@receiver(post_save, sender=ToursHotels) # отправляем сигнал при правке дочерней модели
def do_something(sender, **kwargs):
tour = Tours.objects.get(id=kwargs.get('instance').id)
hotels = ToursHotels.objects.filter(tour_id=kwargs.get('instance').id).order_by('cost')
...
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы