From 2ad5e284526b2b2f55e50d5cf2cf293e990c5e80 Mon Sep 17 00:00:00 2001 From: Tyson Clugg Date: Fri, 12 Jun 2015 23:08:27 +1000 Subject: [PATCH 1/4] WIP on Django 1.8 compatibility. --- dddp/api.py | 20 +++++++++++++------- dddp/msg.py | 18 ++++++++++++------ setup.py | 4 ++-- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/dddp/api.py b/dddp/api.py index 603748d..dfb4c81 100644 --- a/dddp/api.py +++ b/dddp/api.py @@ -11,8 +11,11 @@ import dbarray from django.conf import settings from django.contrib.auth import get_user_model from django.db import connection, connections -from django.db.models import aggregates, Q -from django.db.models.expressions import ExpressionNode +from django.db.models import aggregates, Q, Expression +try: + from django.db.models.expressions import ExpressionNode +except ImportError: + ExpressionNode = None from django.db.models.sql import aggregates as sql_aggregates from django.utils.encoding import force_text from django.db import DatabaseError @@ -353,11 +356,14 @@ class Collection(APIMixin): def serialize(self, obj, meteor_ids): """Generate a DDP msg for obj with specified msg type.""" - # check for F expressions - exps = [ - name for name, val in vars(obj).items() - if isinstance(val, ExpressionNode) - ] + if ExpressionNode is None: + exps = False + else: + # check for F expressions + exps = [ + name for name, val in vars(obj).items() + if isinstance(val, ExpressionNode) + ] if exps: # clone/update obj with values but only for the expression fields obj = deepcopy(obj) diff --git a/dddp/msg.py b/dddp/msg.py index 892f49f..4029b5b 100644 --- a/dddp/msg.py +++ b/dddp/msg.py @@ -1,16 +1,22 @@ """Django DDP utils for DDP messaging.""" from copy import deepcopy from dddp import THREAD_LOCAL as this, REMOVED -from django.db.models.expressions import ExpressionNode +try: + from django.db.models.expressions import ExpressionNode +except AttributeError: + ExpressionNode = None def obj_change_as_msg(obj, msg): """Generate a DDP msg for obj with specified msg type.""" - # check for F expressions - exps = [ - name for name, val in vars(obj).items() - if isinstance(val, ExpressionNode) - ] + if ExpressionNode is None: + exps = False + else: + # check for F expressions + exps = [ + name for name, val in vars(obj).items() + if isinstance(val, ExpressionNode) + ] if exps: # clone and update obj with values but only for the expression fields obj = deepcopy(obj) diff --git a/setup.py b/setup.py index 99d473e..32e4432 100644 --- a/setup.py +++ b/setup.py @@ -14,10 +14,10 @@ setup( packages=find_packages(), include_package_data=True, install_requires=[ - 'Django>=1.7,<1.8', + 'Django>=1.7', 'psycopg2>=2.5.4', 'gevent>=1.0', - 'gevent-websocket>=0.9', + 'gevent-websocket>=0.9,!=0.9.4', 'meteor-ejson>=1.0', 'psycogreen>=1.0', 'django-dbarray>=0.2', From 16e48e50bef20578d433ad1ae6d81141e1fb14a9 Mon Sep 17 00:00:00 2001 From: Tyson Clugg Date: Sun, 14 Jun 2015 00:02:29 +1000 Subject: [PATCH 2/4] Don't barf on multiple invocations of greenify(). --- dddp/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dddp/__init__.py b/dddp/__init__.py index a2d4dcd..7c3c404 100644 --- a/dddp/__init__.py +++ b/dddp/__init__.py @@ -26,10 +26,9 @@ REMOVED = 'removed' def greenify(): """Patch threading and psycopg2 modules for green threads.""" - if 'threading' in sys.modules: + from gevent.monkey import patch_all, saved + if ('threading' in sys.modules) and ('threading' not in saved): raise Exception('threading module loaded before patching!') - - from gevent.monkey import patch_all patch_all() from psycogreen.gevent import patch_psycopg From 9b18e4833a3550d424aa18fddbf5373d142ce3f1 Mon Sep 17 00:00:00 2001 From: Tyson Clugg Date: Sun, 14 Jun 2015 00:19:49 +1000 Subject: [PATCH 3/4] Remove unused imports from AppConfig module. --- dddp/apps.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/dddp/apps.py b/dddp/apps.py index 9cbd8bc..6692e6b 100644 --- a/dddp/apps.py +++ b/dddp/apps.py @@ -4,11 +4,8 @@ from __future__ import print_function from django.apps import AppConfig from django.conf import settings, ImproperlyConfigured -from django.db import DatabaseError -from django.db.models import signals from dddp import autodiscover -from dddp.models import Connection class DjangoDDPConfig(AppConfig): From 07ccc014bc362e857abef5fabaeb97608e71b68e Mon Sep 17 00:00:00 2001 From: Tyson Clugg Date: Sun, 14 Jun 2015 03:31:27 +1000 Subject: [PATCH 4/4] Fix array aggregate for Django 1.8 --- dddp/api.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/dddp/api.py b/dddp/api.py index dfb4c81..cdce7b7 100644 --- a/dddp/api.py +++ b/dddp/api.py @@ -11,11 +11,11 @@ import dbarray from django.conf import settings from django.contrib.auth import get_user_model from django.db import connection, connections -from django.db.models import aggregates, Q, Expression +from django.db.models import aggregates, Q try: from django.db.models.expressions import ExpressionNode except ImportError: - ExpressionNode = None + from django.db.models import Expression as ExpressionNode from django.db.models.sql import aggregates as sql_aggregates from django.utils.encoding import force_text from django.db import DatabaseError @@ -51,6 +51,7 @@ class Array(aggregates.Aggregate): """Array aggregate function.""" func = 'ARRAY' + function = 'array_agg' name = 'Array' def add_to_query(self, query, alias, col, source, is_summary): @@ -67,9 +68,17 @@ class Array(aggregates.Aggregate): return 'ArrayType' new_source = ArrayField() - super(Array, self).add_to_query( - query, alias, col, new_source, is_summary, - ) + try: + super(Array, self).add_to_query( + query, alias, col, new_source, is_summary, + ) + except AttributeError: + query.aggregates[alias] = new_source + + def convert_value(self, value, expression, connection, context): + if not value: + return [] + return value def api_endpoint(path_or_func): @@ -356,14 +365,11 @@ class Collection(APIMixin): def serialize(self, obj, meteor_ids): """Generate a DDP msg for obj with specified msg type.""" - if ExpressionNode is None: - exps = False - else: - # check for F expressions - exps = [ - name for name, val in vars(obj).items() - if isinstance(val, ExpressionNode) - ] + # check for F expressions + exps = [ + name for name, val in vars(obj).items() + if isinstance(val, ExpressionNode) + ] if exps: # clone/update obj with values but only for the expression fields obj = deepcopy(obj)