1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
"""
Useful form fields for use with the mongoengine.
"""
import json
import sys
from gettext import gettext as _
from mongoengine.queryset import DoesNotExist
from wtforms import widgets
from wtforms.fields import SelectFieldBase, StringField, TextAreaField
from wtforms.validators import ValidationError
__all__ = (
"ModelSelectField",
"QuerySetSelectField",
)
if sys.version_info >= (3, 0):
unicode = str
class QuerySetSelectField(SelectFieldBase):
"""
Given a QuerySet either at initialization or inside a view, will display a
select drop-down field of choices. The `data` property actually will
store/keep an ORM model instance, not the ID. Submitting a choice which is
not in the queryset will result in a validation error.
Specifying `label_attr` in the constructor will use that property of the
model instance for display in the list, else the model object's `__str__`
or `__unicode__` will be used.
If `allow_blank` is set to `True`, then a blank choice will be added to the
top of the list. Selecting this choice will result in the `data` property
being `None`. The label for the blank choice can be set by specifying the
`blank_text` parameter.
"""
widget = widgets.Select()
def __init__(
self,
label="",
validators=None,
queryset=None,
label_attr="",
allow_blank=False,
blank_text="---",
label_modifier=None,
**kwargs,
):
super(QuerySetSelectField, self).__init__(label, validators, **kwargs)
self.label_attr = label_attr
self.allow_blank = allow_blank
self.blank_text = blank_text
self.label_modifier = label_modifier
self.queryset = queryset
def iter_choices(self):
if self.allow_blank:
yield ("__None", self.blank_text, self.data is None)
if self.queryset is None:
return
self.queryset.rewind()
for obj in self.queryset:
label = (
self.label_modifier(obj)
if self.label_modifier
else (self.label_attr and getattr(obj, self.label_attr) or obj)
)
if isinstance(self.data, list):
selected = obj in self.data
else:
selected = self._is_selected(obj)
yield (obj.id, label, selected)
def process_formdata(self, valuelist):
if valuelist:
if valuelist[0] == "__None":
self.data = None
else:
if self.queryset is None:
self.data = None
return
try:
obj = self.queryset.get(pk=valuelist[0])
self.data = obj
except DoesNotExist:
self.data = None
def pre_validate(self, form):
if not self.allow_blank or self.data is not None:
if not self.data:
raise ValidationError(_("Not a valid choice"))
def _is_selected(self, item):
return item == self.data
class QuerySetSelectMultipleField(QuerySetSelectField):
widget = widgets.Select(multiple=True)
def __init__(
self,
label="",
validators=None,
queryset=None,
label_attr="",
allow_blank=False,
blank_text="---",
**kwargs,
):
super(QuerySetSelectMultipleField, self).__init__(
label, validators, queryset, label_attr, allow_blank, blank_text, **kwargs
)
def process_formdata(self, valuelist):
if valuelist:
if valuelist[0] == "__None":
self.data = None
else:
if not self.queryset:
self.data = None
return
self.queryset.rewind()
self.data = list(self.queryset(pk__in=valuelist))
if not len(self.data):
self.data = None
# If no value passed, empty the list
else:
self.data = None
def _is_selected(self, item):
return item in self.data if self.data else False
class ModelSelectField(QuerySetSelectField):
"""
Like a QuerySetSelectField, except takes a model class instead of a
queryset and lists everything in it.
"""
def __init__(self, label="", validators=None, model=None, **kwargs):
queryset = kwargs.pop("queryset", model.objects)
super(ModelSelectField, self).__init__(
label, validators, queryset=queryset, **kwargs
)
class ModelSelectMultipleField(QuerySetSelectMultipleField):
"""
Allows multiple select
"""
def __init__(self, label="", validators=None, model=None, **kwargs):
queryset = kwargs.pop("queryset", model.objects)
super(ModelSelectMultipleField, self).__init__(
label, validators, queryset=queryset, **kwargs
)
class JSONField(TextAreaField):
def _value(self):
if self.raw_data:
return self.raw_data[0]
else:
return self.data and unicode(json.dumps(self.data)) or ""
def process_formdata(self, value):
if value:
try:
self.data = json.loads(value[0])
except ValueError:
raise ValueError(self.gettext("Invalid JSON data."))
class DictField(JSONField):
def process_formdata(self, value):
super(DictField, self).process_formdata(value)
if value and not isinstance(self.data, dict):
raise ValueError(self.gettext("Not a valid dictionary."))
class NoneStringField(StringField):
"""
Custom StringField that counts "" as None
"""
def process_formdata(self, valuelist):
if valuelist:
self.data = valuelist[0]
if self.data == "":
self.data = None
class BinaryField(TextAreaField):
"""
Custom TextAreaField that converts its value with binary_type.
"""
def process_formdata(self, valuelist):
if valuelist:
self.data = bytes(valuelist[0], "utf-8")
|