Как мне структурировать дерево нескольких типов моделей? - PullRequest
0 голосов
/ 06 февраля 2019

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

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

class Network(models.Model):
   ...

class Series(models.Model):
   network = models.ForeignKey(Network)
   ...

class Season(models.Model):
   series = models.ForeignKey(Series)
   ...

class Episode(models.Model):
   season = models.ForeignKey(Season)
   ...

Но еслиЯ хочу получить сеть определенного эпизода, я должен искать Эпизод-> Сезон-> Серия-> Сеть.Это кажется неэффективным и плохо спроектированным, поскольку требует много запросов.

Я видел библиотеку django-mptt, но для этого нужно, чтобы ваше дерево было построено из одного типа модели.

ИзС точки зрения дизайна, какой стандартный способ структурировать этот тип дерева?Каковы компромиссы разных подходов?

1 Ответ

0 голосов
/ 06 февраля 2019

Это не так неэффективно.Для подключения к сети для определенного эпизода требуется всего три соединения.

Вы можете упростить свою жизнь, если создадите cached_property для своей модели Episode:

class Network(models.Model):
    name = models.CharField(max_length=255)

# ...

class Episode(models.Model):
    season = models.ForeignKey(Season, on_delete=models.CASCADE)

    @cached_property
    def network(self):
        return self.season.series.network

    @cached_property
    def network_name(self):
        return self.season.series.network.name

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

Хорошая вещь о cached_property это то, что он может быть переопределен путем установки этого атрибута в экземпляре, что именно то, что делает django, когда мы аннотируем значение:

episodes = Episode.objects.annotate(network_name=F('season__series__network__name'))
for episode in episodes:
   print(episode.pk, episode.network_name)

Путем аннотирования имени сети перед доступом к эпизоду, django узнает, что он имеетприсоединиться к этому имени.Вот как выглядит запрос:

SELECT 
    "main_episode"."id", 
    "main_episode"."name", 
    "main_episode"."season_id", 
    "main_network"."name" AS "network_name" 
FROM "main_episode" 
INNER JOIN "main_season" ON ("main_episode"."season_id" = "main_season"."id") 
INNER JOIN "main_series" ON ("main_season"."series_id" = "main_series"."id") 
INNER JOIN "main_network" ON ("main_series"."network_id" = "main_network"."id")

Вы можете видеть, что он присоединился к сети в эпизоде ​​заранее.Так что это один запрос с тремя объединениями.У объединений есть свои затраты, но вам не стоит об этом беспокоиться, пока у вас не возникнут проблемы с производительностью.

Живой пример

...