Skip to content

Commit 3d746bd

Browse files
authored
ready for 5.1.2 (pallets-eco#773)
Add documentation on how to properly subclass a form when using multiple inheritance.
1 parent da2806a commit 3d746bd

File tree

5 files changed

+74
-5
lines changed

5 files changed

+74
-5
lines changed

CHANGES.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Here you can see the full list of changes between each Flask-Security release.
66
Version 5.1.2
77
-------------
88

9-
Released TBD
9+
Released March 12, 2023
1010

1111
Fixes
1212
+++++
@@ -15,6 +15,7 @@ Fixes
1515
- (:pr:`769`) Fix documentation for send_mail. (gg)
1616
- (:pr:`768`) Fix for latest mongoengine and mongomock.
1717
- (:pr:`766`) Fix inappropriate use of &thinsp& in French translations. (maxdup)
18+
- (:pr:`773`) Improve documentation around subclassing forms.
1819

1920
Version 5.1.1
2021
-------------

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
# built documents.
5858
#
5959
# The short X.Y version.
60-
version = "5.1.1"
60+
version = "5.1.2"
6161
# The full version, including alpha/beta/rc tags.
6262
release = version
6363

docs/customizing.rst

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,33 @@ be passed if the model looks like::
122122
various views to no longer function. Many fields have complex (and not
123123
publicly exposed) validators that have side effects.
124124

125+
.. warning::
126+
It is important to ALWAYS subclass the base Flask-Security form and not
127+
attempt to just redefine the class. This is due to the validation method
128+
of many of the forms performs critical additional validation AND will change
129+
or add values to the form as a side-effect. See below for how to do this.
130+
131+
If you need to override an existing field in a form (to override/add validators),
132+
and you want to define a re-usable validator - use multiple inheritance - be extremely
133+
careful about the order of the inherited classes::
134+
135+
from wtforms import PasswordField, ValidationError
136+
from wtforms.validators import DataRequired
137+
138+
def password_validator(form, field):
139+
if field.data.startswith("PASS"):
140+
raise ValidationError("Really - don't start a password with PASS")
141+
142+
class NewPasswordFormMixinEx:
143+
password = PasswordField("password",
144+
validators=[DataRequired(message="PASSWORD_NOT_PROVIDED"),
145+
password_validator])
146+
147+
class MyRegisterForm(NewPasswordFormMixinEx, ConfirmRegisterForm):
148+
pass
149+
150+
app.config["SECURITY_CONFIRM_REGISTER_FORM"] = MyRegisterForm
151+
125152
The following is a list of all the available form overrides:
126153

127154
* ``login_form``: Login form
@@ -536,8 +563,9 @@ The decision on whether to return JSON is based on:
536563
Redirects
537564
---------
538565
Flask-Security uses redirects frequently (when using forms), and most of the redirect
539-
destinations are configurable. When Flask-Security initiates a redirect it always (mostly) flashes a message
540-
that provides some context. In addition, Flask-Security - both in its views and default templates attempt to propagate
566+
destinations are configurable. When Flask-Security initiates a redirect it (almost) always flashes a message
567+
that provides some context for the user.
568+
In addition, Flask-Security - both in its views and default templates, attempts to propagate
541569
any `next` query param and in fact, an existing `?next=/xx` will override most of the configuration redirect URLs.
542570

543571
As a complex example consider an unauthenticated user accessing a `@auth_required` endpoint, and the user has

flask_security/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,4 @@
134134
)
135135
from .webauthn_util import WebauthnUtil
136136

137-
__version__ = "5.1.1"
137+
__version__ = "5.1.2"

tests/test_registerable.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,3 +842,43 @@ def on_user_registered(app, **kwargs):
842842
assert nr["existing_email"]
843843
assert nr["user"]
844844
assert nr["form_data"]["email"] == "dude@lp.com"
845+
846+
847+
def test_subclass(app, sqlalchemy_datastore):
848+
# Test/show how to use multiple inheritance to override individual form fields.
849+
from wtforms import PasswordField, ValidationError
850+
from wtforms.validators import DataRequired
851+
from flask_security.forms import get_form_field_label
852+
853+
def password_validator(form, field):
854+
if field.data.startswith("PASS"):
855+
raise ValidationError("Really - don't start a password with PASS")
856+
857+
class NewPasswordFormMixinEx:
858+
password = PasswordField(
859+
get_form_field_label("password"),
860+
validators=[
861+
DataRequired(message="PASSWORD_NOT_PROVIDED"),
862+
password_validator,
863+
],
864+
)
865+
866+
class MyRegisterForm(NewPasswordFormMixinEx, ConfirmRegisterForm):
867+
pass
868+
869+
app.config["SECURITY_CONFIRM_REGISTER_FORM"] = MyRegisterForm
870+
security = Security(datastore=sqlalchemy_datastore)
871+
security.init_app(app)
872+
873+
client = app.test_client()
874+
875+
data = dict(
876+
email="dude@lp.com",
877+
password="PASSmattmatt",
878+
password_confirm="PASSmattmatt",
879+
)
880+
response = client.post(
881+
"/register", json=data, headers={"Content-Type": "application/json"}
882+
)
883+
assert response.status_code == 400
884+
assert "Really - don't start" in response.json["response"]["errors"][0]

0 commit comments

Comments
 (0)