]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Changed the update() method on association proxy
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 14 Sep 2011 15:31:33 +0000 (11:31 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 14 Sep 2011 15:31:33 +0000 (11:31 -0400)
    dictionary to use a duck typing approach, i.e.
    checks for "keys", to discern between update({})
    and update((a, b)).   Previously, passing a
    dictionary that had tuples as keys would be misinterpreted
    as a sequence. [ticket:2275]

CHANGES
lib/sqlalchemy/ext/associationproxy.py
lib/sqlalchemy/sql/expression.py
test/ext/test_associationproxy.py
test/sql/test_selectable.py

diff --git a/CHANGES b/CHANGES
index a98f3d0c462e5cb808f10d984084ee60e2caf750..99af9e4a3e3a9188a03a4a03be819ba671d64d0c 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -157,6 +157,13 @@ CHANGES
     to the proxied attributes at the class
     level. [ticket:2236]
 
+  - Changed the update() method on association proxy
+    dictionary to use a duck typing approach, i.e.
+    checks for "keys", to discern between update({}) 
+    and update((a, b)).   Previously, passing a 
+    dictionary that had tuples as keys would be misinterpreted
+    as a sequence. [ticket:2275]
+
 0.7.2
 =====
 - orm
index 47b21ab5b096a94f0a9fd53806dfe9ea51171ead..eb5b3e3db6e23772d58bc48bf3f9b864ce97b644 100644 (file)
@@ -768,11 +768,20 @@ class _AssociationDict(_AssociationCollection):
                             len(a))
         elif len(a) == 1:
             seq_or_map = a[0]
-            for item in seq_or_map:
-                if isinstance(item, tuple):
-                    self[item[0]] = item[1]
-                else:
+            # discern dict from sequence - took the advice
+            # from http://www.voidspace.org.uk/python/articles/duck_typing.shtml
+            # still not perfect :(
+            if hasattr(seq_or_map, 'keys'):
+                for item in seq_or_map:
                     self[item] = seq_or_map[item]
+            else:
+                try:
+                    for k, v in seq_or_map:
+                        self[k] = v
+                except ValueError:
+                    raise ValueError(
+                            "dictionary update sequence "
+                            "requires 2-element tuples")
 
         for key, value in kw:
             self[key] = value
index 9e920c34f7512c5efd0af7782297f03606293932..84fcbd569561f850a04b46bf3311354c8ceea0ba 100644 (file)
@@ -4699,7 +4699,7 @@ class Select(_SelectBase):
 
         """
         self._should_correlate = False
-        if fromclauses == (None,):
+        if fromclauses and fromclauses[0] is None:
             self._correlate = set()
         else:
             self._correlate = self._correlate.union(fromclauses)
index ddd0bd8f12766ce7f1ee1abd4943063a39d12d02..c9feddf4c9639dbf9b17a69d5366666b5e9fed43 100644 (file)
@@ -4,10 +4,11 @@ import pickle
 
 from sqlalchemy import *
 from sqlalchemy.orm import *
-from sqlalchemy.orm.collections import collection
+from sqlalchemy.orm.collections import collection, attribute_mapped_collection
 from sqlalchemy.ext.associationproxy import *
 from sqlalchemy.ext.associationproxy import _AssociationList
 from test.lib import *
+from test.lib.testing import assert_raises_message
 from test.lib.util import gc_collect
 from sqlalchemy.sql import not_
 from test.lib import fixtures
@@ -1306,4 +1307,63 @@ class ComparatorTest(fixtures.MappedTest, AssertsCompiledSQL):
             "FROM users JOIN userkeywords ON users.id = "
             "userkeywords.user_id JOIN keywords ON keywords.id = "
             "userkeywords.keyword_id"
-        )
\ No newline at end of file
+        )
+
+class DictOfTupleUpdateTest(fixtures.TestBase):
+    def setup(self):
+        class B(object):
+            def __init__(self, key, elem):
+                self.key = key
+                self.elem = elem
+
+        class A(object):
+            elements = association_proxy("orig", "elem", creator=B)
+
+        m = MetaData()
+        a = Table('a', m, Column('id', Integer, primary_key=True))
+        b = Table('b', m, Column('id', Integer, primary_key=True), 
+                    Column('aid', Integer, ForeignKey('a.id')))
+        mapper(A, a, properties={
+            'orig':relationship(B, collection_class=attribute_mapped_collection('key'))
+        })
+        mapper(B, b)
+        self.A = A
+        self.B = B
+
+    def test_update_one_elem_dict(self):
+        a1 = self.A()
+        a1.elements.update({("B", 3): 'elem2'})
+        eq_(a1.elements, {("B",3):'elem2'})
+
+    def test_update_multi_elem_dict(self):
+        a1 = self.A()
+        a1.elements.update({("B", 3): 'elem2', ("C", 4): "elem3"})
+        eq_(a1.elements, {("B",3):'elem2', ("C", 4): "elem3"})
+
+    def test_update_one_elem_list(self):
+        a1 = self.A()
+        a1.elements.update([(("B", 3), 'elem2')])
+        eq_(a1.elements, {("B",3):'elem2'})
+
+    def test_update_multi_elem_list(self):
+        a1 = self.A()
+        a1.elements.update([(("B", 3), 'elem2'), (("C", 4), "elem3")])
+        eq_(a1.elements, {("B",3):'elem2', ("C", 4): "elem3"})
+
+    def test_update_one_elem_varg(self):
+        a1 = self.A()
+        assert_raises_message(
+            ValueError,
+            "dictionary update sequence requires "
+            "2-element tuples",
+            a1.elements.update, (("B", 3), 'elem2')
+        )
+
+    def test_update_multi_elem_varg(self):
+        a1 = self.A()
+        assert_raises_message(
+            TypeError,
+            "update expected at most 1 arguments, got 2",
+            a1.elements.update,
+            (("B", 3), 'elem2'), (("C", 4), "elem3")
+        )
index 9c1f44e1a0b75a177b5827dbec0a856eaaae919e..4bbcf61d286bd968a05eb8bd4bc10ad640ab8781 100644 (file)
@@ -161,7 +161,6 @@ class SelectableTest(fixtures.TestBase, AssertsExecutionResults, AssertsCompiled
         criterion = a.c.col1 == table2.c.col2
         self.assert_(criterion.compare(j.onclause))
 
-
     def test_union(self):
 
         # tests that we can correspond a column in a Select statement