Нужен архитектурный совет о том, как обрабатывать и / или избегать циклического импорта при использовании Django / DRF - PullRequest
1 голос
/ 15 января 2020

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

# models.py
import events.serializers

class Event(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=255, db_index=True)
    ...

    def save(self, *args, **kwargs):
        is_new = not self.pk
        super().save(*args, **kwargs)
        if is_new:
            self.notify_organiser_followers()

    def notify_organiser_followers(self):
        if self.organiser:
            event_data = events.serializers.BaseEventSerializer(self).data
            payload = {'title': f'New event by {self.organiser.name}',
                       'body': f'{self.organiser.name} has just created a new event: {self.name}',
                       'data': {'event': event_data}}
            send_fcm_messages(self.organiser.followers.all(), payload)

В модели есть сериализатор BaseEventSerializer. В методе сохранения я использую notify_organiser_followers и в процессе сериализации текущего сохраняемого события. Для этого мне нужно импортировать BaseEventSerializer.

Вот как выглядит код events.serializers:

# serializers.py
import events.models

class EventTrackSerializer(serializers.ModelSerializer):
    class Meta:
        model = events.models.EventTrack
        fields = ('id', 'name', 'color')


class BaseEventSerializer(serializers.ModelSerializer):
    event_type = serializers.CharField(source='get_event_type_display')
    locations = serializers.SerializerMethodField()

Как вы видите, serializers.py должен импортировать модели, чтобы использовать их в ModelSerializer. На этом этапе я получаю очевидный циклический импорт.

Я решил это путем локального импорта BaseEventSerializer в функцию notify_organiser_followers:

def notify_organiser_followers(self):
    if self.organiser:
        from events.serializers import BaseEventSerializer

Это устраняет проблему, но я Я бы очень хотел избежать этого, особенно потому, что мне пришлось бы делать одно и то же исправление в нескольких местах моего репо. Другой подход, о котором я подумал, - разделить «обычные» сериализаторы и сериализаторы моделей на отдельные файлы. Тем не менее, кажется, что это только исцеление симптома, а не причины.

Мне хотелось бы получить совет о том, как вообще избежать этой ситуации. У меня была такая же проблема при импорте двух разных приложений, которые используют сериализатор друг друга. Например, Пользователь сериализует События, которые он посещает, Событие сериализует своих посетителей. Как бы вы go о разъединении этих двух моделей? Кажется, что модельные отношения часто вынуждают меня в такие ситуации, и избегать циклического импорта становится действительно сложно.

Я также был бы признателен, если бы у вас были какие-либо более крупные проекты Django / DRF Github, демонстрирующие, как этого избежать, поскольку эта проблема постоянно возникает для меня, как только мое приложение станет достаточно большим.

1 Ответ

1 голос
/ 16 января 2020

Есть несколько разных способов сделать это, и, вероятно, разные комбинации стратегий.

Я рекомендовал бы здесь использовать django сигналы и создать класс обслуживания.

Во-первых, используйте django сигналов , чтобы вызвать спецификацию c действия после события Model s. Django имеет встроенные слушатели для случаев, когда такие вещи, как save, происходят на модели, и вы можете выполнять разные вещи, основываясь на этом. Это позволяет вам отделить ваш слой событий от вашей модели.

Для одиночной части: я думаю, что метод Models не должен выполнять действия, которые не связаны с самим собой. Я думаю, что было бы лучше создать какой-нибудь универсальный сервис c в виде синглтона, что-то вроде

EventService(): 
   def do_event_related_logic(self, event):
       ...

event_service = EventService()

, и вы можете просто импортировать event_service и использовать этот класс для всех связанных с событием логи c. В этом случае вы можете вызвать один из методов вашего синглтона для события post_save.

...