when I print Brand.objects.filter(Q(name__icontains=keyword)|Q(tag__name__icontains=keyword))
, it yields two objects as opposed to my expectation. It should be only one.
My models.py is like
class Brand(models.Model):
name = models.CharField(max_length=20)
tag = models.ManyToManyField('Tag', blank=True)
class Tag(models.Model):
name = models.CharField(max_length=20)
When I print Brand.objects.filter(name__icontains=keyword)
and Brand.objects.filter(tag__name__icontains=keyword)
seperately, each one yields only one same object. But when I merge these two queries, it yields duplicates. I found out .distinct()
solves this problem but I wanna know why. Looking forward to help!
CodePudding user response:
It is not the or condition that results in duplicates, it is the fact that you are filtering on a related model with a ManyToManyField
. This means that the query will look like:
SELECT brand.* FROM brand LEFT OUTER JOIN brand_tag ON brand_tag.brand_id = brand.id LEFT OUTER JOIN tag ON tag.id = brand_tag.tag_id WHERE brand.name LIKE %keyword% OR tag.name LIKE %keyword%
You thus will JOIN
on the tag model, and if multiple tags match the keyword, then that brand will be repeated that many times in the QuerySet
.
If you thus would have filtered with .filter(tag__name__icontains=keyword)
, you would have gotten duplicates as well.
Note: There is a
django-taggit
package [GitHub] to work with content that contains tags. This might be better than implementing your own tag system.