form definition dictionary keys must be the field names now; refactoring in the Form class

This commit is contained in:
Daniil Fajnberg 2021-12-31 01:41:14 +01:00
parent 7618f6650e
commit 314b287ac4

View File

@ -69,6 +69,29 @@ class FormField:
class Form:
@staticmethod
def fields_from_dict(definition: Dict[str, Optional[dict]]) -> Dict[str, FormField]:
"""
Takes a dictionary defining form fields and creates `FormFields` objects from it.
Every key in `definition` is interpreted as the field's name.
The corresponding value can be `None` or a dictionary that can be unpacked into the FormField constructor call
alongside the name.
The special key `alias` in a field's dictionary is also allowed. If it is present, the corresponding value
will be used as the key in the output dictionary; otherwise the field's name is used as the key.
The constructed `FormField` objects are the values in the output dictionary.
"""
field_dict = {}
for name, field_def in definition.items():
if field_def is None:
field_def = {}
if isinstance(field_def, dict):
alias = field_def.pop('alias', name)
field_dict[alias] = FormField(name, **field_def)
else:
raise TypeError("Field definitions must be either dictionaries or `None`")
return field_dict
def __init__(self, definition: Dict[str, Dict], full_payload: bool = True, url: str = None):
"""
Creates a form instance from a definition dictionary. Each element in the dictionary must define a field.
@ -85,7 +108,7 @@ class Form:
url (optional):
Can be set in advance to the url that requests using this form's payload should be made to.
"""
self.fields: Dict[str, FormField] = {alias: FormField(**field_def) for alias, field_def in definition.items()}
self.fields: Dict[str, FormField] = self.fields_from_dict(definition)
self.full_payload_always: bool = full_payload
self.url: Optional[str] = url
@ -107,8 +130,10 @@ class Form:
Args:
kwargs (optional):
Every key must correspond to a key in the internal dictionary of fields;
Every key must correspond to an alias or name of a field in the internal dictionary of fields;
otherwise that key-value-pair is ignored.
If both a field's alias and name are different and both are present as keys in `kwargs`,
the alias-key takes precedence.
Values will be passed into the payload (if they pass validation).
Select fields with predefined options will only allow one of the options to be passed.
If `None` is passed as a value, the corresponding field's default value will be used in the payload.
@ -117,15 +142,15 @@ class Form:
Validated name-value-mapping to be used for HTTP requests from the form's fields.
"""
payload = {}
for key, field in self.fields.items():
if key in kwargs.keys():
value = kwargs[key]
if value is None:
payload[field.name] = field.default
else:
payload[field.name] = field.clean(value)
for alias, field in self.fields.items():
if alias in kwargs.keys():
value = kwargs[alias]
payload[field.name] = field.default if value is None else field.clean(value)
elif alias != field.name and field.name in kwargs.keys():
value = kwargs[field.name]
payload[field.name] = field.default if value is None else field.clean(value)
elif field.required:
raise ValueError(f"`{key}` is a required field, but no argument was passed.")
raise ValueError(f"`{alias}` is a required field, but no argument was passed.")
elif self.full_payload_always:
payload[field.name] = field.default
return payload