Tanzaho
Tanzaho

Reputation: 1372

Duplicate results of `ManyToManyField` when using 'through'

I have two models : User and Article Each User can create an Article via the selection of one or several Tag.

I am using the following representation in my model :

class User(models.Model):
    name = models.CharField(max_length=200, null=False, blank=False, default=None)
    articles = models.ManyToManyField(Article, null=True, blank=True, default=None, through='Tag')

class Article(models.Model):
    url = models.URLField(unique=True)

class Tag(models.Model):
    name = models.CharField(max_length=200, null=False, blank=False, default=None)
    article = models.ForeignKey(Article)
    user = models.ForeignKey(User)

Now, I can add users and articles separately. Then I can associate a user to an article. Say I have only <user 1> and <article 1>. I create two distinct tags (done via the admin) :

          Tag1
<user 1>  --->   <article 1>
          Tag2
<user 1>  --->   <article 1>

I still have only one user, and only one article, but two tags are linking them. When I query user1.article.all() I expect only one article as result.

Expected behavior :

user = User.objects.get(pk=1)
print user.article.all()
[<Article: 1>]

Observed behavior

user = User.objects.get(pk=1)
print user.article.all()
[<Article: 1>, <Article: 1>]

Anyone knows if this is a bug or if I am querying Django (1.7.1 and 1.7.2) in a bad way ? If this is normal, how can I remove the duplicates easily ?

[Edit] Opened a ticket in case it is a bug : https://code.djangoproject.com/ticket/24079#ticket

Upvotes: 1

Views: 817

Answers (1)

Kevin Christopher Henry
Kevin Christopher Henry

Reputation: 49012

You need to use .distinct(). From the docs:

By default, a QuerySet will not eliminate duplicate rows. In practice, this is rarely a problem, because simple queries such as Blog.objects.all() don’t introduce the possibility of duplicate result rows. However, if your query spans multiple tables, it’s possible to get duplicate results when a QuerySet is evaluated. That’s when you’d use distinct().

Upvotes: 2

Related Questions