django-typed-models icon indicating copy to clipboard operation
django-typed-models copied to clipboard

Demonstrate bug where related_name is not working properly when using…

Open mcosti opened this issue 2 years ago • 3 comments

 File "/Users/mcosti/dev/django-typed-models/env/bin/activate/lib/python3.9/site-packages/django/apps/registry.py", line 433, in do_pending_operations
    function(model)
  File "/Users/mcosti/dev/django-typed-models/env/bin/activate/lib/python3.9/site-packages/django/apps/registry.py", line 411, in apply_next_model
    self.lazy_model_operation(next_function, *more_models)
  File "/Users/mcosti/dev/django-typed-models/env/bin/activate/lib/python3.9/site-packages/django/apps/registry.py", line 397, in lazy_model_operation
    function()
  File "/Users/mcosti/dev/django-typed-models/env/bin/activate/lib/python3.9/site-packages/django/db/models/fields/related.py", line 373, in resolve_related_class
    field.do_related_class(related, model)
  File "/Users/mcosti/dev/django-typed-models/typedmodels/models.py", line 108, in do_related_class
    old_do_related_class(other, cls)
  File "/Users/mcosti/dev/django-typed-models/env/bin/activate/lib/python3.9/site-packages/django/db/models/fields/related.py", line 448, in do_related_class
    self.set_attributes_from_rel()
  File "/Users/mcosti/dev/django-typed-models/env/bin/activate/lib/python3.9/site-packages/django/db/models/fields/related.py", line 445, in set_attributes_from_rel
    self.remote_field.set_field_name()
  File "/Users/mcosti/dev/django-typed-models/env/bin/activate/lib/python3.9/site-packages/django/db/models/fields/reverse_related.py", line 306, in set_field_name
    self.field_name = self.field_name or self.model._meta.pk.name
AttributeError: 'str' object has no attribute '_meta'

mcosti avatar Jun 19 '23 15:06 mcosti

The culprit lives somewhere here

                if isinstance(field, models.fields.related.RelatedField):
                    # Monkey patching field instance to make do_related_class use created class instead of base_class.
                    # Actually that class doesn't exist yet, so we just monkey patch base_class for a while,
                    # changing _meta.model_name, so accessor names are generated properly.
                    # We'll do more stuff when the class is created.
                    old_do_related_class = field.do_related_class

                    def do_related_class(self, other, cls):
                        base_class_name = base_class.__name__
                        cls._meta.model_name = classname.lower()
                        old_do_related_class(other, cls)
                        cls._meta.model_name = base_class_name.lower()

                    field.do_related_class = types.MethodType(do_related_class, field)
                if isinstance(field, models.fields.related.RelatedField):
                    remote_field = field.remote_field
                    if (
                        isinstance(remote_field.model, TypedModel)
                        and remote_field.model.base_class
                    ):
                        remote_field.limit_choices_to[
                            "type__in"
                        ] = remote_field.model._typedmodels_subtypes

mcosti avatar Jun 19 '23 15:06 mcosti

This is fixed now. I added a print in RelatedFIeld.resolve_related_class

Without my changes:

<class 'testapp.models.UniqueIdentifier'> <class 'django.contrib.contenttypes.models.ContentType'> testapp.UniqueIdentifier.content_type
<class 'testapp.models.Animal'> <class 'testapp.models.UniqueIdentifier'> testapp.Animal.unique_identifiers
<class 'testapp.models.Canine'> <class 'testapp.models.UniqueIdentifier'> testapp.Canine.unique_identifiers
<class 'testapp.models.Feline'> <class 'testapp.models.UniqueIdentifier'> testapp.Feline.unique_identifiers
<class 'testapp.models.BigCat'> <class 'testapp.models.UniqueIdentifier'> testapp.BigCat.unique_identifiers
<class 'testapp.models.Animal'> <class 'testapp.models.Canine'> testapp.Animal.canines_eaten
<class 'testapp.models.Animal_canines_eaten'> <class 'testapp.models.Animal'> testapp.Animal_canines_eaten.animal
<class 'testapp.models.Animal_canines_eaten'> <class 'testapp.models.Canine'> testapp.Animal_canines_eaten.canine
<class 'testapp.models.AngryBigCat'> <class 'testapp.models.UniqueIdentifier'> testapp.AngryBigCat.unique_identifiers
<class 'testapp.models.Parrot'> <class 'testapp.models.UniqueIdentifier'> testapp.Parrot.unique_identifiers
<class 'testapp.models.Parent'> <class 'testapp.models.Parent'> testapp.Parent.b
<class 'second_testapp.models.BaseReview'> <class 'testapp.models.Product'> second_testapp.BaseReview.product

With the changes (respecting the same way Django deals with these things):

<class '__fake__.Animal_canines_eaten'> <class '__fake__.Animal'> testapp.Animal_canines_eaten.animal
<class '__fake__.Animal_canines_eaten'> <class '__fake__.Canine'> testapp.Animal_canines_eaten.canine
<class '__fake__.Animal'> <class '__fake__.Canine'> testapp.Animal.canines_eaten
<class '__fake__.Parent'> <class '__fake__.Parent'> testapp.Parent.b
<class '__fake__.BaseReview'> <class '__fake__.Product'> second_testapp.BaseReview.product
<class '__fake__.BaseReview'> <class '__fake__.Order'> second_testapp.BaseReview.order
<class '__fake__.Order'> <class '__fake__.Product'> testapp.Order.product

@craigds if you could please have a look that would be amazing. Thank you

mcosti avatar Jun 20 '23 18:06 mcosti

Hey @craigds . Any updates? This no longer impacts me as I've removed the depdency, but I think it's still a good QoL improvement

mcosti avatar Aug 31 '23 18:08 mcosti