Skip to content

Commit adfed53

Browse files
committed
Add DecimalField, FloatField and IntField wtf forms support
1 parent 14f81b6 commit adfed53

File tree

3 files changed

+58
-34
lines changed

3 files changed

+58
-34
lines changed

flask_mongoengine/db_fields.py

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,30 @@ def _setup_strings_common_validators(options: dict, obj: fields.StringField) ->
8585
return options
8686

8787

88+
@wtf_required
89+
def _setup_numbers_common_validators(
90+
options: dict, obj: Union[fields.IntField, fields.DecimalField, fields.FloatField]
91+
) -> dict:
92+
"""
93+
Extend :attr:`base_options` with common validators for number types.
94+
95+
:param options: dict, usually from :class:`WtfFieldMixin.wtf_generated_options`
96+
:param obj: Any :class:`mongoengine.fields.IntField` or
97+
:class:`mongoengine.fields.DecimalField` or
98+
:class:`mongoengine.fields.FloatField` subclasses instance.
99+
"""
100+
assert isinstance(
101+
obj, (fields.IntField, fields.DecimalField, fields.FloatField)
102+
), "Improperly configured"
103+
104+
if obj.min_value or obj.max_value:
105+
options["validators"].insert(
106+
0, wtf_validators_.NumberRange(min=obj.min_value, max=obj.max_value)
107+
)
108+
109+
return options
110+
111+
88112
class WtfFieldMixin:
89113
"""
90114
Extension wrapper class for mongoengine BaseField.
@@ -425,18 +449,16 @@ class DecimalField(WtfFieldMixin, fields.DecimalField):
425449
DEFAULT_WTF_FIELD = wtf_fields.DecimalField if wtf_fields else None
426450
DEFAULT_WTF_CHOICES_COERCE = decimal.Decimal
427451

428-
def to_wtf_field(
429-
self,
430-
*,
431-
model: Optional[Type] = None,
432-
field_kwargs: Optional[dict] = None,
433-
):
452+
@property
453+
@wtf_required
454+
def wtf_generated_options(self) -> dict:
434455
"""
435-
Protection from execution of :func:`to_wtf_field` in form generation.
436-
437-
:raises NotImplementedError: Field converter to WTForm Field not implemented.
456+
Extend form validators with :class:`wtforms.validators.NumberRange`.
438457
"""
439-
raise NotImplementedError("Field converter to WTForm Field not implemented.")
458+
options = super().wtf_generated_options
459+
options = _setup_numbers_common_validators(options, self)
460+
461+
return options
440462

441463

442464
class DictField(WtfFieldMixin, fields.DictField):
@@ -610,21 +632,19 @@ class FloatField(WtfFieldMixin, fields.FloatField):
610632
All arguments should be passed as keyword arguments, to exclude unexpected behaviour.
611633
"""
612634

613-
DEFAULT_WTF_FIELD = wtf_fields.FloatField if wtf_fields else None
635+
DEFAULT_WTF_FIELD = custom_fields.MongoFloatField if wtf_fields else None
614636
DEFAULT_WTF_CHOICES_COERCE = float
615637

616-
def to_wtf_field(
617-
self,
618-
*,
619-
model: Optional[Type] = None,
620-
field_kwargs: Optional[dict] = None,
621-
):
638+
@property
639+
@wtf_required
640+
def wtf_generated_options(self) -> dict:
622641
"""
623-
Protection from execution of :func:`to_wtf_field` in form generation.
624-
625-
:raises NotImplementedError: Field converter to WTForm Field not implemented.
642+
Extend form validators with :class:`wtforms.validators.NumberRange`.
626643
"""
627-
raise NotImplementedError("Field converter to WTForm Field not implemented.")
644+
options = super().wtf_generated_options
645+
options = _setup_numbers_common_validators(options, self)
646+
647+
return options
628648

629649

630650
class GenericEmbeddedDocumentField(WtfFieldMixin, fields.GenericEmbeddedDocumentField):
@@ -770,18 +790,16 @@ class IntField(WtfFieldMixin, fields.IntField):
770790
DEFAULT_WTF_FIELD = wtf_fields.IntegerField if wtf_fields else None
771791
DEFAULT_WTF_CHOICES_COERCE = int
772792

773-
def to_wtf_field(
774-
self,
775-
*,
776-
model: Optional[Type] = None,
777-
field_kwargs: Optional[dict] = None,
778-
):
793+
@property
794+
@wtf_required
795+
def wtf_generated_options(self) -> dict:
779796
"""
780-
Protection from execution of :func:`to_wtf_field` in form generation.
781-
782-
:raises NotImplementedError: Field converter to WTForm Field not implemented.
797+
Extend form validators with :class:`wtforms.validators.NumberRange`.
783798
"""
784-
raise NotImplementedError("Field converter to WTForm Field not implemented.")
799+
options = super().wtf_generated_options
800+
options = _setup_numbers_common_validators(options, self)
801+
802+
return options
785803

786804

787805
class LazyReferenceField(WtfFieldMixin, fields.LazyReferenceField):

flask_mongoengine/wtf/fields.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,3 +353,12 @@ class MongoURLField(EmptyStringIsNoneMixin, wtf_fields.URLField):
353353
"""
354354

355355
pass
356+
357+
358+
class MongoFloatField(wtf_fields.FloatField):
359+
"""
360+
Regular :class:`wtforms.fields.FloatField`, with widget replaced to
361+
:class:`wtforms.widgets.NumberInput`.
362+
"""
363+
364+
widget = wtf_widgets.NumberInput(step="any")

tests/test_db_fields.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,21 +154,18 @@ def test__ensure_callable_or_list__raise_error_if_argument_not_callable_and_not_
154154
db_fields.ComplexDateTimeField,
155155
db_fields.DateField,
156156
db_fields.DateTimeField,
157-
db_fields.DecimalField,
158157
db_fields.DictField,
159158
db_fields.DynamicField,
160159
db_fields.EmbeddedDocumentField,
161160
db_fields.EmbeddedDocumentListField,
162161
db_fields.EnumField,
163162
db_fields.FileField,
164-
db_fields.FloatField,
165163
db_fields.GenericEmbeddedDocumentField,
166164
db_fields.GenericLazyReferenceField,
167165
db_fields.GenericReferenceField,
168166
db_fields.GeoJsonBaseField,
169167
db_fields.GeoPointField,
170168
db_fields.ImageField,
171-
db_fields.IntField,
172169
db_fields.LazyReferenceField,
173170
db_fields.LineStringField,
174171
db_fields.ListField,

0 commit comments

Comments
 (0)