Repository: wtforms/wtforms (python)

Issue:
Breaking Change between 2.3 and 3.0 on SelectField(validate_choice=False)
### Actual Behavior

```python
>>> import wtforms
>>> class F(wtforms.Form):
...     foo = wtforms.SelectField(validate_choice=False)
>>> f = F()
>>> f.validate()
Traceback (most recent call last):
  File "scratch_27.py", line 7, in <module>
    f.validate()
  File "venv/lib/python3.8/site-packages/wtforms/form.py", line 329, in validate
    return super().validate(extra)
  File "venv/lib/python3.8/site-packages/wtforms/form.py", line 146, in validate
    if not field.validate(self, extra):
  File "venv/lib/python3.8/site-packages/wtforms/fields/core.py", line 231, in validate
    self.pre_validate(form)
  File "venv/lib/python3.8/site-packages/wtforms/fields/choices.py", line 136, in pre_validate
    raise TypeError(self.gettext("Choices cannot be None."))
TypeError: Choices cannot be None.
```

A quick fix consists in adding `choices=[]` to the SelectedField: 
```
>>> class F(wtforms.Form):
...     foo = wtforms.SelectField(choices=[], validate_choice=False)
```
### Expected Behavior

As mentioned in the title, I wasn't expecting a breaking change on that point, as it wasn't in the changelog. I wasn't even expecting a change at all: 

```python
>>> import wtforms
>>> class F(wtforms.Form):
...     foo = wtforms.SelectField(validate_choice=False)
>>> f = F()
>>> f.validate()  # no erros
```

### Environment

* Python version: 3.8
* wtforms version: 3.0.1



I am unsure if you'd rather add this to the documentation somewhere, or fix the compatibility break (which seems quite easy).
I've found a similar issue: https://github.com/wtforms/wtforms/issues/606; except that I'm expecting to be able to not provide any choices at all here. 

Pull request:
Select(Multiple)Field choice can be None if validate_choice is False
Allow `SelectField` and `SelectMultipleField` choice to be `None` if choice validation is disabled.
Fixes #735


Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index f030527..8adc247 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -6,6 +6,8 @@ Version 3.x.x
 Unreleased
 
 - Display :class:`~wtforms.Flags` values in their repr. :pr:`808`
+- :class:`~fields.SelectField` and :class:`~fields.SelectMultipleField`
+  ``choices`` can be `None` if `validate_choice` is `False` :pr:`809`
 
 Version 3.1.0
 -------------
diff --git a/src/wtforms/fields/choices.py b/src/wtforms/fields/choices.py
index bab248d..f3db8b3 100644
--- a/src/wtforms/fields/choices.py
+++ b/src/wtforms/fields/choices.py
@@ -135,12 +135,12 @@ class SelectField(SelectFieldBase):
             raise ValueError(self.gettext("Invalid Choice: could not coerce.")) from exc
 
     def pre_validate(self, form):
-        if self.choices is None:
-            raise TypeError(self.gettext("Choices cannot be None."))
-
         if not self.validate_choice:
             return
 
+        if self.choices is None:
+            raise TypeError(self.gettext("Choices cannot be None."))
+
         for _, _, match, _ in self.iter_choices():
             if match:
                 break
@@ -188,12 +188,12 @@ class SelectMultipleField(SelectField):
             ) from exc
 
     def pre_validate(self, form):
-        if self.choices is None:
-            raise TypeError(self.gettext("Choices cannot be None."))
-
         if not self.validate_choice or not self.data:
             return
 
+        if self.choices is None:
+            raise TypeError(self.gettext("Choices cannot be None."))
+
         acceptable = {c[0] for c in self.iter_choices()}
         if any(d not in acceptable for d in self.data):
             unacceptable = [str(d) for d in set(self.data) - acceptable]


Test patch:
diff --git a/tests/fields/test_select.py b/tests/fields/test_select.py
index 82db6f0..aa5874a 100644
--- a/tests/fields/test_select.py
+++ b/tests/fields/test_select.py
@@ -132,6 +132,12 @@ def test_dont_validate_choices():
     assert len(form.a.errors) == 0
 
 
+def test_choices_can_be_none_when_choice_validation_is_disabled():
+    F = make_form(a=SelectField(validate_choice=False))
+    form = F(DummyPostData(a="b"))
+    assert form.validate()
+
+
 def test_choice_shortcut():
     F = make_form(a=SelectField(choices=["foo", "bar"], validate_choice=False))
     form = F(a="bar")
diff --git a/tests/fields/test_selectmultiple.py b/tests/fields/test_selectmultiple.py
index 426c36b..5fd2a99 100644
--- a/tests/fields/test_selectmultiple.py
+++ b/tests/fields/test_selectmultiple.py
@@ -107,6 +107,12 @@ def test_dont_validate_choices():
     assert len(form.a.errors) == 0
 
 
+def test_choices_can_be_none_when_choice_validation_is_disabled():
+    F = make_form(a=SelectMultipleField(validate_choice=False))
+    form = F(DummyPostData(a="b"))
+    assert form.validate()
+
+
 def test_requried_flag():
     F = make_form(
         c=SelectMultipleField(