Skip to content

Commit

Permalink
Make AddRelatedManagers look for "objects" on parent model
Browse files Browse the repository at this point in the history
Previously, AddRelatedManagers would fail if a related model had inherited
its `objects` field from a parent class. This would result in missing
relation attributes. This is fixed by using `get()` instead of `names`;
the former searches the MRO for the symbol, whereas the latter only looks
for symbols declared directly on the class.
  • Loading branch information
RJPercival committed Oct 18, 2021
1 parent fb12560 commit e28a390
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 2 deletions.
5 changes: 3 additions & 2 deletions mypy_django_plugin/transformers/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,8 @@ def run_with_model_cls(self, model_cls: Type[Model]) -> None:
related_manager_info = self.lookup_typeinfo_or_incomplete_defn_error(
fullnames.RELATED_MANAGER_CLASS
) # noqa: E501
if "objects" not in related_model_info.names:
objects = related_model_info.get("objects")
if not objects:
raise helpers.IncompleteDefnException()
except helpers.IncompleteDefnException as exc:
if not self.api.final_iteration:
Expand All @@ -283,7 +284,7 @@ def run_with_model_cls(self, model_cls: Type[Model]) -> None:

# create new RelatedManager subclass
parametrized_related_manager_type = Instance(related_manager_info, [Instance(related_model_info, [])])
default_manager_type = related_model_info.names["objects"].type
default_manager_type = objects.type
if default_manager_type is None:
default_manager_type = self.try_generate_related_manager(related_model_cls, related_model_info)
if (
Expand Down
147 changes: 147 additions & 0 deletions tests/typecheck/fields/test_related.yml
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,153 @@
Transaction().test()
- case: reverse_foreign_key_relationship_for_abstract_models
main: |
from myapp.models import Transaction
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
class Transaction(models.Model):
pass
class AbstractLog(models.Model):
class Meta:
abstract=True
class TransactionLog(AbstractLog):
transaction = models.ForeignKey(Transaction, on_delete=models.CASCADE, related_name="logs")
Transaction().logs
- case: reverse_foreign_key_relationship_for_abstract_models_with_custom_queryset_declared_inline
main: |
from myapp.models import Transaction
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
class AbstractLogQuerySet(models.QuerySet):
pass
class Transaction(models.Model):
pass
class AbstractLog(models.Model):
objects = models.Manager.from_queryset(AbstractLogQuerySet)()
class Meta:
abstract=True
class TransactionLog(AbstractLog):
transaction = models.ForeignKey(Transaction, on_delete=models.CASCADE, related_name="logs")
Transaction().logs
- case: reverse_foreign_key_relationship_for_abstract_models_with_custom_queryset_declared_outside_class
main: |
from myapp.models import Transaction
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
class AbstractLogQuerySet(models.QuerySet):
pass
class Transaction(models.Model):
pass
AbstractLogManager = models.Manager.from_queryset(AbstractLogQuerySet)
class AbstractLog(models.Model):
objects = AbstractLogManager()
class Meta:
abstract=True
class TransactionLog(AbstractLog):
transaction = models.ForeignKey(Transaction, on_delete=models.CASCADE, related_name="logs")
Transaction().logs
- case: reverse_foreign_key_relationship_for_abstract_models_with_custom_manager
main: |
from myapp.models import Transaction
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
class AbstractLogManager(models.Manager):
pass
class Transaction(models.Model):
pass
class AbstractLog(models.Model):
objects = AbstractLogManager()
class Meta:
abstract=True
class TransactionLog(AbstractLog):
transaction = models.ForeignKey(Transaction, on_delete=models.CASCADE, related_name="logs")
Transaction().logs
- case: reverse_foreign_key_relationship_for_abstract_models_with_custom_manager_and_queryset_declared_inline
main: |
from myapp.models import Transaction
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
class AbstractLogQuerySet(models.QuerySet):
pass
class AbstractLogManager(models.Manager):
pass
class Transaction(models.Model):
pass
class AbstractLog(models.Model):
objects = AbstractLogManager.from_queryset(AbstractLogQuerySet)()
class Meta:
abstract=True
class TransactionLog(AbstractLog):
transaction = models.ForeignKey(Transaction, on_delete=models.CASCADE, related_name="logs")
Transaction().logs
- case: reverse_foreign_key_relationship_for_abstract_models_with_custom_manager_and_queryset_declared_outside_class
main: |
from myapp.models import Transaction
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
class AbstractLogQuerySet(models.QuerySet):
pass
class AbstractLogManager(models.Manager):
pass
class Transaction(models.Model):
pass
AbstractLogQSManager = AbstractLogManager.from_queryset(AbstractLogQuerySet)
class AbstractLog(models.Model):
objects = AbstractLogQSManager()
class Meta:
abstract=True
class TransactionLog(AbstractLog):
transaction = models.ForeignKey(Transaction, on_delete=models.CASCADE, related_name="logs")
Transaction().logs
- case: resolve_primary_keys_for_foreign_keys_with_abstract_self_model
main: |
from myapp.models import User
Expand Down

0 comments on commit e28a390

Please sign in to comment.