Skip to content

Commit

Permalink
Merge pull request django-nonrel#64 from kavdev/master
Browse files Browse the repository at this point in the history
django 1.8 compatibility in the db module
  • Loading branch information
aburgel committed Jul 19, 2015
2 parents 80b16cb + 3745f08 commit aff2ac5
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 60 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Changelog
=========

Version 1.8.0 (Jul 12, 2015)
-------------

* Added support for Django 1.8 in the db package

Version 1.7.0 (Jul 12, 2015)
-------------

Expand Down
57 changes: 41 additions & 16 deletions djangotoolbox/db/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,42 @@
import datetime

from django.conf import settings
from django.db.backends import (
BaseDatabaseFeatures,
BaseDatabaseOperations,
BaseDatabaseWrapper,
BaseDatabaseClient,
BaseDatabaseValidation,
BaseDatabaseIntrospection)

import django

if django.VERSION < (1, 8):
from django.db.backends import (
BaseDatabaseFeatures,
BaseDatabaseOperations,
BaseDatabaseWrapper,
BaseDatabaseClient,
BaseDatabaseValidation,
BaseDatabaseIntrospection)
else:
from django.db.backends.base.base import BaseDatabaseWrapper
from django.db.backends.base.client import BaseDatabaseClient
from django.db.backends.base.features import BaseDatabaseFeatures
from django.db.backends.base.validation import BaseDatabaseValidation
from django.db.backends.base.introspection import BaseDatabaseIntrospection
from django.db.backends.base.operations import BaseDatabaseOperations

from django.db.utils import DatabaseError
from django.utils.functional import Promise
from django.utils.safestring import EscapeString, EscapeUnicode, SafeString, \
SafeUnicode
from django.utils import timezone
from django.utils.functional import Promise

if django.VERSION < (1, 5):
from django.utils.encoding import (smart_unicode as smart_text,
smart_str as smart_bytes)
else:
from django.utils.encoding import smart_text, smart_bytes

if django.VERSION < (1, 5):
from django.utils.safestring import (SafeString as SafeBytes,
SafeUnicode as SafeText,
EscapeString as EscapeBytes,
EscapeUnicode as EscapeText)
else:
from django.utils.safestring import SafeBytes, SafeText, EscapeBytes, EscapeText

from .creation import NonrelDatabaseCreation

Expand Down Expand Up @@ -329,14 +353,14 @@ def _value_for_db(self, value, field, field_kind, db_type, lookup):
# Also research cases of database operations not done
# through the sql.Query.
if isinstance(value, Promise):
value = unicode(value)
value = smart_text(value)

# Django wraps strings marked as safe or needed escaping,
# convert them to just strings for type-inspecting back-ends.
if isinstance(value, (SafeString, EscapeString)):
value = str(value)
elif isinstance(value, (SafeUnicode, EscapeUnicode)):
value = unicode(value)
if isinstance(value, (SafeBytes, EscapeBytes)):
value = smart_bytes(value)
elif isinstance(value, (SafeText, EscapeText)):
value = smart_text(value)

# Convert elements of collection fields.
if field_kind in ('ListField', 'SetField', 'DictField',):
Expand Down Expand Up @@ -661,7 +685,7 @@ def close(self):


class Database(object):
class Error(StandardError):
class Error(Exception):
pass

class InterfaceError(Error):
Expand All @@ -688,6 +712,7 @@ class ProgrammingError(DatabaseError):
class NotSupportedError(DatabaseError):
pass


class NonrelDatabaseWrapper(BaseDatabaseWrapper):

Database = Database
Expand Down
67 changes: 47 additions & 20 deletions djangotoolbox/db/basecompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from django.conf import settings
from django.db.models.fields import NOT_PROVIDED
from django.db.models.query import QuerySet
from django.db.models.sql import aggregates as sqlaggregates
from django.db.models.sql.compiler import SQLCompiler
from django.db.models.sql.constants import MULTI, SINGLE
from django.db.models.sql.where import AND, OR
Expand Down Expand Up @@ -213,7 +212,12 @@ def _decode_child(self, child):
value = rhs_params

packed = child.lhs.get_group_by_cols()[0]
alias, column = packed

if django.VERSION < (1, 8):
alias, column = packed
else:
alias = packed.alias
column = packed.target.column
field = child.lhs.output_field

opts = self.query.model._meta
Expand Down Expand Up @@ -390,17 +394,19 @@ def __init__(self, query, connection, using):
# Public API
# ----------------------------------------------

def results_iter(self):
def results_iter(self, results=None):
"""
Returns an iterator over the results from executing query given
to this compiler. Called by QuerySet methods.
"""
fields = self.get_fields()
try:
results = self.build_query(fields).fetch(
self.query.low_mark, self.query.high_mark)
except EmptyResultSet:
results = []

if results is None:
fields = self.get_fields()
try:
results = self.build_query(fields).fetch(
self.query.low_mark, self.query.high_mark)
except EmptyResultSet:
results = []

for entity in results:
yield self._make_result(entity, fields)
Expand All @@ -413,27 +419,39 @@ def execute_sql(self, result_type=MULTI):
Handles SQL-like aggregate queries. This class only emulates COUNT
by using abstract NonrelQuery.count method.
"""
self.pre_sql_setup()

aggregates = self.query.aggregate_select.values()

# Simulate a count().
if aggregates:
assert len(aggregates) == 1
aggregate = aggregates[0]
assert isinstance(aggregate, sqlaggregates.Count)
if django.VERSION < (1, 8):
if aggregate.sql_function != 'COUNT':
raise NotImplementedError("The database backend only supports count() queries.")
else:
if aggregate.function != 'COUNT':
raise NotImplementedError("The database backend only supports count() queries.")

opts = self.query.get_meta()
if aggregate.col != '*' and aggregate.col != (opts.db_table, opts.pk.column):
raise DatabaseError("This database backend only supports "
"count() queries on the primary key.")

if django.VERSION < (1, 8):
if aggregate.col != '*' and aggregate.col != (opts.db_table, opts.pk.column):
raise DatabaseError("This database backend only supports "
"count() queries on the primary key.")
else:
# Fair warning: the latter part of this or statement hasn't been tested
if aggregate.input_field.value != '*' and aggregate.input_field != (opts.db_table, opts.pk.column):
raise DatabaseError("This database backend only supports "
"count() queries on the primary key.")

count = self.get_count()
if result_type is SINGLE:
return [count]
elif result_type is MULTI:
return [[count]]

raise NotImplementedError("The database backend only supports "
"count() queries.")

# ----------------------------------------------
# Additional NonrelCompiler API
# ----------------------------------------------
Expand All @@ -453,8 +471,9 @@ def _make_result(self, entity, fields):
value = field.get_default()
else:
value = self.ops.value_from_db(value, field)
value = self.query.convert_values(value, field,
self.connection)
# This is the default behavior of ``query.convert_values``
# until django 1.8, where multiple converters are a thing.
value = self.connection.ops.convert_values(value, field)
if value is None and not field.null:
raise IntegrityError("Non-nullable field %s can't be None!" %
field.name)
Expand Down Expand Up @@ -505,8 +524,12 @@ def build_query(self, fields=None):
query.order_by(self._get_ordering())

# This at least satisfies the most basic unit tests.
if connections[self.using].use_debug_cursor or (connections[self.using].use_debug_cursor is None and settings.DEBUG):
self.connection.queries.append({'sql': repr(query)})
if django.VERSION < (1, 8):
if connections[self.using].use_debug_cursor or (connections[self.using].use_debug_cursor is None and settings.DEBUG):
self.connection.queries.append({'sql': repr(query)})
else:
if connections[self.using].force_debug_cursor or (connections[self.using].force_debug_cursor is None and settings.DEBUG):
self.connection.queries.append({'sql': repr(query)})
return query

def get_fields(self):
Expand Down Expand Up @@ -592,6 +615,8 @@ class NonrelInsertCompiler(NonrelCompiler):
"""

def execute_sql(self, return_id=False):
self.pre_sql_setup()

to_insert = []
pk_field = self.query.get_meta().pk
for obj in self.query.objs:
Expand Down Expand Up @@ -637,6 +662,8 @@ def insert(self, values, return_id):
class NonrelUpdateCompiler(NonrelCompiler):

def execute_sql(self, result_type):
self.pre_sql_setup()

values = []
for field, _, value in self.query.values:
if hasattr(value, 'prepare_database_save'):
Expand Down
7 changes: 6 additions & 1 deletion djangotoolbox/db/creation.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
from django.db.backends.creation import BaseDatabaseCreation
import django

if django.VERSION < (1, 8):
from django.db.backends.creation import BaseDatabaseCreation
else:
from django.db.backends.base.creation import BaseDatabaseCreation


class NonrelDatabaseCreation(BaseDatabaseCreation):
Expand Down
47 changes: 24 additions & 23 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,28 @@
pass

setup(name='djangotoolbox',
version='1.7.0',
description=DESCRIPTION,
long_description=LONG_DESCRIPTION,
author='Waldemar Kornewald',
author_email='[email protected]',
url='https://github.com/django-nonrel/djangotoolbox',
packages=find_packages(),
license='3-clause BSD',
zip_safe=False,
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
'Framework :: Django',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2.5',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Topic :: Database',
'Topic :: Software Development :: Libraries :: Python Modules',
],
version='1.8.0',
description=DESCRIPTION,
long_description=LONG_DESCRIPTION,
author='Waldemar Kornewald',
author_email='[email protected]',
url='https://github.com/django-nonrel/djangotoolbox',
packages=find_packages(),
license='3-clause BSD',
zip_safe=False,
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
'Framework :: Django',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2.5',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.4',
'Topic :: Database',
'Topic :: Software Development :: Libraries :: Python Modules',
],
)

0 comments on commit aff2ac5

Please sign in to comment.