Skip to content

[Bug] model modifier does not use base class type hints to cast value to correct type #741

@pa-hqt

Description

@pa-hqt

Bug Description

If I inherit from a base class the method typer.get_type_hints does not include the type hints of the properties that are defined in the base class. As a result, the properties are not casted to correct data type when used with unicorn:model="property" in template.
In my case it was an HTML select, where the option values are numerical IDs that are always transmitted as strings and should be converted in the unicorn component.

Suggested fix: change line type_hints = typing_get_type_hints(obj) in django_unicorn.typer.get_type_hints to

if hasattr(obj, "__class__"):
    # Should be called with class object (instead of instance) to get type hints of parent classes. From docs:
    # "If obj is a class C, the function returns a dictionary that merges annotations from C’s base classes with those on C directly.
    # This is done by traversing C.__mro__ and iteratively combining __annotations__ dictionaries." (https://docs.python.org/3/library/typing.html#typing.get_type_hints)
    type_hints = typing_get_type_hints(obj.__class__)
else:
    type_hints = typing_get_type_hints(obj)

External references:
https://discuss.python.org/t/behavior-of-get-type-hints-differs-on-classes-and-instances/70491
https://docs.python.org/3/library/typing.html#typing.get_type_hints

Expected behaviour

If I have defined data type hints in a base class of my current view, these type hints should be considered when the property, that is defined in the base class, is set in child view via the unicorn:model mechanism. (at least for primitive data types)

Screenshots / Screenrecords

N/A

Steps to reproduce

from django_unicorn.components import UnicornView
from django_unicorn.typer import get_type_hints


class BaseView(UnicornView):
    base_property: int = 0


class ChildView(BaseView):
    child_property: int = 0


# Tests that fail
def test_set_property_value_with_casting():
    view = ChildView(component_name="test", component_id="test_get_type_hints_base_class")

    view._set_property("child_property", "1")  # value is transmitted as string, but should be an int
    assert view.child_property == 1, "child_property should be of type int and have value 1"  # works as expected

    view._set_property("base_property", "1")  # value is transmitted as string, but should be an int
    assert view.base_property == 1, "base_property should be of type int and have value 1"  # fails, because no type hint is returned for the base class properties


def test_get_type_hints_base_class():
    expected = {"base_property": int, "child_property": str}
    actual = get_type_hints(ChildView(component_name="test", component_id="test_get_type_hints_base_class"))
    assert actual == expected

What browsers are you seeing the problem on?

No response

👀 Have you checked for similar open issues?

  • I checked and didn't find similar issue

Code of Conduct

  • I agree to follow this project's Code of Conduct

Are you willing to work on this issue ?

No, someone else can work on it

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions