2011-05-25 74 views
3

以下僞代碼最乾淨的方式是什麼?Python/Django迭代一個列表,同時檢查內部對象

class Player 
    id 
    name 

class Participant (is a subset of Player with an added status) 
    player_id (ForeignKey to Player) 
    status 

所以,你可以有玩家的列表(韋恩,查克,鮑比) 然後與會者名單(韋恩(狀態:YES),鮑比(狀態:NO)) 注意查不在參與者名單中。

所以,我遍歷所有玩家,並輸出參與者存在的狀態。

player_list = list with Player objects 
participant_list = list with Participant objects 

for player in player_list: 
    if player.id exists in participant_list //QUESTION IS HERE: HOW DO I DO THIS??? 
     print participant.status 

我不知道如何檢查包含對象的列表的內部id?

回答

3

加入另一個循環

player_list = list with Player objects 
participant_list = list with Participant objects 

for player in player_list: 
    for participant in participant_list: 
     if player == participant.player: 
      print participant.status 
      break 

不是最漂亮的解決方案,但簡單。

-1

或者我認爲你也可以做:

player_list = list with Player objects 
participant_list = list with Participant objects 

for player, participant in zip(player_list, participant_list): 
    if player == participant.player: 
      print participant.status 
      break 

但兩者player_listparticipant_list必須具有相同的長度

+0

這不會做OP想要的。它將比較參與者1的參與者1,參與者2的參與者2等等。 OP希望比較每個參與者與每個參與者(即參與者1,2和3參與者1,然後參與者1,2和3參與者2)。再加上問題的陳述方式,這些名單很可能有不同的長度。 – Blair 2011-05-25 05:31:19

+0

ohh,:D感謝您的更正 – Paulo 2011-05-25 05:48:24

2

你想要的是QuerySet對象values_list()方法。

player_list = Player.objects.filter(...) 
participant_list = Participant.objects.filter(...) 
participant_player_id_list = participant_list.values_list('player_id', flat=True) 

for player in player_list: 
    if player.id in participant_player_id_list: 
     print participant_list.get(player_id=player.id).status 
+2

參與者列表將對每個玩家迭代一次,並且每個玩家/參與者都會運行一次SQL查詢。這意味着,如果您有100個玩家,您將循環100次參與者列表並運行多達102個SQL查詢。將參與者列表轉換爲字典,無論玩家的數量如何,您總是會有1次迭代和2次SQL查詢。 – 2011-05-26 09:19:52

2

對於較小的集播放器/參與者,查找字典就夠了:

player_list = Player.objects.all() 

# Get all relevant participants (or just all if you like) 
player_ids = player_list.value_list('id', flat=True) 
participant_list = Participant.objects.filter(player_id__in=player_ids) 

# Create a lookup dict 
participants = dict([(p.player_id, p) for p in participant_list]) 

# Everything is now prepared for quick access 
for player in player_list: 
    if player.id in participants: 
     print participants[player.id].status 

如果你經常這樣做,你可能需要使用不同的方法。這裏有三種選擇。

繼承

使用繼承並存儲在數據庫中的狀態。這將使類似的查詢在未來更容易/更高效:

class Player(models.Model): 
    name = models.CharField(max_length=128) 
    is_participant = models.BooleanField(default=False) 

class Participant(Player): 
    status = models.CharField(max_length=32) 

    def save(self, *args, **kwargs): 
     if not self.id: 
      self.is_participant = True 
     super(Participant, self).save(*args, **kwargs) 

有自己的數據結構類似,這意味着Participant對象總是有相同的字段Player對象和您可以檢查如果一個球員是一個參與者而不會觸及數據庫或循環遍歷所有參與者。但是,獲取相關參與者將遭受重創。這種方法的一個變種是將一個通用外鍵附加到Player。檢查None的屬性會告訴你玩家是否也是參與者,然後按照鏈接將您帶到相關的數據庫對象。

Denormalisation

如果狀態是你唯一需要的值,你可以將其存儲在播放器的模型太,並自動將其與你的Participant值同步。如果你使用繼承如上,你會得到免費的:

class Player(models.Model): 
    name = models.CharField(max_length=128) 
    participant_status = models.CharField(max_length=32) 

class Participant(Player): 
    pass 

您可以查詢它是這樣的:

for player in Player.objects.all() 
    if player.participant_status: 
     print participant.status 

一個缺點是,你將不得不隱藏播放器狀態字段形式。但好處是,你將能夠查詢a)如果玩家是參與者並且b)在沒有擊中數據庫的情況下獲得狀態。

但是這種設計當然不像標準化的數據庫那樣乾淨,只有在成本太高的情況下才能真正做到。

以一對一的關係

的最後一種方法是使用OneToOneField如果是一對一的關係。理論上你可以從任何一方鏈接兩個模型,但我不確定這是如何與select_related()一起工作的,這是你想用來避免在列表中多次觸擊數據庫的原因。

class Player(models.Model): 
    name = models.CharField(max_length=128) 
    participant = models.OneToOneField('Participant', null=True) 

class Participant(models.Model): 
    status = models.CharField(max_length=32) 

您的查詢是這樣的:

player_list = Player.objects.all().select_related('participant') 

for player in player_list: 
    if player.participant_id is not None: 
    print player.participant.status 
+0

你的答案是超級深奧的,但我不認爲它回答了實際的問題,這是如何去做與對象列表,而不是查詢集。他不得不改變他做事的方式,在我看來,這是一件好事。查詢集比對象列表更易於使用。 – 2011-05-25 16:14:06

+0

第一種方法(lookup dict)也可以使用列表,只是跳過定義'person_list'和'participant_list'標籤的行。 – 2011-05-25 19:04:51

+0

正確你是:) – 2011-05-25 19:49:23

0

沿着相同的路線由詹姆斯和John2x的答案,你可以滾三個有趣的蟒蛇/ Django的特徵組合在一起來做到這一點的方式,是更明確,可能要快得多。管理器方法values_listin_bulk可用於構建模型對象的字典,其中每個對象的id屬性映射到完整對象。第三個技巧是python字典的一個特性,它可以直接傳遞給內置函數set以從字典的鍵中產生一個集合。像這樣:

import operator 

# Get a bulk dict of player objects. 
pl_objs = Player.objects 
bulk_players = pl_objs.in_bulk(pl_obj.values_list("id", flat=True)) 

# Get a bulk dict mapping player.id to Participant instances. 
bulk_participants = dict((operator.attrgetter("player_id"), x) 
          for x in Participant.objects.all()) 

# Now find the overlap with a set intersection; print status. 
for _id in set(bulk_players) & set(bulk_participants): 
    print bulk_participants[_id].status