widgets.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. from flask import json
  2. from jinja2 import escape
  3. from wtforms.widgets import HTMLString, html_params
  4. from flask_admin._compat import as_unicode, text_type
  5. from flask_admin.babel import gettext
  6. from flask_admin.helpers import get_url
  7. from flask_admin.form import RenderTemplateWidget
  8. class InlineFieldListWidget(RenderTemplateWidget):
  9. def __init__(self):
  10. super(InlineFieldListWidget, self).__init__('admin/model/inline_field_list.html')
  11. class InlineFormWidget(RenderTemplateWidget):
  12. def __init__(self):
  13. super(InlineFormWidget, self).__init__('admin/model/inline_form.html')
  14. def __call__(self, field, **kwargs):
  15. kwargs.setdefault('form_opts', getattr(field, 'form_opts', None))
  16. return super(InlineFormWidget, self).__call__(field, **kwargs)
  17. class AjaxSelect2Widget(object):
  18. def __init__(self, multiple=False):
  19. self.multiple = multiple
  20. def __call__(self, field, **kwargs):
  21. kwargs.setdefault('data-role', 'select2-ajax')
  22. kwargs.setdefault('data-url', get_url('.ajax_lookup', name=field.loader.name))
  23. allow_blank = getattr(field, 'allow_blank', False)
  24. if allow_blank and not self.multiple:
  25. kwargs['data-allow-blank'] = u'1'
  26. kwargs.setdefault('id', field.id)
  27. kwargs.setdefault('type', 'hidden')
  28. if self.multiple:
  29. result = []
  30. ids = []
  31. for value in field.data:
  32. data = field.loader.format(value)
  33. result.append(data)
  34. ids.append(as_unicode(data[0]))
  35. separator = getattr(field, 'separator', ',')
  36. kwargs['value'] = separator.join(ids)
  37. kwargs['data-json'] = json.dumps(result)
  38. kwargs['data-multiple'] = u'1'
  39. else:
  40. data = field.loader.format(field.data)
  41. if data:
  42. kwargs['value'] = data[0]
  43. kwargs['data-json'] = json.dumps(data)
  44. placeholder = field.loader.options.get('placeholder', gettext('Please select model'))
  45. kwargs.setdefault('data-placeholder', placeholder)
  46. return HTMLString('<input %s>' % html_params(name=field.name, **kwargs))
  47. class XEditableWidget(object):
  48. """
  49. WTForms widget that provides in-line editing for the list view.
  50. Determines how to display the x-editable/ajax form based on the
  51. field inside of the FieldList (StringField, IntegerField, etc).
  52. """
  53. def __call__(self, field, **kwargs):
  54. kwargs.setdefault('data-value', kwargs.pop('display_value', ''))
  55. kwargs.setdefault('data-role', 'x-editable')
  56. kwargs.setdefault('data-url', './ajax/update/')
  57. kwargs.setdefault('id', field.id)
  58. kwargs.setdefault('name', field.name)
  59. kwargs.setdefault('href', '#')
  60. if not kwargs.get('pk'):
  61. raise Exception('pk required')
  62. kwargs['data-pk'] = str(kwargs.pop("pk"))
  63. kwargs['data-csrf'] = kwargs.pop("csrf", "")
  64. kwargs = self.get_kwargs(field, kwargs)
  65. return HTMLString(
  66. '<a %s>%s</a>' % (html_params(**kwargs),
  67. escape(kwargs['data-value']))
  68. )
  69. def get_kwargs(self, field, kwargs):
  70. """
  71. Return extra kwargs based on the field type.
  72. """
  73. if field.type == 'StringField':
  74. kwargs['data-type'] = 'text'
  75. elif field.type == 'TextAreaField':
  76. kwargs['data-type'] = 'textarea'
  77. kwargs['data-rows'] = '5'
  78. elif field.type == 'BooleanField':
  79. kwargs['data-type'] = 'select'
  80. # data-source = dropdown options
  81. kwargs['data-source'] = json.dumps([
  82. {'value': '', 'text': gettext('No')},
  83. {'value': '1', 'text': gettext('Yes')}
  84. ])
  85. kwargs['data-role'] = 'x-editable-boolean'
  86. elif field.type in ['Select2Field', 'SelectField']:
  87. kwargs['data-type'] = 'select'
  88. choices = [{'value': x, 'text': y} for x, y in field.choices]
  89. # prepend a blank field to choices if allow_blank = True
  90. if getattr(field, 'allow_blank', False):
  91. choices.insert(0, {'value': '__None', 'text': ''})
  92. # json.dumps fixes issue with unicode strings not loading correctly
  93. kwargs['data-source'] = json.dumps(choices)
  94. elif field.type == 'DateField':
  95. kwargs['data-type'] = 'combodate'
  96. kwargs['data-format'] = 'YYYY-MM-DD'
  97. kwargs['data-template'] = 'YYYY-MM-DD'
  98. elif field.type == 'DateTimeField':
  99. kwargs['data-type'] = 'combodate'
  100. kwargs['data-format'] = 'YYYY-MM-DD HH:mm:ss'
  101. kwargs['data-template'] = 'YYYY-MM-DD HH:mm:ss'
  102. # x-editable-combodate uses 1 minute increments
  103. kwargs['data-role'] = 'x-editable-combodate'
  104. elif field.type == 'TimeField':
  105. kwargs['data-type'] = 'combodate'
  106. kwargs['data-format'] = 'HH:mm:ss'
  107. kwargs['data-template'] = 'HH:mm:ss'
  108. kwargs['data-role'] = 'x-editable-combodate'
  109. elif field.type == 'IntegerField':
  110. kwargs['data-type'] = 'number'
  111. elif field.type in ['FloatField', 'DecimalField']:
  112. kwargs['data-type'] = 'number'
  113. kwargs['data-step'] = 'any'
  114. elif field.type in ['QuerySelectField', 'ModelSelectField',
  115. 'QuerySelectMultipleField', 'KeyPropertyField']:
  116. # QuerySelectField and ModelSelectField are for relations
  117. kwargs['data-type'] = 'select'
  118. choices = []
  119. selected_ids = []
  120. for value, label, selected in field.iter_choices():
  121. try:
  122. label = text_type(label)
  123. except TypeError:
  124. # unable to display text value
  125. label = ''
  126. choices.append({'value': text_type(value), 'text': label})
  127. if selected:
  128. selected_ids.append(value)
  129. # blank field is already included if allow_blank
  130. kwargs['data-source'] = json.dumps(choices)
  131. if field.type == 'QuerySelectMultipleField':
  132. kwargs['data-type'] = 'select2'
  133. kwargs['data-role'] = 'x-editable-select2-multiple'
  134. # must use id instead of text or prefilled values won't work
  135. separator = getattr(field, 'separator', ',')
  136. kwargs['data-value'] = separator.join(selected_ids)
  137. else:
  138. raise Exception('Unsupported field type: %s' % (type(field),))
  139. return kwargs