239 "using a different name like %(attr_name)s_ and adding " |
241 "using a different name like %(attr_name)s_ and adding " |
240 "name='%(attr_name)s' to the parameter list of the property " |
242 "name='%(attr_name)s' to the parameter list of the property " |
241 "definition." % locals()) |
243 "definition." % locals()) |
242 |
244 |
243 |
245 |
|
246 def query_descendants(model_instance): |
|
247 """Returns a query for all the descendants of a model instance. |
|
248 |
|
249 Args: |
|
250 model_instance: Model instance to find the descendants of. |
|
251 |
|
252 Returns: |
|
253 Query that will retrieve all entities that have the given model instance |
|
254 as an ancestor. Unlike normal ancestor queries, this does not include the |
|
255 ancestor itself. |
|
256 """ |
|
257 |
|
258 result = Query().ancestor(model_instance); |
|
259 result.filter(datastore_types._KEY_SPECIAL_PROPERTY + ' >', |
|
260 model_instance.key()); |
|
261 return result; |
|
262 |
|
263 |
|
264 def model_to_protobuf(model_instance, _entity_class=datastore.Entity): |
|
265 """Encodes a model instance as a protocol buffer. |
|
266 |
|
267 Args: |
|
268 model_instance: Model instance to encode. |
|
269 Returns: |
|
270 entity_pb.EntityProto representation of the model instance |
|
271 """ |
|
272 return model_instance._populate_entity(_entity_class).ToPb() |
|
273 |
|
274 |
|
275 def model_from_protobuf(pb, _entity_class=datastore.Entity): |
|
276 """Decodes a model instance from a protocol buffer. |
|
277 |
|
278 Args: |
|
279 pb: The protocol buffer representation of the model instance. Can be an |
|
280 entity_pb.EntityProto or str encoding of an entity_bp.EntityProto |
|
281 |
|
282 Returns: |
|
283 Model instance resulting from decoding the protocol buffer |
|
284 """ |
|
285 entity = _entity_class.FromPb(pb) |
|
286 return class_for_kind(entity.kind()).from_entity(entity) |
|
287 |
|
288 |
244 def _initialize_properties(model_class, name, bases, dct): |
289 def _initialize_properties(model_class, name, bases, dct): |
245 """Initialize Property attributes for Model-class. |
290 """Initialize Property attributes for Model-class. |
246 |
291 |
247 Args: |
292 Args: |
248 model_class: Model class to initialize properties for. |
293 model_class: Model class to initialize properties for. |
249 """ |
294 """ |
250 model_class._properties = {} |
295 model_class._properties = {} |
|
296 property_source = {} |
|
297 |
|
298 def get_attr_source(name, cls): |
|
299 for src_cls in cls.mro(): |
|
300 if name in src_cls.__dict__: |
|
301 return src_cls |
|
302 |
251 defined = set() |
303 defined = set() |
252 for base in bases: |
304 for base in bases: |
253 if hasattr(base, '_properties'): |
305 if hasattr(base, '_properties'): |
254 property_keys = base._properties.keys() |
306 property_keys = set(base._properties.keys()) |
255 duplicate_properties = defined.intersection(property_keys) |
307 duplicate_property_keys = defined & property_keys |
256 if duplicate_properties: |
308 for dupe_prop_name in duplicate_property_keys: |
257 raise DuplicatePropertyError( |
309 old_source = property_source[dupe_prop_name] = get_attr_source( |
258 'Duplicate properties in base class %s already defined: %s' % |
310 dupe_prop_name, property_source[dupe_prop_name]) |
259 (base.__name__, list(duplicate_properties))) |
311 new_source = get_attr_source(dupe_prop_name, base) |
260 defined.update(property_keys) |
312 if old_source != new_source: |
261 model_class._properties.update(base._properties) |
313 raise DuplicatePropertyError( |
|
314 'Duplicate property, %s, is inherited from both %s and %s.' % |
|
315 (dupe_prop_name, old_source.__name__, new_source.__name__)) |
|
316 property_keys -= duplicate_property_keys |
|
317 if property_keys: |
|
318 defined |= property_keys |
|
319 property_source.update(dict.fromkeys(property_keys, base)) |
|
320 model_class._properties.update(base._properties) |
262 |
321 |
263 for attr_name in dct.keys(): |
322 for attr_name in dct.keys(): |
264 attr = dct[attr_name] |
323 attr = dct[attr_name] |
265 if isinstance(attr, Property): |
324 if isinstance(attr, Property): |
266 check_reserved_word(attr_name) |
325 check_reserved_word(attr_name) |
580 |
640 |
581 Args: |
641 Args: |
582 parent: Parent instance for this instance or None, indicating a top- |
642 parent: Parent instance for this instance or None, indicating a top- |
583 level instance. |
643 level instance. |
584 key_name: Name for new model instance. |
644 key_name: Name for new model instance. |
585 _app: Intentionally undocumented. |
645 key: Key instance for this instance, overrides parent and key_name |
586 _from_entity: Intentionally undocumented. |
646 _from_entity: Intentionally undocumented. |
587 args: Keyword arguments mapping to properties of model. |
647 args: Keyword arguments mapping to properties of model. |
588 """ |
648 """ |
589 if key_name == '': |
649 if key is not None: |
590 raise BadKeyError('Name cannot be empty.') |
650 if isinstance(key, (tuple, list)): |
591 elif key_name is not None and not isinstance(key_name, basestring): |
651 key = Key.from_path(*key) |
592 raise BadKeyError('Name must be string type, not %s' % |
652 if isinstance(key, basestring): |
593 key_name.__class__.__name__) |
653 key = Key(encoded=key) |
594 |
654 if not isinstance(key, Key): |
595 if parent is not None: |
655 raise TypeError('Expected Key type; received %s (is %s)' % |
596 if not isinstance(parent, (Model, Key)): |
656 (key, key.__class__.__name__)) |
597 raise TypeError('Expected Model type; received %s (is %s)' % |
657 if not key.has_id_or_name(): |
598 (parent, parent.__class__.__name__)) |
658 raise BadKeyError('Key must have an id or name') |
599 if isinstance(parent, Model) and not parent.has_key(): |
659 if key.kind() != self.kind(): |
600 raise BadValueError( |
660 raise BadKeyError('Expected Key kind to be %s; received %s' % |
601 "%s instance must have a complete key before it can be used as a " |
661 (self.kind(), key.kind())) |
602 "parent." % parent.kind()) |
662 if _app is not None and key.app() != _app: |
603 if isinstance(parent, Key): |
663 raise BadKeyError('Expected Key app to be %s; received %s' % |
604 self._parent_key = parent |
664 (_app, key.app())) |
|
665 if key_name is not None: |
|
666 raise BadArgumentError('Cannot use key and key_name at the same time') |
|
667 if parent is not None: |
|
668 raise BadArgumentError('Cannot use key and parent at the same time') |
|
669 self._key = key |
|
670 self._key_name = None |
|
671 self._parent = None |
|
672 self._parent_key = None |
|
673 else: |
|
674 if key_name == '': |
|
675 raise BadKeyError('Name cannot be empty.') |
|
676 elif key_name is not None and not isinstance(key_name, basestring): |
|
677 raise BadKeyError('Name must be string type, not %s' % |
|
678 key_name.__class__.__name__) |
|
679 |
|
680 if parent is not None: |
|
681 if not isinstance(parent, (Model, Key)): |
|
682 raise TypeError('Expected Model type; received %s (is %s)' % |
|
683 (parent, parent.__class__.__name__)) |
|
684 if isinstance(parent, Model) and not parent.has_key(): |
|
685 raise BadValueError( |
|
686 "%s instance must have a complete key before it can be used as a " |
|
687 "parent." % parent.kind()) |
|
688 if isinstance(parent, Key): |
|
689 self._parent_key = parent |
|
690 self._parent = None |
|
691 else: |
|
692 self._parent_key = parent.key() |
|
693 self._parent = parent |
|
694 else: |
|
695 self._parent_key = None |
605 self._parent = None |
696 self._parent = None |
606 else: |
697 self._key_name = key_name |
607 self._parent_key = parent.key() |
698 self._key = None |
608 self._parent = parent |
699 |
609 else: |
|
610 self._parent_key = None |
|
611 self._parent = None |
|
612 self._entity = None |
700 self._entity = None |
613 self._key_name = key_name |
|
614 self._app = _app |
701 self._app = _app |
615 |
702 |
616 properties = self.properties() |
|
617 for prop in self.properties().values(): |
703 for prop in self.properties().values(): |
618 if prop.name in kwds: |
704 if prop.name in kwds: |
619 value = kwds[prop.name] |
705 value = kwds[prop.name] |
620 else: |
706 else: |
621 value = prop.default_value() |
707 value = prop.default_value() |
627 |
713 |
628 def key(self): |
714 def key(self): |
629 """Unique key for this entity. |
715 """Unique key for this entity. |
630 |
716 |
631 This property is only available if this entity is already stored in the |
717 This property is only available if this entity is already stored in the |
632 datastore, so it is available if this entity was fetched returned from a |
718 datastore or if it has a full key, so it is available if this entity was |
633 query, or after put() is called the first time for new entities. |
719 fetched returned from a query, or after put() is called the first time |
|
720 for new entities, or if a complete key was given when constructed. |
634 |
721 |
635 Returns: |
722 Returns: |
636 Datastore key of persisted entity. |
723 Datastore key of persisted entity. |
637 |
724 |
638 Raises: |
725 Raises: |
639 NotSavedError when entity is not persistent. |
726 NotSavedError when entity is not persistent. |
640 """ |
727 """ |
641 if self.is_saved(): |
728 if self.is_saved(): |
642 return self._entity.key() |
729 return self._entity.key() |
|
730 elif self._key: |
|
731 return self._key |
643 elif self._key_name: |
732 elif self._key_name: |
644 if self._parent_key: |
|
645 parent_key = self._parent_key |
|
646 elif self._parent: |
|
647 parent_key = self._parent.key() |
|
648 parent = self._parent_key or (self._parent and self._parent.key()) |
733 parent = self._parent_key or (self._parent and self._parent.key()) |
649 return Key.from_path(self.kind(), self._key_name, parent=parent) |
734 self._key = Key.from_path(self.kind(), self._key_name, parent=parent) |
|
735 return self._key |
650 else: |
736 else: |
651 raise NotSavedError() |
737 raise NotSavedError() |
652 |
738 |
653 def _to_entity(self, entity): |
739 def _to_entity(self, entity): |
654 """Copies information from this model to provided entity. |
740 """Copies information from this model to provided entity. |
711 """ |
800 """ |
712 if self.is_saved(): |
801 if self.is_saved(): |
713 entity = self._entity |
802 entity = self._entity |
714 else: |
803 else: |
715 kwds = {'_app': self._app, |
804 kwds = {'_app': self._app, |
716 'name': self._key_name, |
|
717 'unindexed_properties': self._unindexed_properties} |
805 'unindexed_properties': self._unindexed_properties} |
718 |
806 if self._key is not None: |
719 if self._parent_key is not None: |
807 if self._key.id(): |
720 kwds['parent'] = self._parent_key |
808 kwds['id'] = self._key.id() |
721 elif self._parent is not None: |
809 else: |
722 kwds['parent'] = self._parent._entity |
810 kwds['name'] = self._key.name() |
|
811 if self._key.parent(): |
|
812 kwds['parent'] = self._key.parent() |
|
813 else: |
|
814 if self._key_name is not None: |
|
815 kwds['name'] = self._key_name |
|
816 if self._parent_key is not None: |
|
817 kwds['parent'] = self._parent_key |
|
818 elif self._parent is not None: |
|
819 kwds['parent'] = self._parent._entity |
723 entity = _entity_class(self.kind(), **kwds) |
820 entity = _entity_class(self.kind(), **kwds) |
724 |
821 |
725 self._to_entity(entity) |
822 self._to_entity(entity) |
726 return entity |
823 return entity |
727 |
824 |
747 return self._entity is not None |
844 return self._entity is not None |
748 |
845 |
749 def has_key(self): |
846 def has_key(self): |
750 """Determine if this model instance has a complete key. |
847 """Determine if this model instance has a complete key. |
751 |
848 |
752 Ids are not assigned until the data is saved to the Datastore, but |
849 When not using a fully self-assigned Key, ids are not assigned until the |
753 instances with a key name always have a full key. |
850 data is saved to the Datastore, but instances with a key name always have |
754 |
851 a full key. |
755 Returns: |
852 |
756 True if the object has been persisted to the datastore or has a key_name, |
853 Returns: |
757 otherwise False. |
854 True if the object has been persisted to the datastore or has a key |
758 """ |
855 or has a key_name, otherwise False. |
759 return self.is_saved() or self._key_name |
856 """ |
|
857 return self.is_saved() or self._key or self._key_name |
760 |
858 |
761 def dynamic_properties(self): |
859 def dynamic_properties(self): |
762 """Returns a list of all dynamic properties defined for instance.""" |
860 """Returns a list of all dynamic properties defined for instance.""" |
763 return [] |
861 return [] |
764 |
862 |
1015 raise KindError('Class %s cannot handle kind \'%s\'' % |
1115 raise KindError('Class %s cannot handle kind \'%s\'' % |
1016 (repr(cls), entity.kind())) |
1116 (repr(cls), entity.kind())) |
1017 |
1117 |
1018 entity_values = cls._load_entity_values(entity) |
1118 entity_values = cls._load_entity_values(entity) |
1019 instance = cls(None, _from_entity=True, **entity_values) |
1119 instance = cls(None, _from_entity=True, **entity_values) |
1020 instance._entity = entity |
1120 if entity.is_saved(): |
1021 del instance._key_name |
1121 instance._entity = entity |
|
1122 del instance._key_name |
|
1123 del instance._key |
|
1124 elif entity.key().has_id_or_name(): |
|
1125 instance._key = entity.key() |
1022 return instance |
1126 return instance |
1023 |
1127 |
1024 @classmethod |
1128 @classmethod |
1025 def kind(cls): |
1129 def kind(cls): |
1026 """Returns the datastore kind we use for this model. |
1130 """Returns the datastore kind we use for this model. |
1124 else: |
1228 else: |
1125 key = model_or_key |
1229 key = model_or_key |
1126 keys.append(key) |
1230 keys.append(key) |
1127 datastore.Delete(keys) |
1231 datastore.Delete(keys) |
1128 |
1232 |
|
1233 def allocate_ids(model, size): |
|
1234 """Allocates a range of IDs of size for the model_key defined by model |
|
1235 |
|
1236 Allocates a range of IDs in the datastore such that those IDs will not |
|
1237 be automatically assigned to new entities. You can only allocate IDs |
|
1238 for model keys from your app. If there is an error, raises a subclass of |
|
1239 datastore_errors.Error. |
|
1240 |
|
1241 Args: |
|
1242 model: Model, Key or string to serve as a model specifying the ID sequence |
|
1243 in which to allocate IDs |
|
1244 |
|
1245 Returns: |
|
1246 (start, end) of the allocated range, inclusive. |
|
1247 """ |
|
1248 models_or_keys, multiple = datastore.NormalizeAndTypeCheck( |
|
1249 model, (Model, Key, basestring)) |
|
1250 keys = [] |
|
1251 for model_or_key in models_or_keys: |
|
1252 if isinstance(model_or_key, Model): |
|
1253 key = model_or_key = model_or_key.key() |
|
1254 elif isinstance(model_or_key, basestring): |
|
1255 key = model_or_key = Key(model_or_key) |
|
1256 else: |
|
1257 key = model_or_key |
|
1258 keys.append(key) |
|
1259 return datastore.AllocateIds(keys, size) |
1129 |
1260 |
1130 class Expando(Model): |
1261 class Expando(Model): |
1131 """Dynamically expandable model. |
1262 """Dynamically expandable model. |
1132 |
1263 |
1133 An Expando does not require (but can still benefit from) the definition |
1264 An Expando does not require (but can still benefit from) the definition |
1584 def _get_query(self, |
1722 def _get_query(self, |
1585 _query_class=datastore.Query, |
1723 _query_class=datastore.Query, |
1586 _multi_query_class=datastore.MultiQuery): |
1724 _multi_query_class=datastore.MultiQuery): |
1587 queries = [] |
1725 queries = [] |
1588 for query_set in self.__query_sets: |
1726 for query_set in self.__query_sets: |
1589 query = _query_class(self._model_class.kind(), |
1727 if self._model_class is not None: |
|
1728 kind = self._model_class.kind() |
|
1729 else: |
|
1730 kind = None |
|
1731 query = _query_class(kind, |
1590 query_set, |
1732 query_set, |
1591 keys_only=self._keys_only) |
1733 keys_only=self._keys_only) |
1592 query.Order(*self.__orderings) |
1734 query.Order(*self.__orderings) |
1593 if self.__ancestor is not None: |
1735 if self.__ancestor is not None: |
1594 query.Ancestor(self.__ancestor) |
1736 query.Ancestor(self.__ancestor) |
1663 if match.group(3) is not None: |
1805 if match.group(3) is not None: |
1664 operator = match.group(3) |
1806 operator = match.group(3) |
1665 else: |
1807 else: |
1666 operator = '==' |
1808 operator = '==' |
1667 |
1809 |
1668 if prop in self._model_class._unindexed_properties: |
1810 if self._model_class is None: |
|
1811 if prop != datastore_types._KEY_SPECIAL_PROPERTY: |
|
1812 raise BadQueryError( |
|
1813 'Only %s filters are allowed on kindless queries.' % |
|
1814 datastore_types._KEY_SPECIAL_PROPERTY) |
|
1815 elif prop in self._model_class._unindexed_properties: |
1669 raise PropertyError('Property \'%s\' is not indexed' % prop) |
1816 raise PropertyError('Property \'%s\' is not indexed' % prop) |
1670 |
1817 |
1671 if operator.lower() == 'in': |
1818 if operator.lower() == 'in': |
1672 if self._keys_only: |
1819 if self._keys_only: |
1673 raise BadQueryError('Keys only queries do not support IN filters.') |
1820 raise BadQueryError('Keys only queries do not support IN filters.') |
1709 property = property[1:] |
1856 property = property[1:] |
1710 order = datastore.Query.DESCENDING |
1857 order = datastore.Query.DESCENDING |
1711 else: |
1858 else: |
1712 order = datastore.Query.ASCENDING |
1859 order = datastore.Query.ASCENDING |
1713 |
1860 |
1714 if not issubclass(self._model_class, Expando): |
1861 if self._model_class is None: |
1715 if (property not in self._model_class.properties() and |
1862 if (property != datastore_types._KEY_SPECIAL_PROPERTY or |
1716 property not in datastore_types._SPECIAL_PROPERTIES): |
1863 order != datastore.Query.ASCENDING): |
1717 raise PropertyError('Invalid property name \'%s\'' % property) |
1864 raise BadQueryError( |
1718 |
1865 'Only %s ascending orders are supported on kindless queries' % |
1719 if property in self._model_class._unindexed_properties: |
1866 datastore_types._KEY_SPECIAL_PROPERTY) |
1720 raise PropertyError('Property \'%s\' is not indexed' % property) |
1867 else: |
|
1868 if not issubclass(self._model_class, Expando): |
|
1869 if (property not in self._model_class.properties() and |
|
1870 property not in datastore_types._SPECIAL_PROPERTIES): |
|
1871 raise PropertyError('Invalid property name \'%s\'' % property) |
|
1872 |
|
1873 if property in self._model_class._unindexed_properties: |
|
1874 raise PropertyError('Property \'%s\' is not indexed' % property) |
1721 |
1875 |
1722 self.__orderings.append((property, order)) |
1876 self.__orderings.append((property, order)) |
1723 return self |
1877 return self |
1724 |
1878 |
1725 def ancestor(self, ancestor): |
1879 def ancestor(self, ancestor): |
1772 """ |
1926 """ |
1773 from google.appengine.ext import gql |
1927 from google.appengine.ext import gql |
1774 app = kwds.pop('_app', None) |
1928 app = kwds.pop('_app', None) |
1775 |
1929 |
1776 self._proto_query = gql.GQL(query_string, _app=app) |
1930 self._proto_query = gql.GQL(query_string, _app=app) |
1777 model_class = class_for_kind(self._proto_query._entity) |
1931 if self._proto_query._entity is not None: |
|
1932 model_class = class_for_kind(self._proto_query._entity) |
|
1933 else: |
|
1934 model_class = None |
1778 super(GqlQuery, self).__init__(model_class, |
1935 super(GqlQuery, self).__init__(model_class, |
1779 keys_only=self._proto_query._keys_only) |
1936 keys_only=self._proto_query._keys_only) |
1780 |
1937 |
1781 for property, unused in (self._proto_query.filters().keys() + |
1938 if model_class is not None: |
1782 self._proto_query.orderings()): |
1939 for property, unused in (self._proto_query.filters().keys() + |
1783 if property in model_class._unindexed_properties: |
1940 self._proto_query.orderings()): |
1784 raise PropertyError('Property \'%s\' is not indexed' % property) |
1941 if property in model_class._unindexed_properties: |
|
1942 raise PropertyError('Property \'%s\' is not indexed' % property) |
1785 |
1943 |
1786 self.bind(*args, **kwds) |
1944 self.bind(*args, **kwds) |
1787 |
1945 |
1788 def bind(self, *args, **kwds): |
1946 def bind(self, *args, **kwds): |
1789 """Bind arguments (positional or keyword) to the query. |
1947 """Bind arguments (positional or keyword) to the query. |