diff --git a/cms/qcombine.py b/cms/qcombine.py index 63c80f1..9e4a227 100644 --- a/cms/qcombine.py +++ b/cms/qcombine.py @@ -13,6 +13,7 @@ class QCombine: - when working on fields, we assume that they exists on all of them; - for efficiency, there is no possibility to change order per field; to do so, do it directly on the querysets + - we dont clone the combinator in order to avoid overhead """ order_fields = None lists = None @@ -21,20 +22,39 @@ class QCombine: """ 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): - for qs in self.lists: - if issubclass(type(qs), QuerySet): - qs.filter(**kwargs) + self.map(lambda qs: qs.filter(**kwargs)) return self def exclude(self, **kwargs): - for qs in self.lists: - if issubclass(type(qs), QuerySet): - qs.exclude(**kwargs) + self.map(lambda qs: qs.exclude(**kwargs)) 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): """ Order using these fields. For compatibility, if there is @@ -49,28 +69,72 @@ class QCombine: self.order_reverse = reverse self.order_fields = fields - for qs in self.lists: - if issubclass(type(qs), QuerySet): - qs.order_by(*fields) - else: - qs.sort(key = operator.attrgetter(fields), - reverse = reverse) + self.map( + lambda qs: qs.order_by(*fields), + lambda qs: sorted( + qs, + qs.sort( + key = operator.attrgetter(fields), + reverse = reverse + ) + ) + ) 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): return sum([len(qs) for qs in self.lists]) def __iter__(self): - if self.order_fields: - return heapq.merge( - *self.lists, - key = operator.attrgetter(*self.order_fields), - reverse = self.order_reverse - ) - return itertools.chain(self.lists) + if not self.order_fields: + return itertools.chain(self.lists) + + # FIXME: need it lazy? + return heapq.merge( + *self.lists, + key = operator.attrgetter(*self.order_fields), + reverse = self.order_reverse + ) def __getitem__(self, k): if type(k) == slice: - return list(itertools.islice(iter(self), k.start, k.stop, k.step)) - return list(itertools.islice(iter(self), k)) + it = itertools.islice(iter(self), k.start, k.stop, k.step) + 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 + diff --git a/cms/routes.py b/cms/routes.py index c31f395..f76a02c 100644 --- a/cms/routes.py +++ b/cms/routes.py @@ -5,7 +5,6 @@ from django.contrib.contenttypes.models import ContentType from django.utils import timezone from django.utils.translation import ugettext as _, ugettext_lazy - class Route: """ Base class for routing. Given a model, we generate url specific for each diff --git a/cms/views.py b/cms/views.py index c380ab9..542e2f6 100644 --- a/cms/views.py +++ b/cms/views.py @@ -146,7 +146,6 @@ class PostListView(BaseView, ListView): return super().dispatch(request, *args, **kwargs) def get_queryset(self): - print('get_query_set') if self.route: qs = self.route.get_queryset(self.model, self.request, **self.kwargs) @@ -193,7 +192,6 @@ class PostListView(BaseView, ListView): self.model, self.request, **self.kwargs ) - print([post.title for post in context.get('object_list')]) context['list'] = self.list return context diff --git a/notes.md b/notes.md index 3288469..6a508a9 100644 --- a/notes.md +++ b/notes.md @@ -9,7 +9,6 @@ - programs: - schedule: - (old) schedule.to_string unused? commented - - check one week on two - write more tests - sounds: - inline admin @@ -25,9 +24,10 @@ - cms.views - cms.parts - cms.script + - cms.qcombine - routes - customized header depending on the list (e.g. thread -> link to thread parent) - - different models combinaison + - integrate QCombine - admin cms - content management -> do we use a markup language? - sections: