Opened 10 years ago

Closed 10 years ago

#21754 closed Bug (fixed)

Object discrepency between python2.x and python3.x with model field "to_python" and "get_prep_value" when retrieving object from database

Reported by: Troy Grosfield Owned by: nobody
Component: Python 3 Version: 1.6
Severity: Normal Keywords: python3
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I put together the following example to demonstrate the differences between python 2 and python 3 and how they handle coercing raw data into model objects when interacting with the db. Ignore the lame example, it's just to demonstrate the error with python 3.

Current setup:

  • python2 test is using python 2.7.6
  • python3 test is using python 3.3.3
  • Using sqlite backend
# Model Field and model
class IntStoredAsStringField(models.CharField):
    __metaclass__ = models.SubfieldBase

    def __init__(self, max_length=2000, *args, **kwargs):
        super(IntStoredAsStringField, self).__init__(max_length=max_length,
                                                     *args, **kwargs)

    def to_python(self, value):
        if isinstance(value, int):
            return value

        return int(value)

    def get_prep_value(self, value):
        return smart_text(value)


class SomeModel(models.Model):
    int_stored_as_string_field = IntStoredAsStringField()
# test
class Py2To3ErrorTestCase(TestCase):

    def test_show_error(self):
        some_int = 5
        obj = SomeModel(int_stored_as_string_field=some_int)
        obj.save()

        obj_db = SomeModel.objects.get(id=obj.id)
        # field "to_python" method gets called when retrieving the object 
        # in python 2, doesn't in python 3 which causes this test to fail
        # in python 3.
        self.assertEqual(obj_db.int_stored_as_string_field,
                         obj.int_stored_as_string_field)

Test output succeeds in python 2. In python 3 I see the following error:

======================================================================
FAIL: test_show_error (Py2To3ErrorTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "some/path/test_model_fields.py", line 150, in test_show_error
    obj.int_stored_as_string_field)
AssertionError: '5' != 5

The field's "to_python(...)" method doesn't get called in python 3 like it does in python 2 when retrieving objects from the db. Am I missing something here?

Change History (2)

comment:1 by Marc Tamlyn, 10 years ago

__metaclass__ does nothing on python 3. Can you try your example using http://pythonhosted.org/six/#six.with_metaclass?

comment:2 by Troy Grosfield, 10 years ago

Resolution: fixed
Status: newclosed

That did the trick! Thanks @mjtamlyn! Here's the working change for the example above:

from django.utils.six import with_metaclass

class IntStoredAsStringField(with_metaclass(models.SubfieldBase,
                                            models.CharField)):

    def __init__(self, max_length=2000, *args, **kwargs):
        super(IntStoredAsStringField, self).__init__(max_length=max_length,
                                                     *args, **kwargs)

    def to_python(self, value):
        if isinstance(value, int):
            return value

        return int(value)

    def get_prep_value(self, value):
        return smart_text(value)
Note: See TracTickets for help on using tickets.
Back to Top