diff -r 5c931bd3dc1e -r a7766286a7be thirdparty/google_appengine/google/appengine/ext/db/__init__.py --- a/thirdparty/google_appengine/google/appengine/ext/db/__init__.py Thu Feb 12 10:24:37 2009 +0000 +++ b/thirdparty/google_appengine/google/appengine/ext/db/__init__.py Thu Feb 12 12:30:36 2009 +0000 @@ -77,10 +77,13 @@ +import copy import datetime import logging +import re import time import urlparse +import warnings from google.appengine.api import datastore from google.appengine.api import datastore_errors @@ -187,6 +190,11 @@ _ALLOWED_EXPANDO_PROPERTY_TYPES = set(_ALLOWED_PROPERTY_TYPES) _ALLOWED_EXPANDO_PROPERTY_TYPES.update((list, tuple, type(None))) +_OPERATORS = ['<', '<=', '>', '>=', '=', '==', '!=', 'in'] +_FILTER_REGEX = re.compile( + '^\s*([^\s]+)(\s+(%s)\s*)?$' % '|'.join(_OPERATORS), + re.IGNORECASE | re.UNICODE) + def class_for_kind(kind): """Return base-class responsible for implementing kind. @@ -527,12 +535,12 @@ **kwds): """Creates a new instance of this model. - To create a new entity, you instantiate a model and then call save(), + To create a new entity, you instantiate a model and then call put(), which saves the entity to the datastore: person = Person() person.name = 'Bret' - person.save() + person.put() You can initialize properties in the model in the constructor with keyword arguments: @@ -595,7 +603,7 @@ This property is only available if this entity is already stored in the datastore, so it is available if this entity was fetched returned from a - query, or after save() is called the first time for new entities. + query, or after put() is called the first time for new entities. Returns: Datastore key of persisted entity. @@ -606,7 +614,11 @@ if self.is_saved(): return self._entity.key() elif self._key_name: - parent = self._parent and self._parent.key() + if self._parent_key: + parent_key = self._parent_key + elif self._parent: + parent_key = self._parent.key() + parent = self._parent_key or (self._parent and self._parent.key()) return Key.from_path(self.kind(), self._key_name, parent=parent) else: raise NotSavedError() @@ -1143,7 +1155,7 @@ 1 - Because it is not possible for the datastore to know what kind of property to store on an undefined expando value, setting a property to - None is the same as deleting it form the expando. + None is the same as deleting it from the expando. 2 - Persistent variables on Expando must not begin with '_'. These variables considered to be 'protected' in Python, and are used @@ -1524,16 +1536,71 @@ model_class: Model class to build query for. """ super(Query, self).__init__(model_class) - self.__query_set = {} + self.__query_sets = [{}] self.__orderings = [] self.__ancestor = None - def _get_query(self, _query_class=datastore.Query): - query = _query_class(self._model_class.kind(), self.__query_set) - if self.__ancestor is not None: - query.Ancestor(self.__ancestor) - query.Order(*self.__orderings) - return query + def _get_query(self, + _query_class=datastore.Query, + _multi_query_class=datastore.MultiQuery): + queries = [] + for query_set in self.__query_sets: + query = _query_class(self._model_class.kind(), query_set) + if self.__ancestor is not None: + query.Ancestor(self.__ancestor) + queries.append(query) + + if (_query_class != datastore.Query and + _multi_query_class == datastore.MultiQuery): + warnings.warn( + 'Custom _query_class specified without corresponding custom' + ' _query_multi_class. Things will break if you use queries with' + ' the "IN" or "!=" operators.', RuntimeWarning) + if len(queries) > 1: + raise datastore_errors.BadArgumentError( + 'Query requires multiple subqueries to satisfy. If _query_class' + ' is overridden, _multi_query_class must also be overridden.') + elif (_query_class == datastore.Query and + _multi_query_class != datastore.MultiQuery): + raise BadArgumentError('_query_class must also be overridden if' + ' _multi_query_class is overridden.') + + if len(queries) == 1: + queries[0].Order(*self.__orderings) + return queries[0] + else: + return _multi_query_class(queries, self.__orderings) + + def __filter_disjunction(self, operations, values): + """Add a disjunction of several filters and several values to the query. + + This is implemented by duplicating queries and combining the + results later. + + Args: + operations: a string or list of strings. Each string contains a + property name and an operator to filter by. The operators + themselves must not require multiple queries to evaluate + (currently, this means that 'in' and '!=' are invalid). + + values: a value or list of filter values, normalized by + _normalize_query_parameter. + """ + if not isinstance(operations, (list, tuple)): + operations = [operations] + if not isinstance(values, (list, tuple)): + values = [values] + + new_query_sets = [] + for operation in operations: + if operation.lower().endswith('in') or operation.endswith('!='): + raise BadQueryError('Cannot use "in" or "!=" in a disjunction.') + for query_set in self.__query_sets: + for value in values: + new_query_set = copy.copy(query_set) + datastore._AddOrAppend(new_query_set, operation, value) + new_query_sets.append(new_query_set) + self.__query_sets = new_query_sets def filter(self, property_operator, value): """Add filter to query. @@ -1545,11 +1612,29 @@ Returns: Self to support method chaining. """ - if isinstance(value, (list, tuple)): - raise BadValueError('Filtering on lists is not supported') - - value = _normalize_query_parameter(value) - datastore._AddOrAppend(self.__query_set, property_operator, value) + match = _FILTER_REGEX.match(property_operator) + prop = match.group(1) + if match.group(3) is not None: + operator = match.group(3) + else: + operator = '==' + + if operator.lower() == 'in': + if not isinstance(value, (list, tuple)): + raise BadValueError('Argument to the "in" operator must be a list') + values = [_normalize_query_parameter(v) for v in value] + self.__filter_disjunction(prop + ' =', values) + else: + if isinstance(value, (list, tuple)): + raise BadValueError('Filtering on lists is not supported') + if operator == '!=': + self.__filter_disjunction([prop + ' <', prop + ' >'], + _normalize_query_parameter(value)) + else: + value = _normalize_query_parameter(value) + for query_set in self.__query_sets: + datastore._AddOrAppend(query_set, property_operator, value) + return self def order(self, property): @@ -2359,8 +2444,11 @@ Returns: validated list appropriate to save in the datastore. """ - return self.validate_list_contents( + value = self.validate_list_contents( super(ListProperty, self).get_value_for_datastore(model_instance)) + if self.validator: + self.validator(value) + return value class StringListProperty(ListProperty): @@ -2622,5 +2710,7 @@ run_in_transaction = datastore.RunInTransaction +run_in_transaction_custom_retries = datastore.RunInTransactionCustomRetries RunInTransaction = run_in_transaction +RunInTransactionCustomRetries = run_in_transaction_custom_retries