Skip to content

Custom fields with overwritten passthrough constructors don't change types based on attributes #2043

New issue

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

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

Already on GitHub? # to your account

Open
md384 opened this issue Apr 5, 2024 · 1 comment · May be fixed by #2044
Open
Labels
bug Something isn't working

Comments

@md384
Copy link
Contributor

md384 commented Apr 5, 2024

Bug report

What's wrong

When creating a custom model field with a constructor that uses *args, **kwargs to pass arguments to the parent constructor, attributes that change the typing of the field are not considered.

Below is a simple example where my_custom_field has null=True kwarg so the type of the field on the model instance should be Union[builtins.int, None] but is incorrectly builtins.int.

-   case: test_custom_model_fields
    main: |
        from myapp.models import User
        user = User()
        reveal_type(user.id)  # N: Revealed type is "builtins.int"
        reveal_type(user.my_custom_field)  # N: Revealed type is "Union[builtins.int, None]"
    monkeypatch: true
    installed_apps:
        - myapp
    files:
        -   path: myapp/__init__.py
        -   path: myapp/models.py
            content: |
                from django.db import models
                from django.db.models import fields

                from typing import Any, TypeVar

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

                class MyIntegerField(fields.IntegerField[_ST, _GT]):
                    def __init__(self, *args: Any, **kwargs: Any) -> None:
                        super().__init__(*args, **kwargs)

                class User(models.Model):
                    id = models.AutoField(primary_key=True)
                    my_custom_field = MyIntegerField(null=True)

How is that should be

reveal_type(user.my_custom_field) # N: Revealed type is "Union[builtins.int, None]"

System information

  • OS:
  • python version: 3.11
  • django version: 4.2.5
  • mypy version: 1.7.1
  • django-stubs version: 4.2.7
  • django-stubs-ext version: 4.2.7
@md384 md384 added the bug Something isn't working label Apr 5, 2024
@flaeppe
Copy link
Member

flaeppe commented Apr 7, 2024

I get the provided test case to pass on master without any changes if I instead declare __init__ like:

                class MyIntegerField(fields.IntegerField[_ST, _GT]):
-                    def __init__(self, *args: Any, **kwargs: Any) -> None:
+                    def __init__(self, *args: Any, null: bool = False, **kwargs: Any) -> None:
                        super().__init__(*args, **kwargs)

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

Successfully merging a pull request may close this issue.

2 participants