thirdparty/google_appengine/google/appengine/ext/db/__init__.py
changeset 1278 a7766286a7be
parent 828 f5fd65cc3bf3
child 2309 be1b94099f2d
equal deleted inserted replaced
1277:5c931bd3dc1e 1278:a7766286a7be
    75 
    75 
    76 
    76 
    77 
    77 
    78 
    78 
    79 
    79 
       
    80 import copy
    80 import datetime
    81 import datetime
    81 import logging
    82 import logging
       
    83 import re
    82 import time
    84 import time
    83 import urlparse
    85 import urlparse
       
    86 import warnings
    84 
    87 
    85 from google.appengine.api import datastore
    88 from google.appengine.api import datastore
    86 from google.appengine.api import datastore_errors
    89 from google.appengine.api import datastore_errors
    87 from google.appengine.api import datastore_types
    90 from google.appengine.api import datastore_types
    88 from google.appengine.api import users
    91 from google.appengine.api import users
   185     ])
   188     ])
   186 
   189 
   187 _ALLOWED_EXPANDO_PROPERTY_TYPES = set(_ALLOWED_PROPERTY_TYPES)
   190 _ALLOWED_EXPANDO_PROPERTY_TYPES = set(_ALLOWED_PROPERTY_TYPES)
   188 _ALLOWED_EXPANDO_PROPERTY_TYPES.update((list, tuple, type(None)))
   191 _ALLOWED_EXPANDO_PROPERTY_TYPES.update((list, tuple, type(None)))
   189 
   192 
       
   193 _OPERATORS = ['<', '<=', '>', '>=', '=', '==', '!=', 'in']
       
   194 _FILTER_REGEX = re.compile(
       
   195     '^\s*([^\s]+)(\s+(%s)\s*)?$' % '|'.join(_OPERATORS),
       
   196     re.IGNORECASE | re.UNICODE)
       
   197 
   190 
   198 
   191 def class_for_kind(kind):
   199 def class_for_kind(kind):
   192   """Return base-class responsible for implementing kind.
   200   """Return base-class responsible for implementing kind.
   193 
   201 
   194   Necessary to recover the class responsible for implementing provided
   202   Necessary to recover the class responsible for implementing provided
   525                _app=None,
   533                _app=None,
   526                _from_entity=False,
   534                _from_entity=False,
   527                **kwds):
   535                **kwds):
   528     """Creates a new instance of this model.
   536     """Creates a new instance of this model.
   529 
   537 
   530     To create a new entity, you instantiate a model and then call save(),
   538     To create a new entity, you instantiate a model and then call put(),
   531     which saves the entity to the datastore:
   539     which saves the entity to the datastore:
   532 
   540 
   533        person = Person()
   541        person = Person()
   534        person.name = 'Bret'
   542        person.name = 'Bret'
   535        person.save()
   543        person.put()
   536 
   544 
   537     You can initialize properties in the model in the constructor with keyword
   545     You can initialize properties in the model in the constructor with keyword
   538     arguments:
   546     arguments:
   539 
   547 
   540        person = Person(name='Bret')
   548        person = Person(name='Bret')
   593   def key(self):
   601   def key(self):
   594     """Unique key for this entity.
   602     """Unique key for this entity.
   595 
   603 
   596     This property is only available if this entity is already stored in the
   604     This property is only available if this entity is already stored in the
   597     datastore, so it is available if this entity was fetched returned from a
   605     datastore, so it is available if this entity was fetched returned from a
   598     query, or after save() is called the first time for new entities.
   606     query, or after put() is called the first time for new entities.
   599 
   607 
   600     Returns:
   608     Returns:
   601       Datastore key of persisted entity.
   609       Datastore key of persisted entity.
   602 
   610 
   603     Raises:
   611     Raises:
   604       NotSavedError when entity is not persistent.
   612       NotSavedError when entity is not persistent.
   605     """
   613     """
   606     if self.is_saved():
   614     if self.is_saved():
   607       return self._entity.key()
   615       return self._entity.key()
   608     elif self._key_name:
   616     elif self._key_name:
   609       parent = self._parent and self._parent.key()
   617       if self._parent_key:
       
   618         parent_key = self._parent_key
       
   619       elif self._parent:
       
   620           parent_key = self._parent.key()
       
   621       parent = self._parent_key or (self._parent and self._parent.key())
   610       return Key.from_path(self.kind(), self._key_name, parent=parent)
   622       return Key.from_path(self.kind(), self._key_name, parent=parent)
   611     else:
   623     else:
   612       raise NotSavedError()
   624       raise NotSavedError()
   613 
   625 
   614   def _to_entity(self, entity):
   626   def _to_entity(self, entity):
  1141     properties, such as methods and other values in __dict__ take precedence
  1153     properties, such as methods and other values in __dict__ take precedence
  1142     over values in the datastore.
  1154     over values in the datastore.
  1143 
  1155 
  1144     1 - Because it is not possible for the datastore to know what kind of
  1156     1 - Because it is not possible for the datastore to know what kind of
  1145         property to store on an undefined expando value, setting a property to
  1157         property to store on an undefined expando value, setting a property to
  1146         None is the same as deleting it form the expando.
  1158         None is the same as deleting it from the expando.
  1147 
  1159 
  1148     2 - Persistent variables on Expando must not begin with '_'.  These
  1160     2 - Persistent variables on Expando must not begin with '_'.  These
  1149         variables considered to be 'protected' in Python, and are used
  1161         variables considered to be 'protected' in Python, and are used
  1150         internally.
  1162         internally.
  1151 
  1163 
  1522 
  1534 
  1523     Args:
  1535     Args:
  1524       model_class: Model class to build query for.
  1536       model_class: Model class to build query for.
  1525     """
  1537     """
  1526     super(Query, self).__init__(model_class)
  1538     super(Query, self).__init__(model_class)
  1527     self.__query_set = {}
  1539     self.__query_sets = [{}]
  1528     self.__orderings = []
  1540     self.__orderings = []
  1529     self.__ancestor = None
  1541     self.__ancestor = None
  1530 
  1542 
  1531   def _get_query(self, _query_class=datastore.Query):
  1543   def _get_query(self,
  1532     query = _query_class(self._model_class.kind(), self.__query_set)
  1544                  _query_class=datastore.Query,
  1533     if self.__ancestor is not None:
  1545                  _multi_query_class=datastore.MultiQuery):
  1534       query.Ancestor(self.__ancestor)
  1546     queries = []
  1535     query.Order(*self.__orderings)
  1547     for query_set in self.__query_sets:
  1536     return query
  1548       query = _query_class(self._model_class.kind(), query_set)
       
  1549       if self.__ancestor is not None:
       
  1550         query.Ancestor(self.__ancestor)
       
  1551       queries.append(query)
       
  1552 
       
  1553     if (_query_class != datastore.Query and
       
  1554         _multi_query_class == datastore.MultiQuery):
       
  1555       warnings.warn(
       
  1556           'Custom _query_class specified without corresponding custom'
       
  1557           ' _query_multi_class. Things will break if you use queries with'
       
  1558           ' the "IN" or "!=" operators.', RuntimeWarning)
       
  1559       if len(queries) > 1:
       
  1560         raise datastore_errors.BadArgumentError(
       
  1561             'Query requires multiple subqueries to satisfy. If _query_class'
       
  1562             ' is overridden, _multi_query_class must also be overridden.')
       
  1563     elif (_query_class == datastore.Query and
       
  1564           _multi_query_class != datastore.MultiQuery):
       
  1565       raise BadArgumentError('_query_class must also be overridden if'
       
  1566                              ' _multi_query_class is overridden.')
       
  1567 
       
  1568     if len(queries) == 1:
       
  1569       queries[0].Order(*self.__orderings)
       
  1570       return queries[0]
       
  1571     else:
       
  1572       return _multi_query_class(queries, self.__orderings)
       
  1573 
       
  1574   def __filter_disjunction(self, operations, values):
       
  1575     """Add a disjunction of several filters and several values to the query.
       
  1576 
       
  1577     This is implemented by duplicating queries and combining the
       
  1578     results later.
       
  1579 
       
  1580     Args:
       
  1581       operations: a string or list of strings. Each string contains a
       
  1582         property name and an operator to filter by. The operators
       
  1583         themselves must not require multiple queries to evaluate
       
  1584         (currently, this means that 'in' and '!=' are invalid).
       
  1585 
       
  1586       values: a value or list of filter values, normalized by
       
  1587         _normalize_query_parameter.
       
  1588     """
       
  1589     if not isinstance(operations, (list, tuple)):
       
  1590       operations = [operations]
       
  1591     if not isinstance(values, (list, tuple)):
       
  1592       values = [values]
       
  1593 
       
  1594     new_query_sets = []
       
  1595     for operation in operations:
       
  1596       if operation.lower().endswith('in') or operation.endswith('!='):
       
  1597         raise BadQueryError('Cannot use "in" or "!=" in a disjunction.')
       
  1598       for query_set in self.__query_sets:
       
  1599         for value in values:
       
  1600           new_query_set = copy.copy(query_set)
       
  1601           datastore._AddOrAppend(new_query_set, operation, value)
       
  1602           new_query_sets.append(new_query_set)
       
  1603     self.__query_sets = new_query_sets
  1537 
  1604 
  1538   def filter(self, property_operator, value):
  1605   def filter(self, property_operator, value):
  1539     """Add filter to query.
  1606     """Add filter to query.
  1540 
  1607 
  1541     Args:
  1608     Args:
  1543       value: the filter value.
  1610       value: the filter value.
  1544 
  1611 
  1545     Returns:
  1612     Returns:
  1546       Self to support method chaining.
  1613       Self to support method chaining.
  1547     """
  1614     """
  1548     if isinstance(value, (list, tuple)):
  1615     match = _FILTER_REGEX.match(property_operator)
  1549       raise BadValueError('Filtering on lists is not supported')
  1616     prop = match.group(1)
  1550 
  1617     if match.group(3) is not None:
  1551     value = _normalize_query_parameter(value)
  1618       operator = match.group(3)
  1552     datastore._AddOrAppend(self.__query_set, property_operator, value)
  1619     else:
       
  1620       operator = '=='
       
  1621 
       
  1622     if operator.lower() == 'in':
       
  1623       if not isinstance(value, (list, tuple)):
       
  1624         raise BadValueError('Argument to the "in" operator must be a list')
       
  1625       values = [_normalize_query_parameter(v) for v in value]
       
  1626       self.__filter_disjunction(prop + ' =', values)
       
  1627     else:
       
  1628       if isinstance(value, (list, tuple)):
       
  1629         raise BadValueError('Filtering on lists is not supported')
       
  1630       if operator == '!=':
       
  1631         self.__filter_disjunction([prop + ' <', prop + ' >'],
       
  1632                                   _normalize_query_parameter(value))
       
  1633       else:
       
  1634         value = _normalize_query_parameter(value)
       
  1635         for query_set in self.__query_sets:
       
  1636           datastore._AddOrAppend(query_set, property_operator, value)
       
  1637 
  1553     return self
  1638     return self
  1554 
  1639 
  1555   def order(self, property):
  1640   def order(self, property):
  1556     """Set order of query result.
  1641     """Set order of query result.
  1557 
  1642 
  2357     """Get value from property to send to datastore.
  2442     """Get value from property to send to datastore.
  2358 
  2443 
  2359     Returns:
  2444     Returns:
  2360       validated list appropriate to save in the datastore.
  2445       validated list appropriate to save in the datastore.
  2361     """
  2446     """
  2362     return self.validate_list_contents(
  2447     value = self.validate_list_contents(
  2363         super(ListProperty, self).get_value_for_datastore(model_instance))
  2448         super(ListProperty, self).get_value_for_datastore(model_instance))
       
  2449     if self.validator:
       
  2450       self.validator(value)
       
  2451     return value
  2364 
  2452 
  2365 
  2453 
  2366 class StringListProperty(ListProperty):
  2454 class StringListProperty(ListProperty):
  2367   """A property that stores a list of strings.
  2455   """A property that stores a list of strings.
  2368 
  2456 
  2620     """Not possible to set a new collection."""
  2708     """Not possible to set a new collection."""
  2621     raise BadValueError('Virtual property is read-only')
  2709     raise BadValueError('Virtual property is read-only')
  2622 
  2710 
  2623 
  2711 
  2624 run_in_transaction = datastore.RunInTransaction
  2712 run_in_transaction = datastore.RunInTransaction
       
  2713 run_in_transaction_custom_retries = datastore.RunInTransactionCustomRetries
  2625 
  2714 
  2626 RunInTransaction = run_in_transaction
  2715 RunInTransaction = run_in_transaction
       
  2716 RunInTransactionCustomRetries = run_in_transaction_custom_retries