Django模型实例外键列表在状态更改期间失去一致性

时间:2010-06-11 02:58:31

标签: python django django-models foreign-keys

我的模型Match有两个外键:

class Match(model.Model):
   winner = models.ForeignKey(Player)
   loser = models.ForeignKey(Player)

当我遍历Match时,我发现每个模型实例都使用唯一对象作为外键。这最终会咬我,因为它引入了不一致,这是一个例子:

>>> def print_elo(match_list):
...     for match in match_list:
...         print match.winner.id, match.winner.elo
...         print match.loser.id, match.loser.elo
... 
>>> print_elo(teacher_match_list)
4 1192.0000000000
2 1192.0000000000
5 1208.0000000000
2 1192.0000000000
5 1208.0000000000
4 1192.0000000000
>>> teacher_match_list[0].winner.elo = 3000
>>> print_elo(teacher_match_list) 
4 3000            # Object 4
2 1192.0000000000
5 1208.0000000000
2 1192.0000000000
5 1208.0000000000
4 1192.0000000000 # Object 4
>>>

我这样解决了这个问题:

def unify_refrences(match_list):
    """Makes each unique refrence to a model instance non-unique.

    In cases where multiple model instances are being used django creates a new
    object for each model instance, even if it that means creating the same
    instance twice. If one of these objects has its state changed any other
    object refrencing the same model instance will not be updated. This method
    ensure that state changes are seen. It makes sure that variables which hold
    objects pointing to the same model all hold the same object.

    Visually this means that a list of [var1, var2] whose internals look like so:

        var1 --> object1 --> model1
        var2 --> object2 --> model1

    Will result in the internals being changed so that:

        var1 --> object1 --> model1
        var2 ------^
    """
    match_dict = {}
    for match in match_list:
        try:
            match.winner = match_dict[match.winner.id]
        except KeyError:
            match_dict[match.winner.id] = match.winner
        try:
            match.loser = match_dict[match.loser.id]
        except KeyError:
            match_dict[match.loser.id] = match.loser

我的问题:有没有办法通过使用QuerySets更优雅地解决问题而无需在任何时候调用save?如果没有,我想使解决方案更通用:如何获得模型实例上的外键列表,或者您对我的问题有更好的通用解决方案?

如果您认为我不明白为什么会发生这种情况,请纠正我。

4 个答案:

答案 0 :(得分:2)

这是因为,据我所知,模型实例没有全局缓存,因此每个查询都会创建新实例,并且使用单独的查询懒惰地创建相关对象列表。

您可能会发现select_related()足够聪明,可以在这种情况下解决问题。而不是像以下代码:

match = Match.objects.filter(...).get()

使用:

match = Match.objects.select_related().filter(...).get()

这会立即创建所有属性实例,并且可能足够智能以重用实例。否则,您将需要某种显式缓存(这是您的解决方案所做的)。

警告:我对这种行为感到惊讶,并不是这方面的专家。我在自己的代码中搜索有关此类问题的信息时发现了这篇文章。当我试图理解时,我只是分享我的想法......

答案 1 :(得分:1)

您可能想要签出django-idmapper它定义了一个SharedMemoryModel,这样解释器中每个实例只有一个副本。

答案 2 :(得分:0)

呃,您使用get_or_create()进行播放器记录吗?如果没有,那么您可能在每场比赛中创建相同(或几乎相同)的球员记录的新实例。这可能会导致眼泪和/或精神错乱。

答案 3 :(得分:0)

我找到了一个类似问题的好答案,请看一下:

Django Models: preserve object identity over foreign-key following