2010-06-11 52 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,以便解釋器中每個實例只有一個副本。