Moved the aync validation logic into a mixin class
Intended to be combined with Editor widgets Able to call async validation functions on focus leave.
This commit is contained in:
parent
df8b157ae8
commit
e6a6f7a92b
|
@ -61,18 +61,47 @@ class RealnameEditor(StringEditor, WantsToKnowFormField):
|
||||||
return super().valid_char(ch)
|
return super().valid_char(ch)
|
||||||
|
|
||||||
|
|
||||||
class UsernameEditor(StringEditor, WantsToKnowFormField):
|
class _AsyncValidatedMixin:
|
||||||
|
""" Provides Editor widgets with async validation capabilities """
|
||||||
|
def __init__(self):
|
||||||
|
self.validation_task = None
|
||||||
|
self.initial = None
|
||||||
|
self.validation_result = None
|
||||||
|
self._validate_async_inner = None
|
||||||
|
connect_signal(self, 'change', self._reset_validation)
|
||||||
|
|
||||||
|
def set_initial_state(self, initial):
|
||||||
|
self.initial = initial
|
||||||
|
self.validation_result = initial
|
||||||
|
|
||||||
|
def _reset_validation(self, _, __):
|
||||||
|
self.validation_result = self.initial
|
||||||
|
|
||||||
|
def set_validation_call(self, async_call):
|
||||||
|
self._validate_async_inner = async_call
|
||||||
|
|
||||||
|
def lost_focus(self):
|
||||||
|
if self.validation_task is not None:
|
||||||
|
self.validation_task.cancel()
|
||||||
|
|
||||||
|
self.validation_task = \
|
||||||
|
schedule_task(self._validate_async(self.value))
|
||||||
|
|
||||||
|
async def _validate_async(self, value):
|
||||||
|
# Retrigger field validation because it's not guaranteed that the async
|
||||||
|
# call result will be available when the form fields are validated.
|
||||||
|
if self._validate_async_inner is not None:
|
||||||
|
self.validation_result = await self._validate_async_inner(value)
|
||||||
|
self.bff.validate()
|
||||||
|
|
||||||
|
|
||||||
|
class UsernameEditor(StringEditor, _AsyncValidatedMixin, WantsToKnowFormField):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.on_lost_focus = None
|
|
||||||
self.valid_char_pat = r'[-a-z0-9_]'
|
self.valid_char_pat = r'[-a-z0-9_]'
|
||||||
self.error_invalid_char = _("The only characters permitted in this "
|
self.error_invalid_char = _("The only characters permitted in this "
|
||||||
"field are a-z, 0-9, _ and -")
|
"field are a-z, 0-9, _ and -")
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def lost_focus(self):
|
|
||||||
if self.on_lost_focus is not None:
|
|
||||||
self.on_lost_focus(self.value)
|
|
||||||
|
|
||||||
def valid_char(self, ch):
|
def valid_char(self, ch):
|
||||||
if len(ch) == 1 and not re.match(self.valid_char_pat, ch):
|
if len(ch) == 1 and not re.match(self.valid_char_pat, ch):
|
||||||
self.bff.in_error = True
|
self.bff.in_error = True
|
||||||
|
@ -91,13 +120,10 @@ class IdentityForm(Form):
|
||||||
|
|
||||||
def __init__(self, controller, initial):
|
def __init__(self, controller, initial):
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.validation_task = None
|
|
||||||
self.validation_result = UsernameValidation.OK
|
|
||||||
super().__init__(initial=initial)
|
super().__init__(initial=initial)
|
||||||
# Server validation is only applied on focus leave.
|
widget = self.username.widget
|
||||||
# While editting no previous server validaiton error should be shown.
|
widget.set_initial_state(UsernameValidation.OK)
|
||||||
connect_signal(self.username.widget, 'change', self._reset_validation)
|
widget.set_validation_call(self.controller.validate_username)
|
||||||
self.username.widget.on_lost_focus = self.on_username_edit_complete
|
|
||||||
|
|
||||||
realname = RealnameField(_("Your name:"))
|
realname = RealnameField(_("Your name:"))
|
||||||
hostname = UsernameField(
|
hostname = UsernameField(
|
||||||
|
@ -107,25 +133,6 @@ class IdentityForm(Form):
|
||||||
password = PasswordField(_("Choose a password:"))
|
password = PasswordField(_("Choose a password:"))
|
||||||
confirm_password = PasswordField(_("Confirm your password:"))
|
confirm_password = PasswordField(_("Confirm your password:"))
|
||||||
|
|
||||||
def _reset_validation(self, _, __):
|
|
||||||
self.validation_result = UsernameValidation.OK
|
|
||||||
|
|
||||||
def on_username_edit_complete(self, value):
|
|
||||||
if len(value) < 2:
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.validation_task is not None:
|
|
||||||
self.validation_task.cancel()
|
|
||||||
|
|
||||||
self.validation_task = \
|
|
||||||
schedule_task(self._validate_async(value))
|
|
||||||
|
|
||||||
async def _validate_async(self, value):
|
|
||||||
self.validation_result = await self.controller.validate_username(value)
|
|
||||||
# Retrigger field validation because it's not guaranteed that the async
|
|
||||||
# call result will be available when the form fields are validated.
|
|
||||||
self.username.validate()
|
|
||||||
|
|
||||||
def validate_realname(self):
|
def validate_realname(self):
|
||||||
if len(self.realname.value) > REALNAME_MAXLEN:
|
if len(self.realname.value) > REALNAME_MAXLEN:
|
||||||
return _(
|
return _(
|
||||||
|
@ -159,7 +166,7 @@ class IdentityForm(Form):
|
||||||
return _(
|
return _(
|
||||||
"Username must match USERNAME_REGEX: " + USERNAME_REGEX)
|
"Username must match USERNAME_REGEX: " + USERNAME_REGEX)
|
||||||
|
|
||||||
state = self.validation_result
|
state = self.username.widget.validation_result
|
||||||
if state == UsernameValidation.SYSTEM_RESERVED:
|
if state == UsernameValidation.SYSTEM_RESERVED:
|
||||||
return _(
|
return _(
|
||||||
'The username "{username}" is reserved for use by the system.'
|
'The username "{username}" is reserved for use by the system.'
|
||||||
|
|
Loading…
Reference in New Issue