2010-06-11 57 views
3

我有模型,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 

我的问题:是否有解决问题的方式更优雅通过使用QuerySet而不需要在任何时候调用保存?如果没有,我想让解决方案更通用:如何获得模型实例的外键列表,或者您是否有更好的通用解决方案来解决我的问题?

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

+0

这是一个众所周知的事情(或发行一些)。用django-idmapper来看看答案。 – 2011-01-27 00:02:53

回答

0

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

+0

我不认为Player对象的创建是一个因素。我使用itertools模块中的组合(player_list,2)来帮助创建匹配对象。我只是测试了这个模块,并没有做深层次的拷贝。 – Joshua 2010-06-11 19:58:17

2

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

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

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

使用:

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

这一次创建所有属性实例,并且可以足够聪明,重新使用实例。否则,你将需要某种显式缓存(这就是你的解决方案所做的)。

警告:我对这种行为感到惊讶,我不是这方面的专家。我在我自己的代码中搜索这类问题的信息时发现了这篇文章。我只是分享我认为正在发生的事情,因为我试图了解...

+0

顺便提一句,SQLAlchemy确实有一个缓存(会话)可以避免这个问题。 – 2011-04-01 15:29:31

1

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