Checking for many-to-many relation OR a property

Viewed 40

How can I check whether a many-to-many relationship exists or another property is fulfilled? When I try the query, it returns some rows twice!

Given a model

from django.db import models
class Plug(models.Model):
    name = models.CharField(primary_key=True, max_length=99)

class Widget(models.Model):
    name = models.CharField(primary_key=True, max_length=99)
    shiny = models.BooleanField()
    compatible = models.ManyToManyField(Plug)

I have the following items in my database:

from django.db.models import Q
schuko = Plug.objects.create(name='F')
uk = Plug.objects.create(name='G')
Widget.objects.create(name='microwave', shiny=True).compatible.set([uk])
Widget.objects.create(name='oven', shiny=False).compatible.set([uk])
Widget.objects.create(name='pc', shiny=True).compatible.set([uk, schuko])

Now I want all names of widgets that are shiny and/or compatible with Schuko:

shiny_or_schuko = sorted(
    Widget.objects.filter(Q(shiny=True) | Q(compatible=schuko))
    .values_list('name', flat=True))

But to my surprise, this does not return ['microwave', 'pc']. Instead, 'pc' is listed twice, i.e. shiny_or_schuko is ['microwave', 'pc', 'pc'].

Is this a Django bug? If not, how can I set up the query that I get 'pc' just once?

1 Answers

Is this a Django bug?

No. You simply perform a LEFT OUTER JOIN with the many-to-many table. If two or more related objects match, it will be included multiple times. This can be wanted behavior, for example if you add extra annotations to the elements that takes values from these related objects.

You can make use of .distinct() [Django-doc] to return only distinct elements:

Widget.objects.filter(
    Q(shiny=True) | Q(compatible=schuko)
).values_list('name', flat=True).distinct()
Related