File: fields.py

package info (click to toggle)
flask-mongoengine 1.0.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid
  • size: 508 kB
  • sloc: python: 2,127; makefile: 109
file content (213 lines) | stat: -rw-r--r-- 6,154 bytes parent folder | download
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")