Issues

Issue #3040 new

association proxies are not json serializable

metagriffin
created an issue

this can be easily circumvented by simply casting them when needed, e.g. calling json.dumps(list(assoc_list)).

however, it would be great if these objects were directly serializable. the ORM instrumented objects, InstrumentedList, InstrumentedSet, and InstrumentedDict already achieve this because they respectively subclass list, set, and dict -- the json package handles them appropriately then.

Comments (6)

  1. Mike Bayer repo owner

    I'd want to get into the appropriate ABC logic here, rather than a straight list/dict subclass. I've only dealt with ABC's minimally so if someone wants to start off that would help.

  2. metagriffin reporter

    although i agree it would be good to be ABC-compatible, it would be odd if the Instrumented* objects were non-ABC-style, and the Association* objects were ABC-style.

    thus, my vote would be to get the Association* objects working with list/set/dict, then someone (with more ABC expertise) can upgrade both to ABC-style.

  3. Mike Bayer repo owner

    many failures. feel free to work on them. patch:

    diff --git a/lib/sqlalchemy/ext/associationproxy.py b/lib/sqlalchemy/ext/associationproxy.py
    index 045645f..f243b40 100644
    --- a/lib/sqlalchemy/ext/associationproxy.py
    +++ b/lib/sqlalchemy/ext/associationproxy.py
    @@ -513,7 +512,7 @@ class _AssociationCollection(object):
             self.parent._inflate(self)
    
    
    -class _AssociationList(_AssociationCollection):
    +class _AssociationList(_AssociationCollection, list):
         """Generic, converting, list-to-list proxy."""
    
         def _create(self, value):
    @@ -705,7 +704,7 @@ class _AssociationList(_AssociationCollection):
     _NotProvided = util.symbol('_NotProvided')
    
    
    -class _AssociationDict(_AssociationCollection):
    +class _AssociationDict(_AssociationCollection, dict):
         """Generic, converting, dict-to-dict proxy."""
    
         def _create(self, key, value):
    @@ -853,7 +852,7 @@ class _AssociationDict(_AssociationCollection):
         del func_name, func
    
    
    -class _AssociationSet(_AssociationCollection):
    +class _AssociationSet(_AssociationCollection, set):
         """Generic, converting, set-to-set proxy."""
    
         def _create(self, value):
    

    failures:

    =================================== FAILURES ===================================
    ______________________ CustomSetTest.test_set_comparisons ______________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 484, in test_set_comparisons
        control.intersection(other))
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/assertions.py", line 180, in eq_
        assert a == b, msg or "%r != %r" % (a, b)
    AssertionError: set([]) != set(['a', 'c', 'b'])
    _______________________ CustomSetTest.test_set_mutation ________________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 517, in test_set_mutation
        self.assert_(p.children == control)
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/fixtures.py", line 40, in assert_
        assert val, msg
    AssertionError: None
    ------------------------------- Captured stdout --------------------------------
    Test set(['a', 'c', 'b']).update(set(['a', 'c', 'b'])):
    ('want', "set(['a', 'c', 'b'])")
    ('got', 'set([])')
    ______________________ CustomSetTest.test_set_operations _______________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 415, in test_set_operations
        self.assert_(p1.children == set(['a', 'b', 'c']))
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/fixtures.py", line 40, in assert_
        assert val, msg
    AssertionError: None
    ______________ DictOfTupleUpdateTest.test_update_multi_elem_dict _______________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 1522, in test_update_multi_elem_dict
        eq_(a1.elements, {("B", 3): 'elem2', ("C", 4): "elem3"})
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/assertions.py", line 180, in eq_
        assert a == b, msg or "%r != %r" % (a, b)
    AssertionError: {('B', 3): 'elem2', ('C', 4): 'elem3'} != {('B', 3): 'elem2', ('C', 4): 'elem3'}
    ______________ DictOfTupleUpdateTest.test_update_multi_elem_list _______________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 1532, in test_update_multi_elem_list
        eq_(a1.elements, {("B", 3): 'elem2', ("C", 4): "elem3"})
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/assertions.py", line 180, in eq_
        assert a == b, msg or "%r != %r" % (a, b)
    AssertionError: {('B', 3): 'elem2', ('C', 4): 'elem3'} != {('B', 3): 'elem2', ('C', 4): 'elem3'}
    _______________ DictOfTupleUpdateTest.test_update_one_elem_dict ________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 1517, in test_update_one_elem_dict
        eq_(a1.elements, {("B", 3): 'elem2'})
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/assertions.py", line 180, in eq_
        assert a == b, msg or "%r != %r" % (a, b)
    AssertionError: {('B', 3): 'elem2'} != {('B', 3): 'elem2'}
    _______________ DictOfTupleUpdateTest.test_update_one_elem_list ________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 1527, in test_update_one_elem_list
        eq_(a1.elements, {("B", 3): 'elem2'})
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/assertions.py", line 180, in eq_
        assert a == b, msg or "%r != %r" % (a, b)
    AssertionError: {('B', 3): 'elem2'} != {('B', 3): 'elem2'}
    _____________________ ReconstitutionTest.test_pickle_dict ______________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 1048, in test_pickle_dict
        assert p.kids == {'c1': 'c1', 'c2': 'c2'}
    AssertionError: assert {'c2': 'c2', 'c1': 'c1'} == {'c1': 'c1', 'c2': 'c2'}
      Common items:
      {'c1': 'c1', 'c2': 'c2'}
    ______________________ ReconstitutionTest.test_pickle_set ______________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 1033, in test_pickle_set
        r1 = pickle.loads(pickle.dumps(p))
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1374, in dumps
        Pickler(file, protocol).dump(obj)
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 224, in dump
        self.save(obj)
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 331, in save
        self.save_reduce(obj=obj, *rv)
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 419, in save_reduce
        save(state)
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save
        f(self, obj) # Call unbound method with explicit self
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 649, in save_dict
        self._batch_setitems(obj.iteritems())
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 663, in _batch_setitems
        save(v)
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save
        f(self, obj) # Call unbound method with explicit self
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 562, in save_tuple
        save(element)
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 331, in save
        self.save_reduce(obj=obj, *rv)
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 419, in save_reduce
        save(state)
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save
        f(self, obj) # Call unbound method with explicit self
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 649, in save_dict
        self._batch_setitems(obj.iteritems())
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 663, in _batch_setitems
        save(v)
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save
        f(self, obj) # Call unbound method with explicit self
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 748, in save_global
        (obj, module, name))
    PicklingError: Can't pickle <function <lambda> at 0x1043f8b90>: it's not found as sqlalchemy.ext.associationproxy.<lambda>
    _________________________ SetTest.test_set_comparisons _________________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 484, in test_set_comparisons
        control.intersection(other))
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/assertions.py", line 180, in eq_
        assert a == b, msg or "%r != %r" % (a, b)
    AssertionError: set([]) != set(['a', 'c', 'b'])
    __________________________ SetTest.test_set_mutation ___________________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 517, in test_set_mutation
        self.assert_(p.children == control)
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/fixtures.py", line 40, in assert_
        assert val, msg
    AssertionError: None
    ------------------------------- Captured stdout --------------------------------
    Test set(['a', 'c', 'b']).update(set(['a', 'c', 'b'])):
    ('want', "set(['a', 'c', 'b'])")
    ('got', 'set([])')
    _________________________ SetTest.test_set_operations __________________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 415, in test_set_operations
        self.assert_(p1.children == set(['a', 'b', 'c']))
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/fixtures.py", line 40, in assert_
        assert val, msg
    AssertionError: None
    
  4. Log in to comment