267 raise DuplicatePropertyError('Duplicate property: %s' % attr_name) |
267 raise DuplicatePropertyError('Duplicate property: %s' % attr_name) |
268 defined.add(attr_name) |
268 defined.add(attr_name) |
269 model_class._properties[attr_name] = attr |
269 model_class._properties[attr_name] = attr |
270 attr.__property_config__(model_class, attr_name) |
270 attr.__property_config__(model_class, attr_name) |
271 |
271 |
|
272 model_class._unindexed_properties = frozenset( |
|
273 name for name, prop in model_class._properties.items() if not prop.indexed) |
|
274 |
272 |
275 |
273 class PropertiedClass(type): |
276 class PropertiedClass(type): |
274 """Meta-class for initializing Model classes properties. |
277 """Meta-class for initializing Model classes properties. |
275 |
278 |
276 Used for initializing Properties defined in the context of a model. |
279 Used for initializing Properties defined in the context of a model. |
334 title = db.StringProperty() |
337 title = db.StringProperty() |
335 """ |
338 """ |
336 |
339 |
337 creation_counter = 0 |
340 creation_counter = 0 |
338 |
341 |
339 def __init__(self, verbose_name=None, name=None, default=None, |
342 def __init__(self, |
340 required=False, validator=None, choices=None): |
343 verbose_name=None, |
|
344 name=None, |
|
345 default=None, |
|
346 required=False, |
|
347 validator=None, |
|
348 choices=None, |
|
349 indexed=True): |
341 """Initializes this Property with the given options. |
350 """Initializes this Property with the given options. |
342 |
351 |
343 Args: |
352 Args: |
344 verbose_name: User friendly name of property. |
353 verbose_name: User friendly name of property. |
345 name: Storage name for property. By default, uses attribute name |
354 name: Storage name for property. By default, uses attribute name |
346 as it is assigned in the Model sub-class. |
355 as it is assigned in the Model sub-class. |
347 default: Default value for property if none is assigned. |
356 default: Default value for property if none is assigned. |
348 required: Whether property is required. |
357 required: Whether property is required. |
349 validator: User provided method used for validation. |
358 validator: User provided method used for validation. |
350 choices: User provided set of valid property values. |
359 choices: User provided set of valid property values. |
|
360 indexed: Whether property is indexed. |
351 """ |
361 """ |
352 self.verbose_name = verbose_name |
362 self.verbose_name = verbose_name |
353 self.name = name |
363 self.name = name |
354 self.default = default |
364 self.default = default |
355 self.required = required |
365 self.required = required |
356 self.validator = validator |
366 self.validator = validator |
357 self.choices = choices |
367 self.choices = choices |
|
368 self.indexed = indexed |
358 self.creation_counter = Property.creation_counter |
369 self.creation_counter = Property.creation_counter |
359 Property.creation_counter += 1 |
370 Property.creation_counter += 1 |
360 |
371 |
361 def __property_config__(self, model_class, property_name): |
372 def __property_config__(self, model_class, property_name): |
362 """Configure property, connecting it to its model. |
373 """Configure property, connecting it to its model. |
486 |
497 |
487 Returns: |
498 Returns: |
488 The value converted for use as a model instance attribute. |
499 The value converted for use as a model instance attribute. |
489 """ |
500 """ |
490 return value |
501 return value |
|
502 |
|
503 def _require_parameter(self, kwds, parameter, value): |
|
504 """Sets kwds[parameter] to value. |
|
505 |
|
506 If kwds[parameter] exists and is not value, raises ConfigurationError. |
|
507 |
|
508 Args: |
|
509 kwds: The parameter dict, which maps parameter names (strings) to values. |
|
510 parameter: The name of the parameter to set. |
|
511 value: The value to set it to. |
|
512 """ |
|
513 if parameter in kwds and kwds[parameter] != value: |
|
514 raise ConfigurationError('%s must be %s.' % (parameter, value)) |
|
515 |
|
516 kwds[parameter] = value |
491 |
517 |
492 def _attr_name(self): |
518 def _attr_name(self): |
493 """Attribute name we use for this property in model instances. |
519 """Attribute name we use for this property in model instances. |
494 |
520 |
495 DO NOT USE THIS METHOD. |
521 DO NOT USE THIS METHOD. |
683 self._entity or a new Entity which is not stored on the instance. |
709 self._entity or a new Entity which is not stored on the instance. |
684 """ |
710 """ |
685 if self.is_saved(): |
711 if self.is_saved(): |
686 entity = self._entity |
712 entity = self._entity |
687 else: |
713 else: |
|
714 kwds = {'_app': self._app, |
|
715 'name': self._key_name, |
|
716 'unindexed_properties': self._unindexed_properties} |
|
717 |
688 if self._parent_key is not None: |
718 if self._parent_key is not None: |
689 entity = _entity_class(self.kind(), |
719 kwds['parent'] = self._parent_key |
690 parent=self._parent_key, |
|
691 name=self._key_name, |
|
692 _app=self._app) |
|
693 elif self._parent is not None: |
720 elif self._parent is not None: |
694 entity = _entity_class(self.kind(), |
721 kwds['parent'] = self._parent._entity |
695 parent=self._parent._entity, |
722 entity = _entity_class(self.kind(), **kwds) |
696 name=self._key_name, |
|
697 _app=self._app) |
|
698 else: |
|
699 entity = _entity_class(self.kind(), |
|
700 name=self._key_name, |
|
701 _app=self._app) |
|
702 |
723 |
703 self._to_entity(entity) |
724 self._to_entity(entity) |
704 return entity |
725 return entity |
705 |
726 |
706 def delete(self): |
727 def delete(self): |
930 entity.put() |
951 entity.put() |
931 return entity |
952 return entity |
932 return run_in_transaction(txn) |
953 return run_in_transaction(txn) |
933 |
954 |
934 @classmethod |
955 @classmethod |
935 def all(cls): |
956 def all(cls, **kwds): |
936 """Returns a query over all instances of this model from the datastore. |
957 """Returns a query over all instances of this model from the datastore. |
937 |
958 |
938 Returns: |
959 Returns: |
939 Query that will retrieve all instances from entity collection. |
960 Query that will retrieve all instances from entity collection. |
940 """ |
961 """ |
941 return Query(cls) |
962 return Query(cls, **kwds) |
942 |
963 |
943 @classmethod |
964 @classmethod |
944 def gql(cls, query_string, *args, **kwds): |
965 def gql(cls, query_string, *args, **kwds): |
945 """Returns a query using GQL query string. |
966 """Returns a query using GQL query string. |
946 |
967 |
1298 |
1319 |
1299 |
1320 |
1300 class _BaseQuery(object): |
1321 class _BaseQuery(object): |
1301 """Base class for both Query and GqlQuery.""" |
1322 """Base class for both Query and GqlQuery.""" |
1302 |
1323 |
1303 def __init__(self, model_class): |
1324 def __init__(self, model_class, keys_only=False): |
1304 """Constructor." |
1325 """Constructor. |
1305 |
1326 |
1306 Args: |
1327 Args: |
1307 model_class: Model class from which entities are constructed. |
1328 model_class: Model class from which entities are constructed. |
|
1329 keys_only: Whether the query should return full entities or only keys. |
1308 """ |
1330 """ |
1309 self._model_class = model_class |
1331 self._model_class = model_class |
|
1332 self._keys_only = keys_only |
|
1333 |
|
1334 def is_keys_only(self): |
|
1335 """Returns whether this query is keys only. |
|
1336 |
|
1337 Returns: |
|
1338 True if this query returns keys, False if it returns entities. |
|
1339 """ |
|
1340 return self._keys_only |
1310 |
1341 |
1311 def _get_query(self): |
1342 def _get_query(self): |
1312 """Subclass must override (and not call their super method). |
1343 """Subclass must override (and not call their super method). |
1313 |
1344 |
1314 Returns: |
1345 Returns: |
1323 or use a GQL query with a LIMIT clause. It's more efficient. |
1354 or use a GQL query with a LIMIT clause. It's more efficient. |
1324 |
1355 |
1325 Returns: |
1356 Returns: |
1326 Iterator for this query. |
1357 Iterator for this query. |
1327 """ |
1358 """ |
1328 return _QueryIterator(self._model_class, iter(self._get_query().Run())) |
1359 iterator = self._get_query().Run() |
|
1360 if self._keys_only: |
|
1361 return iterator |
|
1362 else: |
|
1363 return _QueryIterator(self._model_class, iter(iterator)) |
1329 |
1364 |
1330 def __iter__(self): |
1365 def __iter__(self): |
1331 """Iterator for this query. |
1366 """Iterator for this query. |
1332 |
1367 |
1333 If you know the number of results you need, consider fetch() instead, |
1368 If you know the number of results you need, consider fetch() instead, |
1386 if limit < 0 or offset < 0: |
1421 if limit < 0 or offset < 0: |
1387 raise ValueError('Arguments to fetch() must be >= 0') |
1422 raise ValueError('Arguments to fetch() must be >= 0') |
1388 if limit == 0: |
1423 if limit == 0: |
1389 return [] |
1424 return [] |
1390 raw = self._get_query().Get(limit, offset) |
1425 raw = self._get_query().Get(limit, offset) |
1391 return map(self._model_class.from_entity, raw) |
1426 |
|
1427 if self._keys_only: |
|
1428 return raw |
|
1429 else: |
|
1430 return [self._model_class.from_entity(e) for e in raw] |
1392 |
1431 |
1393 def __getitem__(self, arg): |
1432 def __getitem__(self, arg): |
1394 """Support for query[index] and query[start:stop]. |
1433 """Support for query[index] and query[start:stop]. |
1395 |
1434 |
1396 Beware: this ignores the LIMIT clause on GQL queries. |
1435 Beware: this ignores the LIMIT clause on GQL queries. |
1527 |
1566 |
1528 for story in Query(story).filter('title =', 'Foo').order('-date'): |
1567 for story in Query(story).filter('title =', 'Foo').order('-date'): |
1529 print story.title |
1568 print story.title |
1530 """ |
1569 """ |
1531 |
1570 |
1532 def __init__(self, model_class): |
1571 def __init__(self, model_class, keys_only=False): |
1533 """Constructs a query over instances of the given Model. |
1572 """Constructs a query over instances of the given Model. |
1534 |
1573 |
1535 Args: |
1574 Args: |
1536 model_class: Model class to build query for. |
1575 model_class: Model class to build query for. |
1537 """ |
1576 keys_only: Whether the query should return full entities or only keys. |
1538 super(Query, self).__init__(model_class) |
1577 """ |
|
1578 super(Query, self).__init__(model_class, keys_only) |
1539 self.__query_sets = [{}] |
1579 self.__query_sets = [{}] |
1540 self.__orderings = [] |
1580 self.__orderings = [] |
1541 self.__ancestor = None |
1581 self.__ancestor = None |
1542 |
1582 |
1543 def _get_query(self, |
1583 def _get_query(self, |
1544 _query_class=datastore.Query, |
1584 _query_class=datastore.Query, |
1545 _multi_query_class=datastore.MultiQuery): |
1585 _multi_query_class=datastore.MultiQuery): |
1546 queries = [] |
1586 queries = [] |
1547 for query_set in self.__query_sets: |
1587 for query_set in self.__query_sets: |
1548 query = _query_class(self._model_class.kind(), query_set) |
1588 query = _query_class(self._model_class.kind(), |
|
1589 query_set, |
|
1590 keys_only=self._keys_only) |
|
1591 query.Order(*self.__orderings) |
1549 if self.__ancestor is not None: |
1592 if self.__ancestor is not None: |
1550 query.Ancestor(self.__ancestor) |
1593 query.Ancestor(self.__ancestor) |
1551 queries.append(query) |
1594 queries.append(query) |
1552 |
1595 |
1553 if (_query_class != datastore.Query and |
1596 if (_query_class != datastore.Query and |
1564 _multi_query_class != datastore.MultiQuery): |
1607 _multi_query_class != datastore.MultiQuery): |
1565 raise BadArgumentError('_query_class must also be overridden if' |
1608 raise BadArgumentError('_query_class must also be overridden if' |
1566 ' _multi_query_class is overridden.') |
1609 ' _multi_query_class is overridden.') |
1567 |
1610 |
1568 if len(queries) == 1: |
1611 if len(queries) == 1: |
1569 queries[0].Order(*self.__orderings) |
|
1570 return queries[0] |
1612 return queries[0] |
1571 else: |
1613 else: |
1572 return _multi_query_class(queries, self.__orderings) |
1614 return _multi_query_class(queries, self.__orderings) |
1573 |
1615 |
1574 def __filter_disjunction(self, operations, values): |
1616 def __filter_disjunction(self, operations, values): |
1609 property_operator: string with the property and operator to filter by. |
1651 property_operator: string with the property and operator to filter by. |
1610 value: the filter value. |
1652 value: the filter value. |
1611 |
1653 |
1612 Returns: |
1654 Returns: |
1613 Self to support method chaining. |
1655 Self to support method chaining. |
|
1656 |
|
1657 Raises: |
|
1658 PropertyError if invalid property is provided. |
1614 """ |
1659 """ |
1615 match = _FILTER_REGEX.match(property_operator) |
1660 match = _FILTER_REGEX.match(property_operator) |
1616 prop = match.group(1) |
1661 prop = match.group(1) |
1617 if match.group(3) is not None: |
1662 if match.group(3) is not None: |
1618 operator = match.group(3) |
1663 operator = match.group(3) |
1619 else: |
1664 else: |
1620 operator = '==' |
1665 operator = '==' |
1621 |
1666 |
|
1667 if prop in self._model_class._unindexed_properties: |
|
1668 raise PropertyError('Property \'%s\' is not indexed' % prop) |
|
1669 |
1622 if operator.lower() == 'in': |
1670 if operator.lower() == 'in': |
1623 if not isinstance(value, (list, tuple)): |
1671 if self._keys_only: |
|
1672 raise BadQueryError('Keys only queries do not support IN filters.') |
|
1673 elif not isinstance(value, (list, tuple)): |
1624 raise BadValueError('Argument to the "in" operator must be a list') |
1674 raise BadValueError('Argument to the "in" operator must be a list') |
1625 values = [_normalize_query_parameter(v) for v in value] |
1675 values = [_normalize_query_parameter(v) for v in value] |
1626 self.__filter_disjunction(prop + ' =', values) |
1676 self.__filter_disjunction(prop + ' =', values) |
1627 else: |
1677 else: |
1628 if isinstance(value, (list, tuple)): |
1678 if isinstance(value, (list, tuple)): |
1629 raise BadValueError('Filtering on lists is not supported') |
1679 raise BadValueError('Filtering on lists is not supported') |
1630 if operator == '!=': |
1680 if operator == '!=': |
|
1681 if self._keys_only: |
|
1682 raise BadQueryError('Keys only queries do not support != filters.') |
1631 self.__filter_disjunction([prop + ' <', prop + ' >'], |
1683 self.__filter_disjunction([prop + ' <', prop + ' >'], |
1632 _normalize_query_parameter(value)) |
1684 _normalize_query_parameter(value)) |
1633 else: |
1685 else: |
1634 value = _normalize_query_parameter(value) |
1686 value = _normalize_query_parameter(value) |
1635 for query_set in self.__query_sets: |
1687 for query_set in self.__query_sets: |
1660 |
1712 |
1661 if not issubclass(self._model_class, Expando): |
1713 if not issubclass(self._model_class, Expando): |
1662 if (property not in self._model_class.properties() and |
1714 if (property not in self._model_class.properties() and |
1663 property not in datastore_types._SPECIAL_PROPERTIES): |
1715 property not in datastore_types._SPECIAL_PROPERTIES): |
1664 raise PropertyError('Invalid property name \'%s\'' % property) |
1716 raise PropertyError('Invalid property name \'%s\'' % property) |
|
1717 |
|
1718 if property in self._model_class._unindexed_properties: |
|
1719 raise PropertyError('Property \'%s\' is not indexed' % property) |
1665 |
1720 |
1666 self.__orderings.append((property, order)) |
1721 self.__orderings.append((property, order)) |
1667 return self |
1722 return self |
1668 |
1723 |
1669 def ancestor(self, ancestor): |
1724 def ancestor(self, ancestor): |
1707 |
1762 |
1708 Args: |
1763 Args: |
1709 query_string: Properly formatted GQL query string. |
1764 query_string: Properly formatted GQL query string. |
1710 *args: Positional arguments used to bind numeric references in the query. |
1765 *args: Positional arguments used to bind numeric references in the query. |
1711 **kwds: Dictionary-based arguments for named references. |
1766 **kwds: Dictionary-based arguments for named references. |
|
1767 |
|
1768 Raises: |
|
1769 PropertyError if the query filters or sorts on a property that's not |
|
1770 indexed. |
1712 """ |
1771 """ |
1713 from google.appengine.ext import gql |
1772 from google.appengine.ext import gql |
1714 app = kwds.pop('_app', None) |
1773 app = kwds.pop('_app', None) |
|
1774 |
1715 self._proto_query = gql.GQL(query_string, _app=app) |
1775 self._proto_query = gql.GQL(query_string, _app=app) |
1716 super(GqlQuery, self).__init__(class_for_kind(self._proto_query._entity)) |
1776 model_class = class_for_kind(self._proto_query._entity) |
|
1777 super(GqlQuery, self).__init__(model_class, |
|
1778 keys_only=self._proto_query._keys_only) |
|
1779 |
|
1780 for property, unused in (self._proto_query.filters().keys() + |
|
1781 self._proto_query.orderings()): |
|
1782 if property in model_class._unindexed_properties: |
|
1783 raise PropertyError('Property \'%s\' is not indexed' % property) |
|
1784 |
1717 self.bind(*args, **kwds) |
1785 self.bind(*args, **kwds) |
1718 |
1786 |
1719 def bind(self, *args, **kwds): |
1787 def bind(self, *args, **kwds): |
1720 """Bind arguments (positional or keyword) to the query. |
1788 """Bind arguments (positional or keyword) to the query. |
1721 |
1789 |
1738 self._kwds[name] = _normalize_query_parameter(arg) |
1806 self._kwds[name] = _normalize_query_parameter(arg) |
1739 |
1807 |
1740 def run(self): |
1808 def run(self): |
1741 """Override _BaseQuery.run() so the LIMIT clause is handled properly.""" |
1809 """Override _BaseQuery.run() so the LIMIT clause is handled properly.""" |
1742 query_run = self._proto_query.Run(*self._args, **self._kwds) |
1810 query_run = self._proto_query.Run(*self._args, **self._kwds) |
1743 return _QueryIterator(self._model_class, iter(query_run)) |
1811 if self._keys_only: |
|
1812 return query_run |
|
1813 else: |
|
1814 return _QueryIterator(self._model_class, iter(query_run)) |
1744 |
1815 |
1745 def _get_query(self): |
1816 def _get_query(self): |
1746 return self._proto_query.Bind(self._args, self._kwds) |
1817 return self._proto_query.Bind(self._args, self._kwds) |
1747 |
1818 |
1748 |
1819 |
1749 class TextProperty(Property): |
1820 class UnindexedProperty(Property): |
1750 """A string that can be longer than 500 bytes. |
1821 """A property that isn't indexed by either built-in or composite indices. |
1751 |
1822 |
1752 This type should be used for large text values to make sure the datastore |
1823 TextProperty and BlobProperty derive from this class. |
1753 has good performance for queries. |
|
1754 """ |
1824 """ |
|
1825 def __init__(self, *args, **kwds): |
|
1826 """Construct property. See the Property class for details. |
|
1827 |
|
1828 Raises: |
|
1829 ConfigurationError if indexed=True. |
|
1830 """ |
|
1831 self._require_parameter(kwds, 'indexed', False) |
|
1832 kwds['indexed'] = True |
|
1833 super(UnindexedProperty, self).__init__(*args, **kwds) |
1755 |
1834 |
1756 def validate(self, value): |
1835 def validate(self, value): |
1757 """Validate text property. |
1836 """Validate property. |
1758 |
1837 |
1759 Returns: |
1838 Returns: |
1760 A valid value. |
1839 A valid value. |
1761 |
1840 |
1762 Raises: |
1841 Raises: |
1763 BadValueError if property is not instance of 'Text'. |
1842 BadValueError if property is not an instance of data_type. |
1764 """ |
1843 """ |
1765 if value is not None and not isinstance(value, Text): |
1844 if value is not None and not isinstance(value, self.data_type): |
1766 try: |
1845 try: |
1767 value = Text(value) |
1846 value = self.data_type(value) |
1768 except TypeError, err: |
1847 except TypeError, err: |
1769 raise BadValueError('Property %s must be convertible ' |
1848 raise BadValueError('Property %s must be convertible ' |
1770 'to a Text instance (%s)' % (self.name, err)) |
1849 'to a %s instance (%s)' % |
1771 value = super(TextProperty, self).validate(value) |
1850 (self.name, self.data_type.__name__, err)) |
1772 if value is not None and not isinstance(value, Text): |
1851 value = super(UnindexedProperty, self).validate(value) |
1773 raise BadValueError('Property %s must be a Text instance' % self.name) |
1852 if value is not None and not isinstance(value, self.data_type): |
|
1853 raise BadValueError('Property %s must be a %s instance' % |
|
1854 (self.name, self.data_type.__name__)) |
1774 return value |
1855 return value |
|
1856 |
|
1857 |
|
1858 class TextProperty(UnindexedProperty): |
|
1859 """A string that can be longer than 500 bytes.""" |
1775 |
1860 |
1776 data_type = Text |
1861 data_type = Text |
1777 |
1862 |
1778 |
1863 |
1779 class StringProperty(Property): |
1864 class StringProperty(Property): |
1884 """A property whose values are PostalAddress instances.""" |
1969 """A property whose values are PostalAddress instances.""" |
1885 |
1970 |
1886 data_type = PostalAddress |
1971 data_type = PostalAddress |
1887 |
1972 |
1888 |
1973 |
1889 class BlobProperty(Property): |
1974 class BlobProperty(UnindexedProperty): |
1890 """A string that can be longer than 500 bytes. |
1975 """A byte string that can be longer than 500 bytes.""" |
1891 |
|
1892 This type should be used for large binary values to make sure the datastore |
|
1893 has good performance for queries. |
|
1894 """ |
|
1895 |
|
1896 def validate(self, value): |
|
1897 """Validate blob property. |
|
1898 |
|
1899 Returns: |
|
1900 A valid value. |
|
1901 |
|
1902 Raises: |
|
1903 BadValueError if property is not instance of 'Blob'. |
|
1904 """ |
|
1905 if value is not None and not isinstance(value, Blob): |
|
1906 try: |
|
1907 value = Blob(value) |
|
1908 except TypeError, err: |
|
1909 raise BadValueError('Property %s must be convertible ' |
|
1910 'to a Blob instance (%s)' % (self.name, err)) |
|
1911 value = super(BlobProperty, self).validate(value) |
|
1912 if value is not None and not isinstance(value, Blob): |
|
1913 raise BadValueError('Property %s must be a Blob instance' % self.name) |
|
1914 return value |
|
1915 |
1976 |
1916 data_type = Blob |
1977 data_type = Blob |
1917 |
1978 |
1918 |
1979 |
1919 class ByteStringProperty(Property): |
1980 class ByteStringProperty(Property): |
2264 |
2325 |
2265 |
2326 |
2266 class UserProperty(Property): |
2327 class UserProperty(Property): |
2267 """A user property.""" |
2328 """A user property.""" |
2268 |
2329 |
2269 def __init__(self, verbose_name=None, name=None, |
2330 def __init__(self, |
2270 required=False, validator=None, choices=None, |
2331 verbose_name=None, |
2271 auto_current_user=False, auto_current_user_add=False): |
2332 name=None, |
|
2333 required=False, |
|
2334 validator=None, |
|
2335 choices=None, |
|
2336 auto_current_user=False, |
|
2337 auto_current_user_add=False, |
|
2338 indexed=True): |
2272 """Initializes this Property with the given options. |
2339 """Initializes this Property with the given options. |
2273 |
2340 |
2274 Note: this does *not* support the 'default' keyword argument. |
2341 Note: this does *not* support the 'default' keyword argument. |
2275 Use auto_current_user_add=True instead. |
2342 Use auto_current_user_add=True instead. |
2276 |
2343 |
2283 choices: User provided set of valid property values. |
2350 choices: User provided set of valid property values. |
2284 auto_current_user: If true, the value is set to the current user |
2351 auto_current_user: If true, the value is set to the current user |
2285 each time the entity is written to the datastore. |
2352 each time the entity is written to the datastore. |
2286 auto_current_user_add: If true, the value is set to the current user |
2353 auto_current_user_add: If true, the value is set to the current user |
2287 the first time the entity is written to the datastore. |
2354 the first time the entity is written to the datastore. |
|
2355 indexed: Whether property is indexed. |
2288 """ |
2356 """ |
2289 super(UserProperty, self).__init__(verbose_name, name, |
2357 super(UserProperty, self).__init__(verbose_name, name, |
2290 required=required, |
2358 required=required, |
2291 validator=validator, |
2359 validator=validator, |
2292 choices=choices) |
2360 choices=choices, |
|
2361 indexed=indexed) |
2293 self.auto_current_user = auto_current_user |
2362 self.auto_current_user = auto_current_user |
2294 self.auto_current_user_add = auto_current_user_add |
2363 self.auto_current_user_add = auto_current_user_add |
2295 |
2364 |
2296 def validate(self, value): |
2365 def validate(self, value): |
2297 """Validate user. |
2366 """Validate user. |
2358 item_type = basestring |
2427 item_type = basestring |
2359 if not isinstance(item_type, type): |
2428 if not isinstance(item_type, type): |
2360 raise TypeError('Item type should be a type object') |
2429 raise TypeError('Item type should be a type object') |
2361 if item_type not in _ALLOWED_PROPERTY_TYPES: |
2430 if item_type not in _ALLOWED_PROPERTY_TYPES: |
2362 raise ValueError('Item type %s is not acceptable' % item_type.__name__) |
2431 raise ValueError('Item type %s is not acceptable' % item_type.__name__) |
2363 if 'required' in kwds and kwds['required'] is not True: |
2432 if issubclass(item_type, (Blob, Text)): |
2364 raise ValueError('List values must be required') |
2433 self._require_parameter(kwds, 'indexed', False) |
|
2434 kwds['indexed'] = True |
|
2435 self._require_parameter(kwds, 'required', True) |
2365 if default is None: |
2436 if default is None: |
2366 default = [] |
2437 default = [] |
2367 self.item_type = item_type |
2438 self.item_type = item_type |
2368 super(ListProperty, self).__init__(verbose_name, |
2439 super(ListProperty, self).__init__(verbose_name, |
2369 required=True, |
|
2370 default=default, |
2440 default=default, |
2371 **kwds) |
2441 **kwds) |
2372 |
2442 |
2373 def validate(self, value): |
2443 def validate(self, value): |
2374 """Validate list. |
2444 """Validate list. |