From 314b287ac480678f6552787301007cd372c73026 Mon Sep 17 00:00:00 2001 From: Daniil Fajnberg Date: Fri, 31 Dec 2021 01:41:14 +0100 Subject: [PATCH] form definition dictionary keys must be the field names now; refactoring in the `Form` class --- src/yamlhttpforms/form.py | 45 ++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/src/yamlhttpforms/form.py b/src/yamlhttpforms/form.py index eb34500..b44d18a 100644 --- a/src/yamlhttpforms/form.py +++ b/src/yamlhttpforms/form.py @@ -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