-
-
Notifications
You must be signed in to change notification settings - Fork 452
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
Fix crash when constructing an abstract model with recursive relation #1393
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please add a simple test case for that in https://github.com/typeddjango/django-stubs/blob/master/tests/typecheck/models/test_related_fields.yml
Other than that I am fine with the change. Thank you!
Noticed some tests are failing. Investigating |
Yes, sure! |
Hi! Are you planning to continue this work? |
Thanks for the effort so far. If I understand correctly, this PR intended to add support for If you don't have time to finish this, hopefully someone else can pick it up and finalize it. Maybe @flaeppe is interested, no pressure though. :) It would be appreciated if you briefly describe the current state of the PR. |
TODO: add a test that fails without the fix and passes with it and does not break the whole test suite. Got busy at work so had to pause this work. |
@@ -195,6 +195,9 @@ def get_field_set_type_from_model_type_info(info: Optional[TypeInfo], field_name | |||
for field in model_cls._meta.get_fields(): | |||
if isinstance(field, Field): | |||
field_name = field.attname | |||
# Can not determine target_field for recursive relationship when model is abstract | |||
if field.related_model == "self" and model_cls._meta.abstract: | |||
continue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the main portion of the fix. The rest was trying to get typing to work now that model_cls can be a string.
@@ -7,6 +7,7 @@ | |||
reveal_type(Model2().test) # N: Revealed type is "app3.models.Model3_RelatedManager" | |||
reveal_type(Model1().test2) # N: Revealed type is "app3.models.Model4_RelatedManager" | |||
reveal_type(Model2().test2) # N: Revealed type is "app3.models.Model4_RelatedManager" | |||
Model1() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not seem to fail without the fix so need a better test #1394
I rebased, figured out and added a breaking test in fb92aec. The rebase was a bit messy, gonna try to resolve it. I also realised that we the plugin could yield an error when trying to instantiate an abstract model. As Django raises a |
mypy_django_plugin/django/context.py
Outdated
@@ -362,7 +365,7 @@ def get_field_related_model_cls(self, field: Union["RelatedField[Any, Any]", For | |||
def _resolve_field_from_parts( | |||
self, field_parts: Iterable[str], model_cls: Type[Model] | |||
) -> Union["Field[Any, Any]", ForeignObjectRel]: | |||
currently_observed_model = model_cls | |||
currently_observed_model: type[Model] | Literal["self"] = model_cls |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not certain the added annotation here is correct, it only introduces a theoretical Literal["self"]
while call sites of _resolve_field_from_parts
still passes model_cls: Type[Model]
.
So I'm not sure how currently_observed_model
would then end up being a string in this context..
If anything it should be checked whether any of the existing call sites could end up with Type[Model] | Literal["self"]
. Unless I'm missing something, none of the existing call sites for _resolve_field_from_parts
is part of the provided traceback, which makes me slightly more interested in not changing anything here until we've encountered something being wrong.
Instead of using `field.related_model`
@intgr In my eyes, this looks ready now as long as CI doesn't say otherwise. Sorry for being slow to look into this |
first = Concrete.objects.create(parent=None) | ||
Concrete.objects.create(parent=first) | ||
Recursive.objects.create(parent=None) | ||
Recursive(parent=Recursive(parent=None)) # E: Unexpected attribute "parent" for model "Recursive" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the curious one; it was initialisation of an abstract model(i.e. this line) that raised the error seen in the provided traceback
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, just one minor tweak request.
Thanks for picking up this fix. And don't hesitate to ping me again if another PR is sitting without reviews.
Co-authored-by: Marti Raudsepp <[email protected]>
I have made things!
We have abstract django model with a recursive relation. When we added code using one of its child django db models mypy crashed with the following trace
After some investigation we tracked the issue to a field.targer_field call with a reflective field on an abstract django model for which django raises a ValueError https://github.com/django/django/blob/main/django/db/models/fields/related.py#L712
Noticed you are also skipping getting the type for the primary key when the django model is abstract so doing something similar for these fields.
Related issues
Did not find any already created