29

I have to querysets. alllists and subscriptionlists

alllists = List.objects.filter(datamode = 'A')
subscriptionlists = Membership.objects.filter(member__id=memberid, datamode='A')

I need a queryset called unsubscriptionlist, which possess all records in alllists except the records in subscription lists. How to achieve this?

halfer
  • 19,824
  • 17
  • 99
  • 186
Rajasekar
  • 18,392
  • 34
  • 106
  • 137
  • The two querysets in your example appear to use different models. They would need to be using the same model for your question to make sense. – Aram Dulyan May 10 '11 at 08:04
  • 1
    This is a related answer which is much more concise- http://stackoverflow.com/questions/8867743/how-does-one-find-the-entities-in-a-django-query-set-that-are-not-in-another-spe – Devang Nov 02 '13 at 08:07

5 Answers5

35

Since Django 1.11, QuerySets have a difference() method amongst other new methods:

# Capture elements that are in qs_all but not in qs_part
qs_diff = qs_all.difference(qs_part)    

Also see: https://stackoverflow.com/a/45651267/5497962

funnydman
  • 9,083
  • 4
  • 40
  • 55
markus-hinsche
  • 1,372
  • 15
  • 26
22

You should be able to use the set operation difference to help:

set(alllists).difference(set(subscriptionlists))
Brian Fisher
  • 23,519
  • 15
  • 78
  • 82
11

Well I see two options here.

1. Filter things manually (quite ugly)

diff = []
for all in alllists:
    found = False
    for sub in subscriptionlists:
        if sub.id == all.id:
            found = True 
            break
    if not found:
        diff.append(all)

2. Just make another query

diff = List.objects.filter(datamode = 'A').exclude(member__id=memberid, datamode='A')
Silver Light
  • 44,202
  • 36
  • 123
  • 164
4

How about:

subscriptionlists = Membership.objects.filter(member__id=memberid, datamode='A')
unsubscriptionlists = Membership.objects.exclude(member__id=memberid, datamode='A')

The unsubscriptionlists should be the inverse of subscription lists.

Brian's answer will work as well, though set() will most likely evaluate the query and will take a performance hit in evaluating both sets into memory. This method will keep the lazy initialization until you need the data.

Gevious
  • 3,214
  • 3
  • 21
  • 42
  • `unsubscriptionlists = Membership.objects.exclude(member__id=memberid, datamode='A')` should be: `unsubscriptionlists = Membership.objects.filter(datamode='A').exclude(member__id=memberid)` – Chris Pratt May 10 '11 at 21:28
0

In case anyone's searching for a way to do symmetric difference, such operator is not available in Django.

That said, it's not that hard to implement it using difference and union, and it'll all be done in a single query:

q1.difference(q2).union(q2.difference(q1))
João Haas
  • 1,883
  • 13
  • 13