Skip to content

Commit 0ec2c9e

Browse files
committed
Import django-queryset-transform to feincms.utils
This is much more useful than my prefilled attribute implementation, and so elegant and short that it does not warrant adding an external dependency.
1 parent eda2086 commit 0ec2c9e

1 file changed

Lines changed: 112 additions & 0 deletions

File tree

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# Straight import from https://github.com/simonw/django-queryset-transform
2+
3+
"""
4+
django_queryset_transform
5+
=========================
6+
7+
Allows you to register a transforming map function with a Django QuerySet
8+
that will be executed only when the QuerySet itself has been evaluated.
9+
10+
This allows you to build optimisations like "fetch all tags for these 10 rows"
11+
while still benefiting from Django's lazy QuerySet evaluation.
12+
13+
For example:
14+
15+
def lookup_tags(item_qs):
16+
item_pks = [item.pk for item in item_qs]
17+
m2mfield = Item._meta.get_field_by_name('tags')[0]
18+
tags_for_item = Tag.objects.filter(
19+
item__in = item_pks
20+
).extra(select = {
21+
'item_id': '%s.%s' % (
22+
m2mfield.m2m_db_table(), m2mfield.m2m_column_name()
23+
)
24+
})
25+
tag_dict = {}
26+
for tag in tags_for_item:
27+
tag_dict.setdefault(tag.item_id, []).append(tag)
28+
for item in item_qs:
29+
item.fetched_tags = tag_dict.get(item.pk, [])
30+
31+
qs = Item.objects.filter(name__contains = 'e').transform(lookup_tags)
32+
33+
for item in qs:
34+
print item, item.fetched_tags
35+
36+
Prints:
37+
38+
Winter comes to Ogglesbrook [<sledging>, <snow>, <winter>, <skating>]
39+
Summer now [<skating>, <sunny>]
40+
41+
But only executes two SQL queries - one to fetch the items, and one to fetch ALL of the tags for those items.
42+
43+
Since the transformer function can transform an evaluated QuerySet, it
44+
doesn't need to make extra database calls at all - it should work for things
45+
like looking up additional data from a cache.multi_get() as well.
46+
47+
Originally inspired by http://github.com/lilspikey/django-batch-select/
48+
49+
50+
51+
LICENSE
52+
=======
53+
54+
Copyright (c) 2010, Simon Willison.
55+
All rights reserved.
56+
57+
Redistribution and use in source and binary forms, with or without modification,
58+
are permitted provided that the following conditions are met:
59+
60+
1. Redistributions of source code must retain the above copyright notice,
61+
this list of conditions and the following disclaimer.
62+
63+
2. Redistributions in binary form must reproduce the above copyright
64+
notice, this list of conditions and the following disclaimer in the
65+
documentation and/or other materials provided with the distribution.
66+
67+
3. Neither the name of Django nor the names of its contributors may be used
68+
to endorse or promote products derived from this software without
69+
specific prior written permission.
70+
71+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
72+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
73+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
74+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
75+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
76+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
77+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
78+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
79+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
80+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
81+
"""
82+
83+
from django.db import models
84+
85+
class TransformQuerySet(models.query.QuerySet):
86+
def __init__(self, *args, **kwargs):
87+
super(TransformQuerySet, self).__init__(*args, **kwargs)
88+
self._transform_fns = []
89+
90+
def _clone(self, klass=None, setup=False, **kw):
91+
c = super(TransformQuerySet, self)._clone(klass, setup, **kw)
92+
c._transform_fns = self._transform_fns[:]
93+
return c
94+
95+
def transform(self, fn):
96+
c = self._clone()
97+
c._transform_fns.append(fn)
98+
return c
99+
100+
def iterator(self):
101+
result_iter = super(TransformQuerySet, self).iterator()
102+
if self._transform_fns:
103+
results = list(result_iter)
104+
for fn in self._transform_fns:
105+
fn(results)
106+
return iter(results)
107+
return result_iter
108+
109+
class TransformManager(models.Manager):
110+
111+
def get_query_set(self):
112+
return TransformQuerySet(self.model, using=self._db)

0 commit comments

Comments
 (0)