Skip to content

Custom field typing #545

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
markedwards opened this issue Dec 18, 2020 · 11 comments · Fixed by #2578
Closed

Custom field typing #545

markedwards opened this issue Dec 18, 2020 · 11 comments · Fixed by #2578
Labels
bug Something isn't working

Comments

@markedwards
Copy link
Contributor

Bug report

I'm trying to implement generic typing on custom fields, and I'm not having much luck. Any idea if there is a way to make this work?

What's wrong

Note: The following examples require hacking django_stubs_ext.patch._need_generic to include django.db.models.Field, which is something I'll raise separately.

I'm able to specify static typing for a custom field, but I'm not able to specify generic typing. Example code:

class FooField(Field[List[float], List[float]]):
    ... code that implements field storage for a list of floats ...
    
T = TypeVar('T', bound=Union[float, int])

class BarField(Field[List[T], List[T]]):
    def __init__(self, base_type: Type[T], *args: Any, **kwargs: Any) -> None:
        super().__init__(*args, **kwargs)
        self._base_type = base_type

    ... code that implements field storage for a list of T ...

class MyModel(Model):
    foo = FooField()
    bar = BarField(float)

def get_foo_item(model: MyModel, i: int) -> float:
    return model.foo[i]  # No error

def get_bar_item(model: MyModel, i: int) -> float:
    return model.bar[i]  # mypy error: Returning Any from function declared to return "float"

How is that should be

Have I missed something in my implementation, or is this not supported?

System information

  • OS: Debian
  • python version: 3.8.6
  • django version: 3.1.4
  • mypy version: 0.790
  • django-stubs version: 0.1.0
@markedwards markedwards added the bug Something isn't working label Dec 18, 2020
@TylerYep
Copy link

I have the same issue. Does anyone know how to get this working?

@markedwards
Copy link
Contributor Author

markedwards commented Jul 26, 2022

This is still happening in the latest release. Is this not considered to be a bug?

@bryanhelmig
Copy link

Same question over here...

@provinzkraut
Copy link

I'm currently facing the same issue, which I believe to be related to what's described in #336.

Any insights or updates on this? @sobolevn maybe? :)
I've already looked into the code here and as far as I can tell, mypy should be able to pick up the bound types for the generics. If there are any pointers as to what might be needed to support this I'd be happy to contribute as well.

@Pastromhaug
Copy link

Any updates on getting type checking working on custom model fields? I'm currently trying to get this to work to no avail!

@sobolevn
Copy link
Member

This works for me:

from django.db import models

class Custom(models.Field[str, int]):
    ...

class MyModel(models.Model):
    custom = Custom()

def some(m: MyModel):
    reveal_type(m.custom)  # N: Revealed type is "builtins.int"
    m.custom = ['a']  # E: Incompatible types in assignment (expression has type "list[str]", variable has type "str")

@sobolevn
Copy link
Member

If you can reproduce the issue and fix it - please, send a PR! 🤝

@Pastromhaug
Copy link

I was able to get it working just now like this!

_ST = TypeVar("_ST", contravariant=True)
_GT = TypeVar("_GT", covariant=True)


class DateTimeUtcField(models.DateTimeField[_ST, _GT]):
    ...

I found a reference in this PR which I followed :#2043

Thank you! 🙏

@UnknownPlatypus
Copy link
Contributor

UnknownPlatypus commented Feb 26, 2025

Should we add this workaround to the FAQ in the readme ? (I can send a pr if needed)

@sobolevn
Copy link
Member

@UnknownPlatypus sure :)

@bwo
Copy link

bwo commented Apr 7, 2025

I have a custom field type that looks like this:


E = TypeVar('E', bound=Enum)

class EnumField(Field[E, E]):
    def __init__(self, *, enum_cls: type[E], **kwargs):
        self.enum_cls = enum_cls
    ...

And this works (revealed type is as expected) IF I define on models like so:

the_field = EnumField(enum_cls=MyEnum)

class MyModel(Model):
    my_field = the_field

It does not work (revealed type is Any) if I do this:

class MyModel(Model):
    my_field = EnumField(enum_cls=MyEnum)

No amount of futzing with multiple type parameters seems to fix this. Any ideas?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Development

Successfully merging a pull request may close this issue.

8 participants