forked from rc/aircox
work on QCombine, add QCombined
This commit is contained in:
parent
bc87e635c2
commit
ceecbcfa54
|
@ -13,6 +13,7 @@ class QCombine:
|
||||||
- when working on fields, we assume that they exists on all of them;
|
- when working on fields, we assume that they exists on all of them;
|
||||||
- for efficiency, there is no possibility to change order per field;
|
- for efficiency, there is no possibility to change order per field;
|
||||||
to do so, do it directly on the querysets
|
to do so, do it directly on the querysets
|
||||||
|
- we dont clone the combinator in order to avoid overhead
|
||||||
"""
|
"""
|
||||||
order_fields = None
|
order_fields = None
|
||||||
lists = None
|
lists = None
|
||||||
|
@ -21,20 +22,39 @@ class QCombine:
|
||||||
"""
|
"""
|
||||||
lists: list of querysets that are used to initialize the stuff.
|
lists: list of querysets that are used to initialize the stuff.
|
||||||
"""
|
"""
|
||||||
self.lists = lists or []
|
self.lists = list(lists) or []
|
||||||
|
|
||||||
|
def map(self, qs_func, non_qs = None):
|
||||||
|
"""
|
||||||
|
Map results of qs_func for QuerySet instance and of non_qs for
|
||||||
|
the others (if given), because QuerySet always clones itself.
|
||||||
|
"""
|
||||||
|
for i, qs in self.lists:
|
||||||
|
if issubclass(type(qs, QuerySet):
|
||||||
|
self.lists[i] = func(qs)
|
||||||
|
elif non_qs:
|
||||||
|
self.lists[i] = non_qs(qs)
|
||||||
|
|
||||||
|
def all(self):
|
||||||
|
self.map(lambda qs: qs.all())
|
||||||
|
|
||||||
def filter(self, **kwargs):
|
def filter(self, **kwargs):
|
||||||
for qs in self.lists:
|
self.map(lambda qs: qs.filter(**kwargs))
|
||||||
if issubclass(type(qs), QuerySet):
|
|
||||||
qs.filter(**kwargs)
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def exclude(self, **kwargs):
|
def exclude(self, **kwargs):
|
||||||
for qs in self.lists:
|
self.map(lambda qs: qs.exclude(**kwargs))
|
||||||
if issubclass(type(qs), QuerySet):
|
|
||||||
qs.exclude(**kwargs)
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def distinct(self, **kwargs):
|
||||||
|
self.map(qs.distinct())
|
||||||
|
return self
|
||||||
|
|
||||||
|
def get(self, **kwargs):
|
||||||
|
self.filter(**kwargs)
|
||||||
|
it = iter(self)
|
||||||
|
return next(it)
|
||||||
|
|
||||||
def order_by(self, *fields, reverse = False):
|
def order_by(self, *fields, reverse = False):
|
||||||
"""
|
"""
|
||||||
Order using these fields. For compatibility, if there is
|
Order using these fields. For compatibility, if there is
|
||||||
|
@ -49,28 +69,72 @@ class QCombine:
|
||||||
self.order_reverse = reverse
|
self.order_reverse = reverse
|
||||||
self.order_fields = fields
|
self.order_fields = fields
|
||||||
|
|
||||||
for qs in self.lists:
|
self.map(
|
||||||
if issubclass(type(qs), QuerySet):
|
lambda qs: qs.order_by(*fields),
|
||||||
qs.order_by(*fields)
|
lambda qs: sorted(
|
||||||
else:
|
qs,
|
||||||
qs.sort(key = operator.attrgetter(fields),
|
qs.sort(
|
||||||
reverse = reverse)
|
key = operator.attrgetter(fields),
|
||||||
|
reverse = reverse
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def clone(self):
|
||||||
|
"""
|
||||||
|
Make a clone of the class. Not that lists are copied, non-deeply
|
||||||
|
"""
|
||||||
|
return QCombine(*[
|
||||||
|
qs.all() if issubclass(type(qs), QuerySet) else qs.copy()
|
||||||
|
for qs in self.lists
|
||||||
|
])
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return sum([len(qs) for qs in self.lists])
|
return sum([len(qs) for qs in self.lists])
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
if self.order_fields:
|
if not self.order_fields:
|
||||||
|
return itertools.chain(self.lists)
|
||||||
|
|
||||||
|
# FIXME: need it lazy?
|
||||||
return heapq.merge(
|
return heapq.merge(
|
||||||
*self.lists,
|
*self.lists,
|
||||||
key = operator.attrgetter(*self.order_fields),
|
key = operator.attrgetter(*self.order_fields),
|
||||||
reverse = self.order_reverse
|
reverse = self.order_reverse
|
||||||
)
|
)
|
||||||
return itertools.chain(self.lists)
|
|
||||||
|
|
||||||
def __getitem__(self, k):
|
def __getitem__(self, k):
|
||||||
if type(k) == slice:
|
if type(k) == slice:
|
||||||
return list(itertools.islice(iter(self), k.start, k.stop, k.step))
|
it = itertools.islice(iter(self), k.start, k.stop, k.step)
|
||||||
return list(itertools.islice(iter(self), k))
|
else:
|
||||||
|
it = itertools.islice(iter(self), k)
|
||||||
|
return list(it)
|
||||||
|
|
||||||
|
|
||||||
|
class QCombined:
|
||||||
|
"""
|
||||||
|
This class is used to register a route for multiple models to a website.
|
||||||
|
A QCombine is created with qs for all given models when objects
|
||||||
|
property is retrieved.
|
||||||
|
|
||||||
|
Note: there no other use-case.
|
||||||
|
"""
|
||||||
|
def __init__(*models):
|
||||||
|
self.models = models
|
||||||
|
self._meta = self.Meta()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('publication')
|
||||||
|
verbose_name_plural = _('publications')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def objects(self):
|
||||||
|
"""
|
||||||
|
The QCombine that is returned actually holds the models' managers,
|
||||||
|
in order to simulate the same behaviour than a regular model.
|
||||||
|
"""
|
||||||
|
qs = QCombine([model.objects for model in self.models])
|
||||||
|
return qs
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ from django.contrib.contenttypes.models import ContentType
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||||
|
|
||||||
|
|
||||||
class Route:
|
class Route:
|
||||||
"""
|
"""
|
||||||
Base class for routing. Given a model, we generate url specific for each
|
Base class for routing. Given a model, we generate url specific for each
|
||||||
|
|
|
@ -146,7 +146,6 @@ class PostListView(BaseView, ListView):
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
print('get_query_set')
|
|
||||||
if self.route:
|
if self.route:
|
||||||
qs = self.route.get_queryset(self.model, self.request,
|
qs = self.route.get_queryset(self.model, self.request,
|
||||||
**self.kwargs)
|
**self.kwargs)
|
||||||
|
@ -193,7 +192,6 @@ class PostListView(BaseView, ListView):
|
||||||
self.model, self.request, **self.kwargs
|
self.model, self.request, **self.kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
print([post.title for post in context.get('object_list')])
|
|
||||||
context['list'] = self.list
|
context['list'] = self.list
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
4
notes.md
4
notes.md
|
@ -9,7 +9,6 @@
|
||||||
- programs:
|
- programs:
|
||||||
- schedule:
|
- schedule:
|
||||||
- (old) schedule.to_string unused? commented
|
- (old) schedule.to_string unused? commented
|
||||||
- check one week on two
|
|
||||||
- write more tests
|
- write more tests
|
||||||
- sounds:
|
- sounds:
|
||||||
- inline admin
|
- inline admin
|
||||||
|
@ -25,9 +24,10 @@
|
||||||
- cms.views
|
- cms.views
|
||||||
- cms.parts
|
- cms.parts
|
||||||
- cms.script
|
- cms.script
|
||||||
|
- cms.qcombine
|
||||||
- routes
|
- routes
|
||||||
- customized header depending on the list (e.g. thread -> link to thread parent)
|
- customized header depending on the list (e.g. thread -> link to thread parent)
|
||||||
- different models combinaison
|
- integrate QCombine
|
||||||
- admin cms
|
- admin cms
|
||||||
- content management -> do we use a markup language?
|
- content management -> do we use a markup language?
|
||||||
- sections:
|
- sections:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user