我应该如何构建多种模型类型的树?
How should I structure a tree of multiple model types?
我正在尝试将电视节目建模到剧集级别。鉴于树的每个级别(网络、系列、季节、剧集)都有不同的字段,我想为每个级别使用不同的模型类型。
我最初的方法是在每个级别使用外键跟踪父级(这是一种简化的方法,我知道会有其他字段):
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)
...
但是如果我想获取特定剧集的网络,我必须查找 Episode->Season->Series->Network。这似乎效率低下且架构不佳,因为它需要大量查询。
我看到了库 django-mptt
,但这需要您的树由单一模型类型构建。
从设计的角度来看,构造这种树的标准方法是什么?不同方法的权衡是什么?
并不是那么低效。它需要 "only" 三个连接才能获得某一集的网络。
如果您在 Episode
模型上创建 cached_property
,您的生活会更轻松:
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")
可以看到提前入网了。所以这是一个包含三个连接的查询。联接确实有成本,但在您遇到性能问题之前,您不应该担心这一点。
我正在尝试将电视节目建模到剧集级别。鉴于树的每个级别(网络、系列、季节、剧集)都有不同的字段,我想为每个级别使用不同的模型类型。
我最初的方法是在每个级别使用外键跟踪父级(这是一种简化的方法,我知道会有其他字段):
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)
...
但是如果我想获取特定剧集的网络,我必须查找 Episode->Season->Series->Network。这似乎效率低下且架构不佳,因为它需要大量查询。
我看到了库 django-mptt
,但这需要您的树由单一模型类型构建。
从设计的角度来看,构造这种树的标准方法是什么?不同方法的权衡是什么?
并不是那么低效。它需要 "only" 三个连接才能获得某一集的网络。
如果您在 Episode
模型上创建 cached_property
,您的生活会更轻松:
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")
可以看到提前入网了。所以这是一个包含三个连接的查询。联接确实有成本,但在您遇到性能问题之前,您不应该担心这一点。