Skip to content

Commit 0086ca3

Browse files
committed
Add default field options generation to WtfFieldMixin
1 parent bedecfe commit 0086ca3

File tree

2 files changed

+57
-12
lines changed

2 files changed

+57
-12
lines changed

flask_mongoengine/db_fields.py

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,13 @@
4343
]
4444
import decimal
4545
import warnings
46-
from typing import Callable, Dict, List, Optional, Type, Union
46+
from typing import Callable, List, Optional, Type, Union
4747

4848
from bson import ObjectId
4949
from mongoengine import fields
5050

51+
from flask_mongoengine.decorators import wtf_required
52+
5153
try:
5254
from wtforms import fields as wtf_fields
5355
from wtforms import validators as wtf_validators_
@@ -75,7 +77,7 @@ class WtfFieldMixin:
7577

7678
DEFAULT_WTF_FIELD = None
7779
DEFAULT_WTF_CHOICES_FIELD = wtf_fields.SelectField if wtf_fields else None
78-
DEFAULT_WTF_CHOICES_FIELD_COERCE = str
80+
DEFAULT_WTF_CHOICES_COERCE = str
7981

8082
def __init__(
8183
self,
@@ -85,7 +87,8 @@ def __init__(
8587
wtf_field_class: Optional[Type] = None,
8688
wtf_filters: Optional[Union[List, Callable]] = None,
8789
wtf_validators: Optional[Union[List, Callable]] = None,
88-
wtf_options: Optional[Dict] = None,
90+
wtf_choices_coerce: Optional[Callable] = None,
91+
wtf_options: Optional[dict] = None,
8992
**kwargs,
9093
):
9194
"""
@@ -98,6 +101,8 @@ def __init__(
98101
:attr:`DEFAULT_WTF_FIELD` and :attr:`DEFAULT_WTF_CHOICES_FIELD`
99102
:param wtf_filters: wtf form field filters.
100103
:param wtf_validators: wtf form field validators.
104+
:param wtf_choices_coerce: Callable function to replace
105+
:attr:`DEFAULT_WTF_CHOICES_COERCE` for choices fields.
101106
:param wtf_options: Dictionary with WTForm Field settings.
102107
Applied last, takes precedence over any generated field options.
103108
:param kwargs: keyword arguments silently bypassed to normal mongoengine fields
@@ -131,7 +136,7 @@ def __init__(
131136
wtf_filters or filters, "wtf_filters"
132137
)
133138
self.wtf_options = wtf_options
134-
139+
self.wtf_choices_coerce = wtf_choices_coerce or self.DEFAULT_WTF_CHOICES_COERCE
135140
# Some attributes that will be updated by parent methods
136141
self.required = False
137142
self.default = None
@@ -140,7 +145,6 @@ def __init__(
140145

141146
# Internals
142147
self._wtf_field_class = wtf_field_class
143-
self._wtf_options = {}
144148

145149
super().__init__(**kwargs)
146150

@@ -153,6 +157,47 @@ def wtf_field_class(self):
153157
return self.DEFAULT_WTF_CHOICES_FIELD
154158
return self.DEFAULT_WTF_FIELD
155159

160+
@property
161+
@wtf_required
162+
def wtf_generated_options(self) -> dict:
163+
"""
164+
WTForm Field options generated by class, not updated by user provided :attr:`wtf_options`.
165+
"""
166+
wtf_field_kwargs: dict = {
167+
"label": getattr(self, "verbose_name", self.name),
168+
"description": getattr(self, "help_text", None) or "",
169+
"default": self.default,
170+
# Create a copy of the lists with list() call, since we will be modifying it
171+
"validators": list(self.wtf_validators) or [],
172+
"filters": list(self.wtf_filters) or [],
173+
}
174+
175+
if self.required:
176+
wtf_field_kwargs["validators"].append(wtf_validators_.InputRequired())
177+
else:
178+
wtf_field_kwargs["validators"].append(wtf_validators_.Optional())
179+
180+
if self.choices:
181+
wtf_field_kwargs["choices"] = self.choices
182+
wtf_field_kwargs["coerce"] = self.wtf_choices_coerce
183+
184+
return wtf_field_kwargs
185+
186+
@property
187+
@wtf_required
188+
def wtf_field_options(self):
189+
"""
190+
Final WTForm Field options that will be applied as :attr:`wtf_field_class` kwargs.
191+
192+
It is not recommended to overwrite this property, for logic update overwrite
193+
:attr:`wtf_generated_options`
194+
"""
195+
wtf_field_kwargs = self.wtf_generated_options
196+
if self.wtf_options is not None:
197+
wtf_field_kwargs.update(self.wtf_options)
198+
199+
return wtf_field_kwargs
200+
156201
@staticmethod
157202
def _ensure_callable_or_list(argument, msg_flag: str) -> Optional[List]:
158203
"""
@@ -162,7 +207,7 @@ def _ensure_callable_or_list(argument, msg_flag: str) -> Optional[List]:
162207
:param msg_flag: Argument string name for error message.
163208
"""
164209
if argument is None:
165-
return None
210+
return []
166211

167212
if callable(argument):
168213
return [argument]
@@ -208,7 +253,7 @@ class BooleanField(WtfFieldMixin, fields.BooleanField):
208253
"""
209254

210255
DEFAULT_WTF_FIELD = wtf_fields.BooleanField if wtf_fields else None
211-
DEFAULT_WTF_CHOICES_FIELD_COERCE = bool
256+
DEFAULT_WTF_CHOICES_COERCE = bool
212257

213258
def to_wtf_field(self, model, field_kwargs):
214259
"""
@@ -300,7 +345,7 @@ class DecimalField(WtfFieldMixin, fields.DecimalField):
300345
"""
301346

302347
DEFAULT_WTF_FIELD = wtf_fields.DecimalField if wtf_fields else None
303-
DEFAULT_WTF_CHOICES_FIELD_COERCE = decimal.Decimal
348+
DEFAULT_WTF_CHOICES_COERCE = decimal.Decimal
304349

305350
def to_wtf_field(self, model, field_kwargs):
306351
"""
@@ -451,7 +496,7 @@ class FloatField(WtfFieldMixin, fields.FloatField):
451496
"""
452497

453498
DEFAULT_WTF_FIELD = wtf_fields.FloatField if wtf_fields else None
454-
DEFAULT_WTF_CHOICES_FIELD_COERCE = float
499+
DEFAULT_WTF_CHOICES_COERCE = float
455500

456501
def to_wtf_field(self, model, field_kwargs):
457502
"""
@@ -573,7 +618,7 @@ class IntField(WtfFieldMixin, fields.IntField):
573618
"""
574619

575620
DEFAULT_WTF_FIELD = wtf_fields.IntegerField if wtf_fields else None
576-
DEFAULT_WTF_CHOICES_FIELD_COERCE = int
621+
DEFAULT_WTF_CHOICES_COERCE = int
577622

578623
def to_wtf_field(self, model, field_kwargs):
579624
"""
@@ -730,7 +775,7 @@ class ObjectIdField(WtfFieldMixin, fields.ObjectIdField):
730775
All arguments should be passed as keyword arguments, to exclude unexpected behaviour.
731776
"""
732777

733-
DEFAULT_WTF_CHOICES_FIELD_COERCE = ObjectId
778+
DEFAULT_WTF_CHOICES_COERCE = ObjectId
734779

735780
def to_wtf_field(self, model, field_kwargs):
736781
"""

tests/test_db_fields.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def test__init__set_additional_instance_arguments(self, db, mocker: MockerFixtur
100100
def test__ensure_callable_or_list__return_none_if_argument_is_none(self):
101101
assert (
102102
db_fields.WtfFieldMixin._ensure_callable_or_list(argument=None, msg_flag="")
103-
is None
103+
== []
104104
)
105105

106106
def test__ensure_callable_or_list__return_callable_argument_as_list(self):

0 commit comments

Comments
 (0)