1# Copyright 2014 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""Class for representing a single entity in the Cloud Datastore.""" 16 17 18from google.cloud._helpers import _ensure_tuple_or_list 19 20 21class Entity(dict): 22 """Entities are akin to rows in a relational database 23 24 An entity storing the actual instance of data. 25 26 Each entity is officially represented with a 27 :class:`~google.cloud.datastore.key.Key`, however it is possible that 28 you might create an entity with only a partial key (that is, a key 29 with a kind, and possibly a parent, but without an ID). In such a 30 case, the datastore service will automatically assign an ID to the 31 partial key. 32 33 Entities in this API act like dictionaries with extras built in that 34 allow you to delete or persist the data stored on the entity. 35 36 Entities are mutable and act like a subclass of a dictionary. 37 This means you could take an existing entity and change the key 38 to duplicate the object. 39 40 Use :meth:`~google.cloud.datastore.client.Client.get` to retrieve an 41 existing entity: 42 43 .. testsetup:: entity-ctor 44 45 import uuid 46 47 from google.cloud import datastore 48 from google.cloud import datastore 49 50 unique = str(uuid.uuid4())[0:8] 51 client = datastore.Client(namespace='ns{}'.format(unique)) 52 53 entity = datastore.Entity(client.key('EntityKind', 1234)) 54 entity['property'] = 'value' 55 client.put(entity) 56 57 .. doctest:: entity-ctor 58 59 >>> key = client.key('EntityKind', 1234) 60 >>> client.get(key) 61 <Entity('EntityKind', 1234) {'property': 'value'}> 62 63 You can the set values on the entity just like you would on any 64 other dictionary. 65 66 .. doctest:: entity-ctor 67 68 >>> entity['age'] = 20 69 >>> entity['name'] = 'JJ' 70 71 .. testcleanup:: entity-ctor 72 73 client.delete(entity.key) 74 75 However, not all types are allowed as a value for a Google Cloud Datastore 76 entity. The following basic types are supported by the API: 77 78 * :class:`datetime.datetime` 79 * :class:`~google.cloud.datastore.key.Key` 80 * :class:`bool` 81 * :class:`float` 82 * :class:`int` (as well as :class:`long` in Python 2) 83 * ``unicode`` (called ``str`` in Python 3) 84 * ``bytes`` (called ``str`` in Python 2) 85 * :class:`~google.cloud.datastore.helpers.GeoPoint` 86 * :data:`None` 87 88 In addition, three container types are supported: 89 90 * :class:`list` 91 * :class:`~google.cloud.datastore.entity.Entity` 92 * :class:`dict` (will just be treated like an ``Entity`` without 93 a key or ``exclude_from_indexes``) 94 95 Each entry in a list must be one of the value types (basic or 96 container) and each value in an 97 :class:`~google.cloud.datastore.entity.Entity` must as well. In 98 this case an :class:`~google.cloud.datastore.entity.Entity` **as a 99 container** acts as a :class:`dict`, but also has the special annotations 100 of ``key`` and ``exclude_from_indexes``. 101 102 And you can treat an entity like a regular Python dictionary: 103 104 .. testsetup:: entity-dict 105 106 from google.cloud import datastore 107 108 entity = datastore.Entity() 109 entity['age'] = 20 110 entity['name'] = 'JJ' 111 112 .. doctest:: entity-dict 113 114 >>> sorted(entity.keys()) 115 ['age', 'name'] 116 >>> sorted(entity.items()) 117 [('age', 20), ('name', 'JJ')] 118 119 .. note:: 120 121 When saving an entity to the backend, values which are "text" 122 (``unicode`` in Python2, ``str`` in Python3) will be saved using 123 the 'text_value' field, after being encoded to UTF-8. When 124 retrieved from the back-end, such values will be decoded to "text" 125 again. Values which are "bytes" (``str`` in Python2, ``bytes`` in 126 Python3), will be saved using the 'blob_value' field, without 127 any decoding / encoding step. 128 129 :type key: :class:`google.cloud.datastore.key.Key` 130 :param key: Optional key to be set on entity. 131 132 :type exclude_from_indexes: tuple of string 133 :param exclude_from_indexes: Names of fields whose values are not to be 134 indexed for this entity. 135 """ 136 137 def __init__(self, key=None, exclude_from_indexes=()): 138 super(Entity, self).__init__() 139 self.key = key 140 self.exclude_from_indexes = set( 141 _ensure_tuple_or_list("exclude_from_indexes", exclude_from_indexes) 142 ) 143 """Names of fields which are *not* to be indexed for this entity.""" 144 # NOTE: This will be populated when parsing a protobuf in 145 # google.cloud.datastore.helpers.entity_from_protobuf. 146 self._meanings = {} 147 148 def __eq__(self, other): 149 """Compare two entities for equality. 150 151 Entities compare equal if their keys compare equal and their 152 properties compare equal. 153 154 :rtype: bool 155 :returns: True if the entities compare equal, else False. 156 """ 157 if not isinstance(other, Entity): 158 return NotImplemented 159 160 return ( 161 self.key == other.key 162 and self.exclude_from_indexes == other.exclude_from_indexes 163 and self._meanings == other._meanings 164 and super(Entity, self).__eq__(other) 165 ) 166 167 def __ne__(self, other): 168 """Compare two entities for inequality. 169 170 Entities compare equal if their keys compare equal and their 171 properties compare equal. 172 173 :rtype: bool 174 :returns: False if the entities compare equal, else True. 175 """ 176 return not self == other 177 178 @property 179 def kind(self): 180 """Get the kind of the current entity. 181 182 .. note:: 183 184 This relies entirely on the :class:`google.cloud.datastore.key.Key` 185 set on the entity. That means that we're not storing the kind 186 of the entity at all, just the properties and a pointer to a 187 Key which knows its Kind. 188 """ 189 if self.key: 190 return self.key.kind 191 192 @property 193 def id(self): 194 """Get the ID of the current entity. 195 196 .. note:: 197 198 This relies entirely on the :class:`google.cloud.datastore.key.Key` 199 set on the entity. That means that we're not storing the ID 200 of the entity at all, just the properties and a pointer to a 201 Key which knows its ID. 202 """ 203 if self.key is None: 204 return None 205 else: 206 return self.key.id 207 208 def __repr__(self): 209 if self.key: 210 return "<Entity%s %s>" % ( 211 self.key._flat_path, 212 super(Entity, self).__repr__(), 213 ) 214 else: 215 return "<Entity %s>" % (super(Entity, self).__repr__(),) 216