Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 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
Duncan M. McGreggor
committed
Aug 31, 2011
1 parent
aecc4be
commit 0aea017
Showing
7 changed files
with
227 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,5 @@ | |
|
||
|
||
class DatabaseClient(BaseDatabaseClient): | ||
|
||
executable_name = 'riak attach' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from django.db.utils import DatabaseError | ||
|
||
|
||
class RiakError(Exception): | ||
""" | ||
""" | ||
|
||
|
||
class RiakDatabaseError(RiakError, DatabaseError): | ||
""" | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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: ..., | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters