[damned-lies] Use the new UNION capability of the Django 1.11 ORM



commit bcab396142414d39f4c04829ec8291e225479b03
Author: Claude Paroz <claude 2xlibre net>
Date:   Wed Apr 5 11:22:22 2017 +0200

    Use the new UNION capability of the Django 1.11 ORM

 common/utils.py   |  122 +----------------------------------------------------
 vertimus/feeds.py |   30 ++++++-------
 2 files changed, 15 insertions(+), 137 deletions(-)
---
diff --git a/common/utils.py b/common/utils.py
index 13cd504..7c75383 100644
--- a/common/utils.py
+++ b/common/utils.py
@@ -1,4 +1,3 @@
-import operator
 from django.conf import settings
 from django.utils.translation import ugettext as _, get_language
 from languages.models import Language
@@ -23,137 +22,20 @@ def lc_sorted(*args, **kwargs):
         kwargs['key'] = lambda x: collator.getSortKey(key(x))
     return sorted(*args, **kwargs)
 
+
 def trans_sort_object_list(lst, tr_field):
     """Sort an object list with translated_name"""
     for l in lst:
         l.translated_name = _(getattr(l, tr_field))
     return lc_sorted(lst, key=lambda o: o.translated_name.lower())
 
-def merge_sorted_by_field(object_list1, object_list2, field):
-    """
-    Each call returns the next item, sorted by field in ascending order or in
-    descending order if the field name begins by a minus sign.
-
-    >>> from datetime import datetime
-    >>> import itertools
-    >>> class Foo(object):
-    ...     def __init__(self, num):
-    ...         self.num = num
-    ...     def __repr__(self):
-    ...         return str(self.num)
-    ...
-    >>> l1 = (Foo(1), Foo(8), Foo(5))
-    >>> l2 = (Foo(1), Foo(2), Foo(4), Foo(4), Foo(6))
-    >>> merge_sorted_by_field(l1, l2, 'num')
-    [1, 1, 2, 4, 4, 5, 6, 8]
-    >>> merge_sorted_by_field(l1, l2, 'num')[:3]
-    [1, 1, 2]
-    >>> l1 = (Foo(3), Foo(9), Foo(5))
-    >>> l2 = (Foo(6), Foo(4), Foo(4), Foo(2), Foo(1))
-    >>> [el.num for el in merge_sorted_by_field(l1, l2, '-num')]
-    [9, 6, 5, 4, 4, 3, 2, 1]
-    """
-    import itertools
-    if field is not None and field[0] == '-':
-        # Reverse the sort order
-        field = field[1:]
-        reverse = True
-    else:
-        reverse = False
-
-    return sorted(itertools.chain(object_list1, object_list2),
-                  key=lambda x: getattr(x, field),
-                  reverse=reverse)
-
-def imerge_sorted_by_field(object_list1, object_list2, field):
-    """
-    Each call returns the next item, sorted by field in ascending order or in
-    descending order if the field name begins by a minus sign.
-
-    This function is faster (only one comparison by iteration) and uses less
-    memory than merge_sorted_by_field (iterator) but the lists of objects must
-    be already sorted in the same order as field.
-
-    >>> from datetime import datetime
-    >>> import itertools
-    >>> class Foo(object):
-    ...     def __init__(self, num):
-    ...         self.num = num
-    ...     def __repr__(self):
-    ...         return str(self.num)
-    ...
-    >>> l1 = (Foo(1), Foo(3), Foo(5))
-    >>> l2 = (Foo(1), Foo(2), Foo(4), Foo(6), Foo(8))
-    >>> [el.num for el in imerge_sorted_by_field(l1, l2, 'num')]
-    [1, 1, 2, 3, 4, 5, 6, 8]
-    >>> [el.num for el in itertools.islice(imerge_sorted_by_field(l1, l2, 'num'), 3)]
-    [1, 1, 2]
-    >>> l1 = []
-    >>> [el.num for el in imerge_sorted_by_field(l1, l2, 'num')]
-    [1, 2, 4, 6, 8]
-    >>> l1 = (Foo(5), Foo(4), Foo(1))
-    >>> l2 = (Foo(6), Foo(4), Foo(4), Foo(2), Foo(1))
-    >>> [el.num for el in imerge_sorted_by_field(l1, l2, '-num')]
-    [6, 5, 4, 4, 4, 2, 1, 1]
-    """
-    if field is not None and field[0] == '-':
-        # Reverse the sort order
-        field = field[1:]
-        op = operator.gt
-    else:
-        op = operator.lt
-
-    iter1, iter2 = iter(object_list1), iter(object_list2)
-
-    # Too many try/except couples to my taste but I don't know how to filter the
-    # StopIteration to find the source.
-
-    try:
-        el1 = next(iter1)
-    except StopIteration:
-        # Finish the other list
-        while True:
-            el2 = next(iter2)
-            yield el2
-
-    try:
-        el2 = next(iter2)
-    except StopIteration:
-        # Finish the other list
-        while True:
-            # el1 is already fetched
-            yield el1
-            el1 = next(iter1)
-
-    while True:
-        if op(getattr(el1, field), getattr(el2, field)):
-            yield el1
-            try:
-                el1 = next(iter1)
-            except StopIteration:
-                # Finish the other list
-                while True:
-                    yield el2
-                    el2 = next(iter2)
-        else:
-            yield el2
-            try:
-                el2 = next(iter2)
-            except StopIteration:
-                # Finish the other list
-                while True:
-                    yield el1
-                    el1 = next(iter1)
 
 def is_site_admin(user):
     return user.is_superuser or settings.ADMIN_GROUP in [g.name for g in user.groups.all()]
 
+
 def get_user_locale(request):
     curlang = Language.get_language_from_ianacode(request.LANGUAGE_CODE)
     if curlang and curlang.locale == 'en':
         curlang = None
     return curlang
-
-if __name__ == "__main__":
-    import doctest
-    doctest.testmod()
diff --git a/vertimus/feeds.py b/vertimus/feeds.py
index 7d27a44..e199123 100644
--- a/vertimus/feeds.py
+++ b/vertimus/feeds.py
@@ -1,12 +1,12 @@
-from itertools import islice
 from django.core import urlresolvers
+from django.contrib.sites.models import Site
 from django.contrib.syndication.views import Feed, FeedDoesNotExist
 from django.utils.translation import ugettext_lazy as _
-from django.contrib.sites.models import Site
+
 from languages.models import Language
 from teams.models import Team
 from vertimus.models import Action, ActionArchived
-from common.utils import imerge_sorted_by_field
+
 
 class LatestActionsByLanguage(Feed):
     title_template = 'feeds/actions_title.html'
@@ -29,13 +29,11 @@ class LatestActionsByLanguage(Feed):
         return _("Latest actions of the GNOME Translation Project for the %s language") % obj.name
 
     def items(self, obj):
-        # The Django ORM doesn't provide the UNION SQL feature :-(
-        # so we need to fetch twice more objects than required
-        actions = 
Action.objects.filter(state_db__language=obj.id).select_related('state_db').order_by('-created')[:20]
-        archived_actions = 
ActionArchived.objects.filter(state_db__language=obj.id).select_related('state_db').order_by('-created')[:20]
-
-        # islice avoid to fetch too many objects
-        return islice(imerge_sorted_by_field(actions, archived_actions, '-created'), 20)
+        actions = Action.objects.filter(state_db__language=obj.id).select_related('state_db'
+            ).union(
+            
ActionArchived.objects.filter(state_db__language=obj.id).defer('sequence').select_related('state_db')
+        )
+        return actions.order_by('-created')[:20]
 
     def item_link(self, item):
         link = urlresolvers.reverse('vertimus_by_names',
@@ -73,13 +71,11 @@ class LatestActionsByTeam(Feed):
         return _("Latest actions made by the %s team of the GNOME Translation Project") % obj
 
     def items(self, obj):
-        # The Django ORM doesn't provide the UNION SQL feature :-(
-        # so we need to fetch twice more objects than required
-        actions = 
Action.objects.filter(state_db__language__team=obj.id).select_related('state_db').order_by('-created')[:20]
-        archived_actions = 
ActionArchived.objects.filter(state_db__language__team=obj.id).select_related('state_db').order_by('-created')[:20]
-
-        # islice avoid to fetch too many objects
-        return islice(imerge_sorted_by_field(actions, archived_actions, '-created'), 20)
+        actions = Action.objects.filter(state_db__language__team=obj.id).select_related('state_db'
+            ).union(
+            
ActionArchived.objects.filter(state_db__language__team=obj.id).defer('sequence').select_related('state_db')
+        )
+        return actions.order_by('-created')[:20]
 
     def item_link(self, item):
         link = urlresolvers.reverse('vertimus_by_names',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]