我应该如何构造多个模型类型的树?

时间:2019-02-06 19:16:16

标签: python django django-models django-mptt

我正在尝试将电视节目建模到情节级别。鉴于树的每个级别(网络,系列,季节,情节)具有不同的字段,因此我想为每个级别使用不同的模型类型。

我最初的方法是在每个级别使用外键跟踪父级(这是一种简化的方法,我知道还会有其他字段):

export default class HomeScreen extends React.Component {

  state = {
    progress: 0, 
    drunk: 0, // this is the state I want to set to zero
    goal: 0
  };

  componentDidMount() {

    this.willFocusSubscription = this.props.navigation.addListener(
      "willFocus",
      payload => {
        console.debug("willFocus", payload);
        //retrieve items from AsyncStorage
        _retrieveData = async key => {
          try {
            const sliderValue = await AsyncStorage.getItem("sliderValue");
            const drunk = await AsyncStorage.getItem("drunk");
            const progress = await AsyncStorage.getItem("progress");
            if (drunk !== null) {
              // We have data!! and set them to states
              this.setState({
                goal: parseInt(sliderValue),
                drunk: parseInt(drunk),
                progress: parseFloat(progress)
              });
            }
          } catch (error) {
            console.log(error.message);
          }
        };
        _retrieveData();
      }
    );
  }

  componentWillUnmount() {
    if (this.willFocusSubscription) {
      this.willFocusSubscription.remove();
    }
  }

  //function to drink water, set state and setItem Async
  drinkWater = () => {
    this.setState(prevState => ({
      drunk: prevState.drunk + 200,
      progress: (prevState.drunk + 200) / this.state.goal
    }));

    let progress = this.state.progress;
    let drunk = this.state.drunk;

    _storeData = async () => {
      try {
        await AsyncStorage.setItem("progress", progress.toString());
        await AsyncStorage.setItem("drunk", drunk.toString());
      } catch (error) {
        console.log(error.message);
      }
    };

    _storeData();
  };

但是,如果要获取特定情节的网络,则必须查找情节->季节->系列->网络。这似乎效率低下,架构欠佳,因为它需要大量查询。

我看到了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) ... 库,但这要求您的树必须是单一模型类型。

从设计的角度来看,构造这种树的标准方法是什么?不同方法的权衡是什么?

1 个答案:

答案 0 :(得分:0)

它不是那么低效。只需“三个”联接即可获得特定情节的网络。

如果您在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")

您可以看到它已预先加入网络。因此,这是一个具有三个联接的查询。加入确实要付出代价,但在遇到性能问题之前,您不必担心。

Live example