Ticket #1807: list_union.3.diff

File list_union.3.diff, 4.4 KB (added by mir@…, 18 years ago)

another patch, replaces other patches

  • django/db/models/query.py

    a b class QuerySet(object):  
    321321        if tables: clone._tables.extend(tables)
    322322        return clone
    323323
     324    ########################
     325    # PUBLIC CLASS METHODS #
     326    ########################
     327
     328    def list_union(cls, model, queries):
     329        """Returns the list_union of a list of QuerySets.
     330        Returns a new QuerySet.
     331        """
     332        if not queries:
     333            return cls(model).extra(where=["1=0"])
     334        return reduce(cls.__or__, queries)
     335
     336    list_union = classmethod(list_union)
     337
     338    def list_intersection(cls, model, queries):
     339        """Returns the list_intersection of a list of QuerySets, a QuerySet."""
     340        if not queries:
     341            return cls(model).extra(where=["1=0"])
     342        else:
     343            return reduce(cls.__and__, queries)
     344
     345    list_intersection = classmethod(list_intersection)
     346
    324347    ###################
    325348    # PRIVATE METHODS #
    326349    ###################
  • new file tests/modeltests/union_intersection/models.py

    - +  
     1"""
     2XX. list_union() and list_intersection()
     3
     4These class methods consolidate a list of QuerySets into a single QuerySet.
     5This is really just a way to use `|` or `&` on all the lists.
     6
     7"""
     8
     9from django.db import models
     10
     11class Journal(models.Model):
     12    name = models.CharField(maxlength=100)
     13
     14    def __repr__(self):
     15        return name
     16
     17class Reporter(models.Model):
     18    first_name = models.CharField(maxlength=30)
     19    last_name = models.CharField(maxlength=30)
     20    email = models.EmailField()
     21
     22    def __repr__(self):
     23        return "%s %s" % (self.first_name, self.last_name)
     24
     25class Article(models.Model):
     26    headline = models.CharField(maxlength=100)
     27    reporter = models.ForeignKey(Reporter, null=True)
     28    journal = models.ForeignKey(Journal, null=True)
     29
     30    def __repr__(self):
     31        return self.headline
     32
     33    class Meta:
     34        ordering = ('headline',)
     35
     36
     37
     38API_TESTS = """
     39>>> from django.db.models.query import QuerySet
     40
     41# Create a few Entries
     42>>> r = Reporter(first_name='John', last_name='Smith', email='john@example.com')
     43>>> r.save()
     44
     45>>> r2 = Reporter(first_name='Paul', last_name='Jones', email='paul@example.com')
     46>>> r2.save()
     47
     48>>> r3 = Reporter(first_name='Mike', last_name='Miller', email='mike@example.com')
     49>>> r3.save()
     50
     51>>> j1 = Journal(name="Daily Shadow")
     52>>> j1.save()
     53
     54>>> j2 = Journal(name="Nightly Light")
     55>>> j2.save()
     56
     57>>> a1 = r.article_set.create(headline='Hello', journal=j1)
     58>>> a1.save()
     59
     60>>> a2 = r2.article_set.create(headline='Goodbye', journal=j1)
     61>>> a2.save()
     62
     63>>> a3 = r2.article_set.create(headline='Hello and goodbye', journal=j2)
     64>>> a3.save()
     65
     66>>> a4 = r3.article_set.create(headline='Have a nice day', journal=j1)
     67>>> a4.save()
     68
     69>>> a5 = Article(headline='Never mind')
     70>>> a5.save()
     71
     72# pathologic cases: empty lists and empty QuerySets
     73>>> QuerySet.list_union(Article, []).count()
     740
     75
     76>>> QuerySet.list_intersection(Article, []).count()
     770
     78
     79>>> QuerySet.list_union(Reporter, [Reporter.objects.filter(first_name=str(i)) for i in range(3)]).count()
     800
     81
     82>>> QuerySet.list_intersection(Reporter, [Reporter.objects.filter(first_name=str(i)) for i in range(3)]).count()
     830
     84
     85# normal case ... find all articles from reporters from example.com
     86# of course you could write this easier as Article.objects.filter(reporter__email__iendswith=...)
     87>>> QuerySet.list_union(Article, [reporter.article_set.all() for reporter in Reporter.objects.filter(email__iendswith='@example.com')])
     88[Goodbye, Have a nice day, Hello, Hello and goodbye]
     89
     90# all the articles from reporters who have written more than one article.
     91# note that this will fetch only the required articles from the database
     92# this could be written with custom sql using extra()
     93>>> reporter_articles = [(reporter, reporter.article_set.all()) for reporter in Reporter.objects.all()]
     94>>> QuerySet.list_union(Article, [articles for reporter,articles in reporter_articles if articles.count() > 1])
     95[Goodbye, Hello and goodbye]
     96
     97# Let's find all authors that have created articles in all known journals
     98>>> [Reporter.objects.filter(article__journal__id__exact=journal.id) for journal in Journal.objects.all()]
     99[[John Smith, Paul Jones, Mike Miller], [Paul Jones]]
     100>>> QuerySet.list_intersection(Reporter, [Reporter.objects.filter(article__journal__id__exact=journal.id) for journal in Journal.objects.all()])
     101[Paul Jones]
     102"""
Back to Top