@@ -340,17 +340,15 @@ def _entity_from_protobuf(protobuf):
340340 return _entity_from_ds_entity (ds_entity )
341341
342342
343- def _entity_to_protobuf (entity , set_key = True ):
344- """Serialize an entity to a protobuffer .
343+ def _entity_to_ds_entity (entity , set_key = True ):
344+ """Convert an NDB entity to Datastore entity .
345345
346346 Args:
347- entity (Model): The entity to be serialized .
347+ entity (Model): The entity to be converted .
348348
349349 Returns:
350- google.cloud.datastore_v1.types.Entity: The protocol buffer
351- representation.
350+ google.cloud.datastore.entity.Entity: The converted entity.
352351 """
353- # First, make a datastore entity
354352 data = {}
355353 for cls in type (entity ).mro ():
356354 for prop in cls .__dict__ .values ():
@@ -376,7 +374,20 @@ def _entity_to_protobuf(entity, set_key=True):
376374 ds_entity = entity_module .Entity ()
377375 ds_entity .update (data )
378376
379- # Then, use datatore to get the protocol buffer
377+ return ds_entity
378+
379+
380+ def _entity_to_protobuf (entity , set_key = True ):
381+ """Serialize an entity to a protocol buffer.
382+
383+ Args:
384+ entity (Model): The entity to be serialized.
385+
386+ Returns:
387+ google.cloud.datastore_v1.types.Entity: The protocol buffer
388+ representation.
389+ """
390+ ds_entity = _entity_to_ds_entity (entity , set_key = set_key )
380391 return helpers .entity_to_protobuf (ds_entity )
381392
382393
@@ -3362,29 +3373,29 @@ class StructuredProperty(Property):
33623373 The values of the sub-entity are indexed and can be queried.
33633374 """
33643375
3365- _modelclass = None
3376+ _model_class = None
33663377 _kwargs = None
33673378
3368- def __init__ (self , modelclass , name = None , ** kwargs ):
3379+ def __init__ (self , model_class , name = None , ** kwargs ):
33693380 super (StructuredProperty , self ).__init__ (name = name , ** kwargs )
33703381 if self ._repeated :
3371- if modelclass ._has_repeated :
3382+ if model_class ._has_repeated :
33723383 raise TypeError (
33733384 "This StructuredProperty cannot use repeated=True "
33743385 "because its model class (%s) contains repeated "
33753386 "properties (directly or indirectly)."
3376- % modelclass .__name__
3387+ % model_class .__name__
33773388 )
3378- self ._modelclass = modelclass
3389+ self ._model_class = model_class
33793390
33803391 def _get_value (self , entity ):
33813392 """Override _get_value() to *not* raise UnprojectedPropertyError.
33823393
3383- This is necessary because the projection must include both the sub-entity and
3384- the property name that is projected (e.g. 'foo.bar' instead of only 'foo'). In
3385- that case the original code would fail, because it only looks for the property
3386- name ('foo'). Here we check for a value, and only call the original code if the
3387- value is None.
3394+ This is necessary because the projection must include both the
3395+ sub-entity and the property name that is projected (e.g. 'foo.bar'
3396+ instead of only 'foo'). In that case the original code would fail,
3397+ because it only looks for the property name ('foo'). Here we check for
3398+ a value, and only call the original code if the value is None.
33883399 """
33893400 value = self ._get_user_value (entity )
33903401 if value is None and entity ._projection :
@@ -3403,11 +3414,11 @@ def _get_for_dict(self, entity):
34033414 def __getattr__ (self , attrname ):
34043415 """Dynamically get a subproperty."""
34053416 # Optimistically try to use the dict key.
3406- prop = self ._modelclass ._properties .get (attrname )
3417+ prop = self ._model_class ._properties .get (attrname )
34073418 if prop is None :
34083419 raise AttributeError (
34093420 "Model subclass %s has no attribute %s"
3410- % (self ._modelclass .__name__ , attrname )
3421+ % (self ._model_class .__name__ , attrname )
34113422 )
34123423 prop_copy = copy .copy (prop )
34133424 prop_copy ._name = self ._name + "." + prop_copy ._name
@@ -3436,37 +3447,41 @@ def _comparison(self, op, value):
34363447 ) # Import late to avoid circular imports.
34373448
34383449 return FilterNode (self ._name , op , value )
3450+
34393451 value = self ._do_validate (value )
3440- value = self ._call_to_base_type (value )
34413452 filters = []
34423453 match_keys = []
3443- for prop in self ._modelclass ._properties .values ():
3444- vals = prop ._get_base_value_unwrapped_as_list (value )
3454+ for prop in self ._model_class ._properties .values ():
3455+ subvalue = prop ._get_value (value )
34453456 if prop ._repeated :
3446- if vals : # pragma: no branch
3457+ if subvalue : # pragma: no branch
34473458 raise exceptions .BadFilterError (
34483459 "Cannot query for non-empty repeated property %s"
34493460 % prop ._name
34503461 )
34513462 continue # pragma: NO COVER
3452- val = vals [ 0 ]
3453- if val is not None : # pragma: no branch
3463+
3464+ if subvalue is not None : # pragma: no branch
34543465 altprop = getattr (self , prop ._code_name )
3455- filt = altprop ._comparison (op , val )
3466+ filt = altprop ._comparison (op , subvalue )
34563467 filters .append (filt )
34573468 match_keys .append (altprop ._name )
3469+
34583470 if not filters :
34593471 raise exceptions .BadFilterError (
34603472 "StructuredProperty filter without any values"
34613473 )
3474+
34623475 if len (filters ) == 1 :
34633476 return filters [0 ]
3477+
34643478 if self ._repeated :
34653479 raise NotImplementedError ("This depends on code not yet ported." )
34663480 # pb = value._to_pb(allow_partial=True)
34673481 # pred = RepeatedStructuredPropertyPredicate(match_keys, pb,
34683482 # self._name + '.')
34693483 # filters.append(PostFilterNode(pred))
3484+
34703485 return ConjunctionNode (* filters )
34713486
34723487 def _IN (self , value ):
@@ -3491,11 +3506,11 @@ def _IN(self, value):
34913506 def _validate (self , value ):
34923507 if isinstance (value , dict ):
34933508 # A dict is assumed to be the result of a _to_dict() call.
3494- return self ._modelclass (** value )
3495- if not isinstance (value , self ._modelclass ):
3509+ return self ._model_class (** value )
3510+ if not isinstance (value , self ._model_class ):
34963511 raise exceptions .BadValueError (
34973512 "Expected %s instance, got %s"
3498- % (self ._modelclass .__name__ , value .__class__ )
3513+ % (self ._model_class .__name__ , value .__class__ )
34993514 )
35003515
35013516 def _has_value (self , entity , rest = None ):
@@ -3507,27 +3522,34 @@ def _has_value(self, entity, rest=None):
35073522
35083523 Args:
35093524 entity (ndb.Model): An instance of a model.
3510- rest (list[str]): optional list of attribute names to check in addition.
3525+ rest (list[str]): optional list of attribute names to check in
3526+ addition.
35113527
35123528 Returns:
35133529 bool: True if the entity has a value for that property.
35143530 """
35153531 ok = super (StructuredProperty , self )._has_value (entity )
35163532 if ok and rest :
3517- lst = self ._get_base_value_unwrapped_as_list (entity )
3518- if len (lst ) != 1 :
3519- raise RuntimeError (
3520- "Failed to retrieve sub-entity of StructuredProperty"
3521- " %s" % self ._name
3522- )
3523- subent = lst [0 ]
3533+ value = self ._get_value (entity )
3534+ if self ._repeated :
3535+ if len (value ) != 1 :
3536+ raise RuntimeError (
3537+ "Failed to retrieve sub-entity of StructuredProperty"
3538+ " %s" % self ._name
3539+ )
3540+ subent = value [0 ]
3541+ else :
3542+ subent = value
3543+
35243544 if subent is None :
35253545 return True
3546+
35263547 subprop = subent ._properties .get (rest [0 ])
35273548 if subprop is None :
35283549 ok = False
35293550 else :
35303551 ok = subprop ._has_value (subent , rest [1 :])
3552+
35313553 return ok
35323554
35333555 def _check_property (self , rest = None , require_indexed = True ):
@@ -3541,15 +3563,42 @@ def _check_property(self, rest=None, require_indexed=True):
35413563 raise InvalidPropertyError (
35423564 "Structured property %s requires a subproperty" % self ._name
35433565 )
3544- self ._modelclass ._check_properties (
3566+ self ._model_class ._check_properties (
35453567 [rest ], require_indexed = require_indexed
35463568 )
35473569
3548- def _get_base_value_at_index (self , entity , index ):
3549- assert self ._repeated
3550- value = self ._retrieve_value (entity , self ._default )
3551- value [index ] = self ._opt_call_to_base_type (value [index ])
3552- return value [index ].b_val
3570+ def _to_base_type (self , value ):
3571+ """Convert a value to the "base" value type for this property.
3572+
3573+ Args:
3574+ value: The given class value to be converted.
3575+
3576+ Returns:
3577+ bytes
3578+
3579+ Raises:
3580+ TypeError: If ``value`` is not the correct ``Model`` type.
3581+ """
3582+ if not isinstance (value , self ._model_class ):
3583+ raise TypeError (
3584+ "Cannot convert to protocol buffer. Expected {} value; "
3585+ "received {}" .format (self ._model_class .__name__ , value )
3586+ )
3587+ return _entity_to_ds_entity (value )
3588+
3589+ def _from_base_type (self , value ):
3590+ """Convert a value from the "base" value type for this property.
3591+ Args:
3592+ value(~google.cloud.datastore.Entity or bytes): The value to be
3593+ converted.
3594+ Returns:
3595+ The converted value with given class.
3596+ """
3597+ if isinstance (value , entity_module .Entity ):
3598+ value = _entity_from_ds_entity (
3599+ value , model_class = self ._model_class
3600+ )
3601+ return value
35533602
35543603 def _get_value_size (self , entity ):
35553604 values = self ._retrieve_value (entity , self ._default )
@@ -3569,7 +3618,8 @@ class LocalStructuredProperty(BlobProperty):
35693618 .. automethod:: _from_base_type
35703619 .. automethod:: _validate
35713620 Args:
3572- kls (ndb.Model): The class of the property.
3621+ model_class (type): The class of the property. (Must be subclass of
3622+ ``ndb.Model``.)
35733623 name (str): The name of the property.
35743624 compressed (bool): Indicates if the value should be compressed (via
35753625 ``zlib``).
@@ -3585,19 +3635,19 @@ class LocalStructuredProperty(BlobProperty):
35853635 to the datastore.
35863636 """
35873637
3588- _kls = None
3638+ _model_class = None
35893639 _keep_keys = False
35903640 _kwargs = None
35913641
3592- def __init__ (self , kls , ** kwargs ):
3642+ def __init__ (self , model_class , ** kwargs ):
35933643 indexed = kwargs .pop ("indexed" , False )
35943644 if indexed :
35953645 raise NotImplementedError (
35963646 "Cannot index LocalStructuredProperty {}." .format (self ._name )
35973647 )
35983648 keep_keys = kwargs .pop ("keep_keys" , False )
35993649 super (LocalStructuredProperty , self ).__init__ (** kwargs )
3600- self ._kls = kls
3650+ self ._model_class = model_class
36013651 self ._keep_keys = keep_keys
36023652
36033653 def _validate (self , value ):
@@ -3609,11 +3659,13 @@ def _validate(self, value):
36093659 """
36103660 if isinstance (value , dict ):
36113661 # A dict is assumed to be the result of a _to_dict() call.
3612- value = self ._kls (** value )
3662+ value = self ._model_class (** value )
36133663
3614- if not isinstance (value , self ._kls ):
3664+ if not isinstance (value , self ._model_class ):
36153665 raise exceptions .BadValueError (
3616- "Expected {}, got {!r}" .format (self ._kls .__name__ , value )
3666+ "Expected {}, got {!r}" .format (
3667+ self ._model_class .__name__ , value
3668+ )
36173669 )
36183670
36193671 def _to_base_type (self , value ):
@@ -3623,12 +3675,12 @@ def _to_base_type(self, value):
36233675 Returns:
36243676 bytes
36253677 Raises:
3626- TypeError: If ``value`` is not a given class .
3678+ TypeError: If ``value`` is not the correct ``Model`` type .
36273679 """
3628- if not isinstance (value , self ._kls ):
3680+ if not isinstance (value , self ._model_class ):
36293681 raise TypeError (
36303682 "Cannot convert to bytes expected {} value; "
3631- "received {}" .format (self ._kls .__name__ , value )
3683+ "received {}" .format (self ._model_class .__name__ , value )
36323684 )
36333685 pb = _entity_to_protobuf (value , set_key = self ._keep_keys )
36343686 return pb .SerializePartialToString ()
@@ -3647,7 +3699,7 @@ def _from_base_type(self, value):
36473699 value = helpers .entity_from_protobuf (pb )
36483700 if not self ._keep_keys and value .key :
36493701 value .key = None
3650- return _entity_from_ds_entity (value , model_class = self ._kls )
3702+ return _entity_from_ds_entity (value , model_class = self ._model_class )
36513703
36523704
36533705class GenericProperty (Property ):
@@ -4328,7 +4380,7 @@ def _fix_up_properties(cls):
43284380 if isinstance (attr , Property ):
43294381 if attr ._repeated or (
43304382 isinstance (attr , StructuredProperty )
4331- and attr ._modelclass ._has_repeated
4383+ and attr ._model_class ._has_repeated
43324384 ):
43334385 cls ._has_repeated = True
43344386 cls ._properties [attr ._name ] = attr
0 commit comments