File tree Expand file tree Collapse file tree 3 files changed +46
-0
lines changed
packages/google-cloud-ndb Expand file tree Collapse file tree 3 files changed +46
-0
lines changed Original file line number Diff line number Diff line change @@ -3990,17 +3990,29 @@ def __getattr__(self, attrname):
39903990 """Dynamically get a subproperty."""
39913991 # Optimistically try to use the dict key.
39923992 prop = self ._model_class ._properties .get (attrname )
3993+
3994+ # We're done if we have a hit and _code_name matches.
3995+ if prop is None or prop ._code_name != attrname :
3996+ # Otherwise, use linear search looking for a matching _code_name.
3997+ for candidate in self ._model_class ._properties .values ():
3998+ if candidate ._code_name == attrname :
3999+ prop = candidate
4000+ break
4001+
39934002 if prop is None :
39944003 raise AttributeError (
39954004 "Model subclass %s has no attribute %s"
39964005 % (self ._model_class .__name__ , attrname )
39974006 )
4007+
39984008 prop_copy = copy .copy (prop )
39994009 prop_copy ._name = self ._name + "." + prop_copy ._name
4010+
40004011 # Cache the outcome, so subsequent requests for the same attribute
40014012 # name will get the copied property directly rather than going
40024013 # through the above motions all over again.
40034014 setattr (self , attrname , prop_copy )
4015+
40044016 return prop_copy
40054017
40064018 def _comparison (self , op , value ):
Original file line number Diff line number Diff line change @@ -969,6 +969,30 @@ def make_entities():
969969 results [1 ].bar .three
970970
971971
972+ @pytest .mark .usefixtures ("client_context" )
973+ def test_query_structured_property_rename_subproperty (dispose_of ):
974+ """Regression test for #449
975+
976+ https://github.com/googleapis/python-ndb/issues/449
977+ """
978+
979+ class OtherKind (ndb .Model ):
980+ one = ndb .StringProperty ("a_different_name" )
981+
982+ class SomeKind (ndb .Model ):
983+ bar = ndb .StructuredProperty (OtherKind )
984+
985+ key = SomeKind (bar = OtherKind (one = "pish" )).put ()
986+ dispose_of (key ._key )
987+
988+ eventually (SomeKind .query ().fetch , length_equals (1 ))
989+
990+ query = SomeKind .query ().filter (SomeKind .bar .one == "pish" )
991+ results = query .fetch ()
992+ assert len (results ) == 1
993+ assert results [0 ].bar .one == "pish"
994+
995+
972996@pytest .mark .usefixtures ("client_context" )
973997def test_query_repeated_structured_property_with_properties (dispose_of ):
974998 class OtherKind (ndb .Model ):
Original file line number Diff line number Diff line change @@ -3058,6 +3058,16 @@ class Mine(model.Model):
30583058 assert isinstance (prop .foo , model .StringProperty )
30593059 assert prop .foo ._name == "bar.foo"
30603060
3061+ @staticmethod
3062+ def test___getattr__use_codename ():
3063+ class Mine (model .Model ):
3064+ foo = model .StringProperty ("notfoo" )
3065+
3066+ prop = model .StructuredProperty (Mine )
3067+ prop ._name = "bar"
3068+ assert isinstance (prop .foo , model .StringProperty )
3069+ assert prop .foo ._name == "bar.notfoo"
3070+
30613071 @staticmethod
30623072 def test___getattr___bad_prop ():
30633073 class Mine (model .Model ):
You can’t perform that action at this time.
0 commit comments