Permalink
Browse files

* Added exceptions module and some starter exception classes.

* Added a utility module for decorators.
* Added stubs from the django-nonrel project template code
  (https://bitbucket.org/wkornewald/backend-template/).
* Added a "Community" section to the intro, providing a link to the new Google
  group discussion mail list/forum.
  • Loading branch information...
1 parent aecc4be commit 0aea017574d7143184b1e24a25328a205e9f5cb6 Duncan M. McGreggor committed Aug 31, 2011
@@ -2,4 +2,5 @@
class DatabaseClient(BaseDatabaseClient):
+
executable_name = 'riak attach'
@@ -0,0 +1,69 @@
+from djangotoolbox.db.basecompiler import (
+ NonrelCompiler, NonrelInsertCompiler, NonrelUpdateCompiler,
+ NonrelDeleteCompiler)
+
+from django_riak_engine import query
+from django_riak_engine.util import decorators
+
+
+class SQLCompiler(NonrelCompiler):
+
+ query_class = query.BackendQuery
+
+ # This gets called for each field type when you fetch() an entity.
+ # db_type is the string that you used in the DatabaseCreation mapping
+ def convert_value_from_db(self, db_type, value):
+ # TODO: implement this
+
+ # Handle list types
+ if (isinstance(value, (list, tuple))
+ and len(value)
+ and db_type.startswith('ListField:')):
+ db_sub_type = db_type.split(':', 1)[1]
+ value = [self.convert_value_from_db(db_sub_type, subvalue)
+ for subvalue in value]
+ elif isinstance(value, str):
+ # Always retrieve strings as unicode
+ value = value.decode('utf-8')
+ return value
+
+ # This gets called for each field type when you insert() an entity.
+ # db_type is the string that you used in the DatabaseCreation mapping
+ def convert_value_for_db(self, db_type, value):
+ # TODO: implement this
+
+ if isinstance(value, unicode):
+ value = unicode(value)
+ elif isinstance(value, str):
+ # Always store strings as unicode
+ value = value.decode('utf-8')
+ elif isinstance(value, (list, tuple)) and len(value) and \
+ db_type.startswith('ListField:'):
+ db_sub_type = db_type.split(':', 1)[1]
+ value = [self.convert_value_for_db(db_sub_type, subvalue)
+ for subvalue in value]
+ return value
+
+
+# This handles both inserts and updates of individual entities
+class SQLInsertCompiler(NonrelInsertCompiler, SQLCompiler):
+
+ @decorators.safe_call
+ def insert(self, data, return_id=False):
+ # TODO: implement this
+ pk_column = self.query.get_meta().pk.column
+ if pk_column in data:
+ data['_id'] = data[pk_column]
+ del data[pk_column]
+ # XXX use save from database connection
+ pk = save_entity(self.connection.db_connection,
+ self.query.get_meta().db_table, data)
+ return pk
+
+
+class SQLUpdateCompiler(NonrelUpdateCompiler, SQLCompiler):
+ pass
+
+
+class SQLDeleteCompiler(NonrelDeleteCompiler, SQLCompiler):
+ pass
@@ -0,0 +1,11 @@
+from django.db.utils import DatabaseError
+
+
+class RiakError(Exception):
+ """
+ """
+
+
+class RiakDatabaseError(RiakError, DatabaseError):
+ """
+ """
View
@@ -0,0 +1,30 @@
+# TODO: Change this to match your DB
+# Valid query types (a dictionary is used for speedy lookups).
+OPERATORS_MAP = {
+ 'exact': '=',
+ 'gt': '>',
+ 'gte': '>=',
+ 'lt': '<',
+ 'lte': '<=',
+ 'in': 'IN',
+ 'isnull': lambda lookup_type, value: ('=' if value else '!=', None),
+
+ #'startswith': lambda lookup_type, value: ...,
+ #'range': lambda lookup_type, value: ...,
+ #'year': lambda lookup_type, value: ...,
+}
+
+
+NEGATION_MAP = {
+ 'exact': '!=',
+ 'gt': '<=',
+ 'gte': '<',
+ 'lt': '>=',
+ 'lte': '>',
+ 'in': 'NOTIN',
+ 'isnull': lambda lookup_type, value: ('!=' if value else '=', None),
+
+ #'startswith': lambda lookup_type, value: ...,
+ #'range': lambda lookup_type, value: ...,
+ #'year': lambda lookup_type, value: ...,
+}
@@ -0,0 +1,92 @@
+from django.db.utils import DatabaseError
+
+from djangotoolbox.db import basecompiler
+
+from django_riak_engine import maps
+from django_riak_engine.util import decorators
+
+
+class BackendQuery(basecompiler.NonrelQuery):
+
+ def __init__(self, compiler, fields):
+ super(BackendQuery, self).__init__(compiler, fields)
+ # TODO: add your initialization code here
+ # note that django_mongodb_engine sets self.db_query to a dict
+ self.db_query = LowLevelQuery(self.connection.db_connection)
+
+ # This is needed for debugging
+ def __repr__(self):
+ # TODO: add some meaningful query string for debugging
+ return '<BackendQuery: ...>'
+
+ @decorators.safe_call
+ def fetch(self):
+ # TODO: run your low-level query here
+ low_mark, high_mark = self.limits
+ if high_mark is None:
+ # Infinite fetching
+ results = self.db_query.fetch_infinite(offset=low_mark)
+ elif high_mark > low_mark:
+ # Range fetching
+ results = self.db_query.fetch_range(high_mark - low_mark, low_mark)
+ else:
+ results = ()
+
+ for entity in results:
+ entity[self.query.get_meta().pk.column] = entity['_id']
+ del entity['_id']
+ yield entity
+
+ @decorators.safe_call
+ def count(self, limit=None):
+ # TODO: implement this
+ return self.db_query.count(limit)
+
+ @decorators.safe_call
+ def delete(self):
+ # TODO: implement this
+ self.db_query.delete()
+
+ @decorators.safe_call
+ def order_by(self, ordering):
+ # TODO: implement this
+ for order in ordering:
+ if order.startswith('-'):
+ column, direction = order[1:], 'DESC'
+ else:
+ column, direction = order, 'ASC'
+ if column == self.query.get_meta().pk.column:
+ column = '_id'
+ self.db_query.add_ordering(column, direction)
+
+ # This function is used by the default add_filters() implementation which
+ # only supports ANDed filter rules and simple negation handling for
+ # transforming OR filters to AND filters:
+ # NOT (a OR b) => (NOT a) AND (NOT b)
+ @decorators.safe_call
+ def add_filter(self, column, lookup_type, negated, db_type, value):
+ # TODO: implement this or the add_filters() function (see the base
+ # class for a sample implementation)
+
+ # Emulated/converted lookups
+ if column == self.query.get_meta().pk.column:
+ column = '_id'
+
+ if negated:
+ try:
+ op = maps.NEGATION_MAP[lookup_type]
+ except KeyError:
+ raise DatabaseError("Lookup type %r can't be negated" % lookup_type)
+ else:
+ try:
+ op = maps.OPERATORS_MAP[lookup_type]
+ except KeyError:
+ raise DatabaseError("Lookup type %r isn't supported" % lookup_type)
+
+ # Handle special-case lookup types
+ if callable(op):
+ op, value = op(lookup_type, value)
+
+ db_value = self.convert_value_for_db(db_type, value)
+ self.db_query.filter(column, op, db_value)
+
@@ -0,0 +1,16 @@
+import functools
+import sys
+
+from django.db.utils import DatabaseError
+
+from django_riak_engine.exceptions import RiakDatabaseError
+
+
+def safe_call(func):
+ @functools.wraps(func)
+ def _func(*args, **kwargs):
+ try:
+ return func(*args, **kwargs)
+ except RiakDatabaseError, e:
+ raise DatabaseError, DatabaseError(*tuple(e)), sys.exc_info()[2]
+ return _func
View
@@ -28,4 +28,12 @@ About django-riak-engine
In order to be used in Django-nonrel applications, this project is cenetered
upon creating the necessary database adaptors for Django.
+Community
+---------
+
+There is a development discussion Google group here for public participation of
+anyone interested in coding on the project:
+
+ * http://groups.google.com/group/django-riak-dev
+

0 comments on commit 0aea017

Please sign in to comment.