def set_property_from_data( component_or_field: Union[UnicornView, UnicornField, Model], name: str, value: Any, ) -> None: """ Sets properties on the component based on passed-in data. """ try: if not hasattr(component_or_field, name): return except ValueError: # Treat ValueError the same as a missing field because trying to access a many-to-many # field before the model's pk will throw this exception return field = getattr(component_or_field, name) component_field_is_model_or_unicorn_field = _is_component_field_model_or_unicorn_field( component_or_field, name) # UnicornField and Models are always a dictionary (can be nested) if component_field_is_model_or_unicorn_field: # Re-get the field since it might have been set in `_is_component_field_model_or_unicorn_field` field = getattr(component_or_field, name) type_hints = get_type_hints(component_or_field) if isinstance(value, dict): for key in value.keys(): key_value = value[key] set_property_from_data(field, key, key_value) elif hasattr(field, "related_val"): # Use `related_val` to check for many-to-many field.set(value) else: type_hints = get_type_hints(component_or_field) type_hint = type_hints.get(name) if _is_queryset(field, type_hint, value): value = _create_queryset(field, type_hint, value) elif type_hint: if is_dataclass(type_hint): value = type_hint(**value) else: # Construct the specified type by passing the value in # Usually the value will be a string (because it is coming from JSON) # and basic types can be constructed by passing in a string, # i.e. int("1") or float("1.1") try: value = type_hint(value) except TypeError: # Ignore this exception because some type-hints can't be instantiated like this (e.g. `List[]`) pass if hasattr(component_or_field, "_set_property"): # Can assume that `component_or_field` is a component component_or_field._set_property(name, value) else: setattr(component_or_field, name, value)
def test_get_type_hints_missing_type_hints(): def test_func(input_str): return input_str expected = {} actual = get_type_hints(test_func) assert actual == expected
def test_get_type_hints(): def test_func(input_str: str): return input_str expected = {"input_str": str} actual = get_type_hints(test_func) assert actual == expected
def set_property_from_data( component_or_field: Union[UnicornView, UnicornField, Model], name: str, value: Any, ) -> None: """ Sets properties on the component based on passed-in data. """ if not hasattr(component_or_field, name): return if isinstance(component_or_field, Model): set_property_for_model(component_or_field, name,value = value) return field = getattr(component_or_field, name) component_field_is_model_or_unicorn_field = _is_component_field_model_or_unicorn_field( component_or_field, name ) # UnicornField and Models are always a dictionary (can be nested) if component_field_is_model_or_unicorn_field: # Re-get the field since it might have been set in `_is_component_field_model_or_unicorn_field` field = getattr(component_or_field, name) if isinstance(value, dict): for key in value.keys(): key_value = value[key] set_property_from_data(field, key, key_value) else: set_property_from_data(field, field.name, value) else: type_hints = get_type_hints(component_or_field) if name in type_hints: # Construct the specified type by passing the value in # Usually the value will be a string (because it is coming from JSON) # and basic types can be constructed by passing in a string, # i.e. int("1") or float("1.1") if is_dataclass(type_hints[name]): value = type_hints[name](**value) else: value = type_hints[name](value) if hasattr(component_or_field, "_set_property"): # Can assume that `component_or_field` is a component component_or_field._set_property(name, value) else: setattr(component_or_field, name, value)
def _is_component_field_model_or_unicorn_field( component_or_field: Union[UnicornView, UnicornField, Model], name: str, ) -> bool: """ Determines whether a component's field is a Django `Model` or `UnicornField` either by checking the field's instance or inspecting the type hints. One side-effect is that the field will be instantiated if it is currently `None` and the type hint is available. Args: component: `UnicornView` to check. name: Name of the field. Returns: Whether the field is a Django `Model` or `UnicornField`. """ field = getattr(component_or_field, name) if isinstance(field, UnicornField) or isinstance(field, Model): return True is_subclass_of_model = False is_subclass_of_unicorn_field = False component_type_hints = {} try: component_type_hints = get_type_hints(component_or_field) if name in component_type_hints: is_subclass_of_model = issubclass(component_type_hints[name], Model) if not is_subclass_of_model: is_subclass_of_unicorn_field = issubclass( component_type_hints[name], UnicornField) # Construct a new class if the field is None and there is a type hint available if field is None: if is_subclass_of_model or is_subclass_of_unicorn_field: field = component_type_hints[name]() setattr(component_or_field, name, field) except TypeError: pass return is_subclass_of_model or is_subclass_of_unicorn_field
def _call_method_name(component: UnicornView, method_name: str, args: List[Any], kwargs: Dict[str, Any]) -> Any: """ Calls the method name with parameters. Args: param component: Component to call method on. param method_name: Method name to call. param args: List of arguments for the method. param kwargs: Dictionary of kwargs for the method. """ if method_name is not None and hasattr(component, method_name): func = getattr(component, method_name) if len(args) == 1 or len(kwargs.keys()) == 1: arguments = get_method_arguments(func) type_hints = get_type_hints(func) for argument in arguments: if argument in type_hints: if issubclass(type_hints[argument], Model): DbModel = type_hints[argument] key = "pk" value = None if args: value = args.pop() elif kwargs: (key, value) = list(kwargs.items())[0] del kwargs[key] model = DbModel.objects.get(**{key: value}) args.append(model) if args and kwargs: return func(*args, **kwargs) elif args: return func(*args, **kwargs) elif kwargs: return func(**kwargs) else: return func()