2013-04-17 110 views
0

我有一個模型,Entry.特別值得注意的是它的addTags函數,它爲Entry的實例添加一個標記並保存它。下面是類的完整代碼:Django ManyToMany平等

class Entry(models.Model): 

    title = models.CharField(max_length=80) 
    author = models.ForeignKey(User) 
    pubdate = models.DateTimeField() 
    tags = models.ManyToManyField(Tag) 
    text = models.TextField() 

    def getAllTags(self): 
     ''' 
     Returns all tags applied to a given entry 
     ''' 
     return self.tags.all() 

    def addTag(self, tagName): 
     ''' 
     Add a tag to an entry. If tag does not exist, create it. If tag has 
     already been added, do nothing. 
     ''' 
     try: 
      tag = Tag.objects.get(name=tagName) 
      return tag 
     except Tag.DoesNotExist: 
      tag = self.tags.create(name=tagName) 
      self.save() 
      return tag 

奇怪的是,當我測試用下面的代碼的addTag功能(取值範以前創建,是一個有效的Entry對象):

tag1 = entry1.addTag("testtag2") 
    tag2 = entry1.addTag("testtag2") 
    tag3 = entry1.addTag("testtag3") 
    tagList = [tag1, tag2, tag3] 
    for listTag, objTag in zip(tagList, entry1.tags.all()): 
     print "%s: %s" % (listTag, objTag) 
    self.assertEqual(entry1.tags.all(), tagList) 

我得到以下斷言錯誤:

AssertionError: [<Tag: testtag>, <Tag: testtag2>, <Tag: testtag3>] != [<Tag: testtag>, <Tag: testtag2>, <Tag: testtag3>] 

然而,打印語句中上面的代碼給我下面的:

testtag: testtag 
testtag2: testtag2 
testtag3: testtag3 

表明隨着預期的標籤已創建。看起來好像每個標籤對象的不同內存位置都已經創建好了,但我不明白這是怎麼回事。思考?

+1

只是一個提示:你可以使用'Tag.objects.get_or_create'而不是此嘗試/除塊 –

+0

你怎麼不看看這可能是這樣嗎? Django使用每個查詢集返回一個模型的新實例。 – XORcist

+0

@Fernando:太棒了。我預計,Django的友善人士可能會預料到這種情況併爲其提供代碼。 – user1427661

回答

2
self.assertEqual(list(entry1.tags.all()), tagList) 

entry1.tags.all()是一個查詢集它不會覆蓋__eq__方法,並通過它的id(內存位置)與其他對象進行比較。如果要將它返回的對象與tagList中的對象進行比較,則必須首先通過調用列表來評估該查詢集。

列表(entry1.tags.all())項的情況是不一樣的人作爲標記列表。這並不改變這兩個列表是相等的,因爲Entry實例有一個被覆蓋的__eq__方法,並且通過它們的pk進行比較。

編輯:

當對列表進行比較一個QuerySet

entry1.tags.all() == tagList 

你是不是比較由它返回的情況下,因爲一個QuerySet沒有覆蓋的__eq__方法,因此不能與列表相比較。

但是可以比較兩個列表。列表中的所有實例都進行兩兩比較,並且由於它們的__eq__提供了除id比較之外的其他方法(它測試pk相等),所以比較相同。

+0

這可以工作,但是能否向我解釋在查詢集中調用'list'如何改變對象對其他對象的評估方式(即eq方法)。 – user1427661

+0

見編輯........ – XORcist

0

試試這個重構代碼:

class Entry(models.Model): 

     title = models.CharField(max_length=80) 
     author = models.ForeignKey(User) 
     pubdate = models.DateTimeField() 
     tags = models.ManyToManyField(Tag) 
     text = models.TextField() 

     def getAllTags(self): 
      ''' 
      Returns all tags applied to a given entry 
      ''' 
      return self.tags.all() 

     def addTag(self, tagName): 
      ''' 
      Add a tag to an entry. If tag does not exist, create it. If tag has 
      already been added, do nothing. 
      ''' 
      tag = Tag.objects.get_or_create(name=tagName) 
      self.tags.add(tag) 
      self.save() 

此外,在測試中,你要比較兩個列表:

self.assertEqual(list(entry1.tags.all()), tagList) 
+0

不回答這個問題... – XORcist

+0

@ MOTER,你說得對。所以,我編輯了我的答案。 –