Skip to content
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

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

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)

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.

2 participants