2013-04-17 111 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,你说得对。所以,我编辑了我的答案。 –