fields.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. from __future__ import unicode_literals
  2. import decimal
  3. import operator
  4. from wtforms import fields, widgets
  5. from wtforms.compat import text_type, string_types
  6. class ReferencePropertyField(fields.SelectFieldBase):
  7. """
  8. A field for ``db.ReferenceProperty``. The list items are rendered in a
  9. select.
  10. :param reference_class:
  11. A db.Model class which will be used to generate the default query
  12. to make the list of items. If this is not specified, The `query`
  13. property must be overridden before validation.
  14. :param get_label:
  15. If a string, use this attribute on the model class as the label
  16. associated with each option. If a one-argument callable, this callable
  17. will be passed model instance and expected to return the label text.
  18. Otherwise, the model object's `__str__` or `__unicode__` will be used.
  19. :param allow_blank:
  20. If set to true, a blank choice will be added to the top of the list
  21. to allow `None` to be chosen.
  22. :param blank_text:
  23. Use this to override the default blank option's label.
  24. """
  25. widget = widgets.Select()
  26. def __init__(self, label=None, validators=None, reference_class=None,
  27. get_label=None, allow_blank=False,
  28. blank_text='', **kwargs):
  29. super(ReferencePropertyField, self).__init__(label, validators,
  30. **kwargs)
  31. if get_label is None:
  32. self.get_label = lambda x: x
  33. elif isinstance(get_label, string_types):
  34. self.get_label = operator.attrgetter(get_label)
  35. else:
  36. self.get_label = get_label
  37. self.allow_blank = allow_blank
  38. self.blank_text = blank_text
  39. self._set_data(None)
  40. if reference_class is not None:
  41. self.query = reference_class.all()
  42. def _get_data(self):
  43. if self._formdata is not None:
  44. for obj in self.query:
  45. if str(obj.key()) == self._formdata:
  46. self._set_data(obj)
  47. break
  48. return self._data
  49. def _set_data(self, data):
  50. self._data = data
  51. self._formdata = None
  52. data = property(_get_data, _set_data)
  53. def iter_choices(self):
  54. if self.allow_blank:
  55. yield ('__None', self.blank_text, self.data is None)
  56. for obj in self.query:
  57. key = str(obj.key())
  58. label = self.get_label(obj)
  59. yield (key, label, (self.data.key() == obj.key()) if self.data else False)
  60. def process_formdata(self, valuelist):
  61. if valuelist:
  62. if valuelist[0] == '__None':
  63. self.data = None
  64. else:
  65. self._data = None
  66. self._formdata = valuelist[0]
  67. def pre_validate(self, form):
  68. if not self.allow_blank or self.data is not None:
  69. for obj in self.query:
  70. if str(self.data.key()) == str(obj.key()):
  71. break
  72. else:
  73. raise ValueError(self.gettext('Not a valid choice'))
  74. class KeyPropertyField(fields.SelectFieldBase):
  75. """
  76. A field for ``ndb.KeyProperty``. The list items are rendered in a select.
  77. :param reference_class:
  78. A db.Model class which will be used to generate the default query
  79. to make the list of items. If this is not specified, The `query`
  80. property must be overridden before validation.
  81. :param get_label:
  82. If a string, use this attribute on the model class as the label
  83. associated with each option. If a one-argument callable, this callable
  84. will be passed model instance and expected to return the label text.
  85. Otherwise, the model object's `__str__` or `__unicode__` will be used.
  86. :param allow_blank:
  87. If set to true, a blank choice will be added to the top of the list
  88. to allow `None` to be chosen.
  89. :param blank_text:
  90. Use this to override the default blank option's label.
  91. """
  92. widget = widgets.Select()
  93. def __init__(self, label=None, validators=None, reference_class=None,
  94. get_label=None, allow_blank=False, blank_text='', **kwargs):
  95. super(KeyPropertyField, self).__init__(label, validators, **kwargs)
  96. if get_label is None:
  97. self.get_label = lambda x: x
  98. elif isinstance(get_label, basestring):
  99. self.get_label = operator.attrgetter(get_label)
  100. else:
  101. self.get_label = get_label
  102. self.allow_blank = allow_blank
  103. self.blank_text = blank_text
  104. self._set_data(None)
  105. if reference_class is not None:
  106. self.query = reference_class.query()
  107. def _get_data(self):
  108. if self._formdata is not None:
  109. for obj in self.query:
  110. if str(obj.key.id()) == self._formdata:
  111. self._set_data(obj)
  112. break
  113. return self._data
  114. def _set_data(self, data):
  115. self._data = data
  116. self._formdata = None
  117. data = property(_get_data, _set_data)
  118. def iter_choices(self):
  119. if self.allow_blank:
  120. yield ('__None', self.blank_text, self.data is None)
  121. for obj in self.query:
  122. key = str(obj.key.id())
  123. label = self.get_label(obj)
  124. yield (key, label, (self.data.key == obj.key) if self.data else False)
  125. def process_formdata(self, valuelist):
  126. if valuelist:
  127. if valuelist[0] == '__None':
  128. self.data = None
  129. else:
  130. self._data = None
  131. self._formdata = valuelist[0]
  132. def pre_validate(self, form):
  133. if self.data is not None:
  134. for obj in self.query:
  135. if self.data.key == obj.key:
  136. break
  137. else:
  138. raise ValueError(self.gettext('Not a valid choice'))
  139. elif not self.allow_blank:
  140. raise ValueError(self.gettext('Not a valid choice'))
  141. class StringListPropertyField(fields.TextAreaField):
  142. """
  143. A field for ``db.StringListProperty``. The list items are rendered in a
  144. textarea.
  145. """
  146. def _value(self):
  147. if self.raw_data:
  148. return self.raw_data[0]
  149. else:
  150. return self.data and text_type("\n".join(self.data)) or ''
  151. def process_formdata(self, valuelist):
  152. if valuelist:
  153. try:
  154. self.data = valuelist[0].splitlines()
  155. except ValueError:
  156. raise ValueError(self.gettext('Not a valid list'))
  157. class IntegerListPropertyField(fields.TextAreaField):
  158. """
  159. A field for ``db.StringListProperty``. The list items are rendered in a
  160. textarea.
  161. """
  162. def _value(self):
  163. if self.raw_data:
  164. return self.raw_data[0]
  165. else:
  166. return text_type('\n'.join(self.data)) if self.data else ''
  167. def process_formdata(self, valuelist):
  168. if valuelist:
  169. try:
  170. self.data = [int(value) for value in valuelist[0].splitlines()]
  171. except ValueError:
  172. raise ValueError(self.gettext('Not a valid integer list'))
  173. class GeoPtPropertyField(fields.TextField):
  174. def process_formdata(self, valuelist):
  175. if valuelist:
  176. try:
  177. lat, lon = valuelist[0].split(',')
  178. self.data = '%s,%s' % (decimal.Decimal(lat.strip()), decimal.Decimal(lon.strip()),)
  179. except (decimal.InvalidOperation, ValueError):
  180. raise ValueError('Not a valid coordinate location')