我正在尝试将电视节目建模到情节级别。鉴于树的每个级别(网络,系列,季节,情节)具有不同的字段,因此我想为每个级别使用不同的模型类型。
我最初的方法是在每个级别使用外键跟踪父级(这是一种简化的方法,我知道还会有其他字段):
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)
...
库,但这要求您的树必须是单一模型类型。
从设计的角度来看,构造这种树的标准方法是什么?不同方法的权衡是什么?
答案 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")
您可以看到它已预先加入网络。因此,这是一个具有三个联接的查询。加入确实要付出代价,但在遇到性能问题之前,您不必担心。