Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python 3 compatibility #60

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions djangotoolbox/db/base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django.utils.six.moves import cPickle as pickle
import datetime
import six
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from django.utils import six?


from django.conf import settings
from django.db.backends import (
Expand All @@ -11,8 +11,16 @@
BaseDatabaseIntrospection)
from django.db.utils import DatabaseError
from django.utils.functional import Promise
from django.utils.safestring import EscapeString, EscapeUnicode, SafeString, \
SafeUnicode
from django.utils.safestring import EscapeString, SafeString

if six.PY3:
import pickle
EscapeUnicode = EscapeString
SafeUnicode = SafeString
StandardError = Exception
else:
import cPickle as pickle
from django.utils.safestring import EscapeUnicode, SafeUnicode
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not continue to import pickle from django.utils.six.moves?

from django.utils import timezone

from .creation import NonrelDatabaseCreation
Expand Down Expand Up @@ -435,7 +443,7 @@ def _value_for_db_collection(self, value, field, field_kind, db_type,
value = (
(key, self._value_for_db(subvalue, subfield,
subkind, db_subtype, lookup))
for key, subvalue in value.iteritems())
for key, subvalue in six.iteritems(value))

# Return just a dict, a once-flattened list;
if db_type == 'dict':
Expand Down Expand Up @@ -492,7 +500,7 @@ def _value_from_db_collection(self, value, field, field_kind, db_type):
if db_type == 'list':
value = zip(value[::2], value[1::2])
else:
value = value.iteritems()
value = six.iteritems(value)

# DictField needs to hold a dict.
return dict(
Expand Down Expand Up @@ -551,7 +559,7 @@ def _value_for_db_model(self, value, field, field_kind, db_type, lookup):
value = (
(subfield.column, self._value_for_db(
subvalue, lookup=lookup, *self._convert_as(subfield, lookup)))
for subfield, subvalue in value.iteritems())
for subfield, subvalue in six.iteritems(value))

# Cast to a dict, interleave columns with values on a list,
# serialize, or return a generator.
Expand Down
7 changes: 4 additions & 3 deletions djangotoolbox/db/basecompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from django.db.models.sql.where import AND, OR
from django.db.utils import DatabaseError, IntegrityError
from django.utils.tree import Node
from django.utils import six
from django.db import connections

try:
Expand Down Expand Up @@ -209,7 +210,7 @@ def _decode_child(self, child):
raise DatabaseError("This database doesn't support filtering "
"on non-primary key ForeignKey fields.")

field = (f for f in opts.fields if f.column == column).next()
field = six.next(f for f in opts.fields if f.column == column)
assert field.rel is not None

value = self._normalize_lookup_value(
Expand Down Expand Up @@ -388,7 +389,7 @@ def execute_sql(self, result_type=MULTI):
Handles SQL-like aggregate queries. This class only emulates COUNT
by using abstract NonrelQuery.count method.
"""
aggregates = self.query.aggregate_select.values()
aggregates = list(self.query.aggregate_select.values())

# Simulate a count().
if aggregates:
Expand Down Expand Up @@ -503,7 +504,7 @@ def get_fields(self):
db_table = self.query.model._meta.db_table
only_load = dict((k, v) for k, v in only_load.items()
if v or k == db_table)
if len(only_load.keys()) > 1:
if len(only_load) > 1:
raise DatabaseError("Multi-table inheritance is not "
"supported by non-relational DBs %s." %
repr(only_load))
Expand Down
15 changes: 7 additions & 8 deletions djangotoolbox/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.db.models.fields.subclassing import Creator
from django.db.utils import IntegrityError
from django.db.models.fields.related import add_lazy_relation
from django.utils import six


__all__ = ('RawField', 'ListField', 'SetField', 'DictField',
Expand Down Expand Up @@ -81,11 +82,11 @@ def contribute_to_class(self, cls, name):
super(AbstractIterableField, self).contribute_to_class(cls, name)

# If items' field uses SubfieldBase we also need to.
item_metaclass = getattr(self.item_field, '__metaclass__', None)
if item_metaclass and issubclass(item_metaclass, models.SubfieldBase):
item_metaclass = type(self.item_field)
if issubclass(item_metaclass, models.SubfieldBase):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the __metaclass__ is the same as an object's type.

setattr(cls, self.name, Creator(self))

if isinstance(self.item_field, models.ForeignKey) and isinstance(self.item_field.rel.to, basestring):
if isinstance(self.item_field, models.ForeignKey) and isinstance(self.item_field.rel.to, six.string_types):
"""
If rel.to is a string because the actual class is not yet defined, look up the
actual class later. Refer to django.models.fields.related.RelatedField.contribute_to_class.
Expand Down Expand Up @@ -225,15 +226,15 @@ def get_internal_type(self):

def _map(self, function, iterable, *args, **kwargs):
return self._type((key, function(value, *args, **kwargs))
for key, value in iterable.iteritems())
for key, value in six.iteritems(iterable))

def validate(self, values, model_instance):
if not isinstance(values, dict):
raise ValidationError("Value is of type %r. Should be a dict." %
type(values))


class EmbeddedModelField(models.Field):
class EmbeddedModelField(six.with_metaclass(models.SubfieldBase, models.Field)):
"""
Field that allows you to embed a model instance.

Expand All @@ -245,8 +246,6 @@ class EmbeddedModelField(models.Field):
the embedded instance (not just pre_save, get_db_prep_* and
to_python).
"""
__metaclass__ = models.SubfieldBase

def __init__(self, embedded_model=None, *args, **kwargs):
self.embedded_model = embedded_model
kwargs.setdefault('default', None)
Expand All @@ -271,7 +270,7 @@ def _set_model(self, model):
our "model" attribute in its contribute_to_class method).
"""
self._model = model
if model is not None and isinstance(self.embedded_model, basestring):
if model is not None and isinstance(self.embedded_model, six.string_types):

def _resolve_lookup(self_, resolved_model, model):
self.embedded_model = resolved_model
Expand Down
12 changes: 6 additions & 6 deletions djangotoolbox/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ def validate_state(self, columns, *state_table):
current_state = object_list_to_table(
columns, self.model.all())[1:]
if not equal_lists(current_state, state_table):
print "DB state not valid:"
print "Current state:"
print columns
print("DB state not valid:")
print("Current state:")
print(columns)
for state in current_state:
print state
print "Should be:"
print(state)
print("Should be:")
for state in state_table:
print state
print(state)
self.fail("DB state not valid.")


Expand Down
17 changes: 9 additions & 8 deletions djangotoolbox/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from django.dispatch.dispatcher import receiver
from django.test import TestCase
from django.utils.unittest import expectedFailure, skip
from django.utils import six

from .fields import ListField, SetField, DictField, EmbeddedModelField

Expand Down Expand Up @@ -217,7 +218,7 @@ def test_chained_filter(self):
def test_setfield(self):
setdata = [1, 2, 3, 2, 1]
# At the same time test value conversion.
SetModel(setfield=map(str, setdata)).save()
SetModel(setfield=list(map(str, setdata))).save()
item = SetModel.objects.filter(setfield=3)[0]
self.assertEqual(item.setfield, set(setdata))
# This shouldn't raise an error because the default value is
Expand Down Expand Up @@ -392,29 +393,29 @@ def test_error_messages(self):

def test_typed_listfield(self):
EmbeddedModelFieldModel.objects.create(
typed_list=[SetModel(setfield=range(3)),
SetModel(setfield=range(9))],
ordered_list=[Target(index=i) for i in xrange(5, 0, -1)])
typed_list=[SetModel(setfield=list(range(3))),
SetModel(setfield=list(range(9)))],
ordered_list=[Target(index=i) for i in six.moves.xrange(5, 0, -1)])
obj = EmbeddedModelFieldModel.objects.get()
self.assertIn(5, obj.typed_list[1].setfield)
self.assertEqual([target.index for target in obj.ordered_list],
range(1, 6))
list(range(1, 6)))

def test_untyped_listfield(self):
EmbeddedModelFieldModel.objects.create(untyped_list=[
EmbeddedModel(someint=7),
OrderedListModel(ordered_ints=range(5, 0, -1)),
OrderedListModel(ordered_ints=list(range(5, 0, -1))),
SetModel(setfield=[1, 2, 2, 3])])
instances = EmbeddedModelFieldModel.objects.get().untyped_list
for instance, cls in zip(instances,
[EmbeddedModel, OrderedListModel, SetModel]):
self.assertIsInstance(instance, cls)
self.assertNotEqual(instances[0].auto_now, None)
self.assertEqual(instances[1].ordered_ints, range(1, 6))
self.assertEqual(instances[1].ordered_ints, list(range(1, 6)))

def test_untyped_dict(self):
EmbeddedModelFieldModel.objects.create(untyped_dict={
'a': SetModel(setfield=range(3)),
'a': SetModel(setfield=list(range(3))),
'b': DictModel(dictfield={'a': 1, 'b': 2}),
'c': DictModel(dictfield={}, auto_now={'y': 1})})
data = EmbeddedModelFieldModel.objects.get().untyped_dict
Expand Down