Skip to content

Commit

Permalink
* Added exceptions module and some starter exception classes.
Browse files Browse the repository at this point in the history
* 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
Duncan M. McGreggor committed Aug 31, 2011
1 parent aecc4be commit 0aea017
Show file tree
Hide file tree
Showing 7 changed files with 227 additions and 0 deletions.
1 change: 1 addition & 0 deletions django_riak_engine/client.py
Expand Up @@ -2,4 +2,5 @@


class DatabaseClient(BaseDatabaseClient):

executable_name = 'riak attach'
69 changes: 69 additions & 0 deletions django_riak_engine/compiler.py
@@ -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
11 changes: 11 additions & 0 deletions django_riak_engine/exceptions.py
@@ -0,0 +1,11 @@
from django.db.utils import DatabaseError


class RiakError(Exception):
"""
"""


class RiakDatabaseError(RiakError, DatabaseError):
"""
"""
30 changes: 30 additions & 0 deletions django_riak_engine/maps.py
@@ -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: ...,
}
92 changes: 92 additions & 0 deletions django_riak_engine/query.py
@@ -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)

16 changes: 16 additions & 0 deletions django_riak_engine/util/decorators.py
@@ -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
8 changes: 8 additions & 0 deletions docs/INTRO.rst
Expand Up @@ -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.