Opened 8 years ago

Closed 8 years ago

#26797 closed Bug (duplicate)

Reverse `related_name` of a proxy not accessible to inherited proxies in queryset

Reported by: Maxime Lorant Owned by: nobody
Component: Database layer (models, ORM) Version: 1.9
Severity: Normal Keywords: related_name reverse relation
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Maxime Lorant)

Taken the following example (not real world use case, but it is somewhat equivalent of something I encountered):

from django.db import models


class Animal(models.Model):
    name = models.CharField("animal name", max_length=255, blank=True, null=False)

class Mammal(Animal):
    class Meta:
        proxy = True

class Cat(Mammal):
    class Meta:
        proxy = True

class Dog(Mammal):
    class Meta:
        proxy = True

class TrueFriendship(models.Model):
    mammal = models.ForeignKey(Mammal, related_name="dog_friends")
    dog = models.ForeignKey(Dog, related_name="mammal_friends")

I want to get the number of friends of a queryset of cats. However, doing this in a shell results in an exception:

>>> from nature.models import *
>>> cat = Cat.objects.create(name="cat1")
>>> dog = Dog.objects.create(name="dog1")
>>> TrueFriendship.objects.create(cat=cat, dog=dog)
>>> Cat.objects.annotate(nb_dog_friends=models.Count('dog_friends'))
Traceback (most recent call last):
  File "/data/.virtualenvs/bugproxy/local/lib/python2.7/site-packages/django/db/models/manager.py", line 122, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
[...]
  File "/data/.virtualenvs/bugproxy/local/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1330, in names_to_path
    "Choices are: %s" % (name, ", ".join(available)))
FieldError: Cannot resolve keyword 'dog_friends' into field. Choices are: id, name

(Full traceback attached in a file) The same goes for "simple" query such as Cat.objects.filter(dog_friends__isnull=False). However, using the Mammal proxy, both queries work and the reverse relation is available on single Cat object:

>>> Mammal.objects.annotate(nb_dog_friends=models.Count('dog_friends')).first().nb_dog_friends
1
>>> Cat.objects.first().dog_friends.all()
[<TrueFriendship: TrueFriendship object>]

Something is inconsistent here: I would expect to have access to dog_friends in both cases (on a model instance and a queryset)

Attachments (1)

traceback.txt (2.0 KB ) - added by Maxime Lorant 8 years ago.

Download all attachments as: .zip

Change History (4)

by Maxime Lorant, 8 years ago

Attachment: traceback.txt added

comment:1 by Maxime Lorant, 8 years ago

Description: modified (diff)
Summary: Reverse `related_name` of a proxy not accessible to inherited proxiesReverse `related_name` of a proxy not accessible to inherited proxies in queryset

comment:2 by Maxime Lorant, 8 years ago

The bug seems to be only on the 1.9 branch (tried with 1.9.1, 1.9.6 & 1.9.7) in fact. I tried using Django==1.10b1 and the query works as expected:

>>> Cat.objects.filter(dog_friends__isnull=False)
<QuerySet [<Cat: Cat object>]>

Is this ticket still relevant? It is referenced in the 1.10 release notes:

Reverse foreign keys from proxy models are now propagated to their concrete class. The reverse relation attached by a ForeignKey pointing to a proxy model is now accessible as a descriptor on the proxied model class and may be referenced in queryset filtering.

I don't know if any backport is possible (looks like a new feature than a bug the way this note is written)

Last edited 8 years ago by Maxime Lorant (previous) (diff)

comment:3 by Simon Charette, 8 years ago

Resolution: duplicate
Status: newclosed

Hi Maxime,

Thanks for your report.

As you've noticed this is a duplicate of #18012 which won't be backported to 1.9 as it's not related to a security issue, a regression from 1.7, or a bug in a new feature introduced in 1.8. You can have a look at our backward compatibility policy for more details.

The release note was added under the Backwards incompatible changes in 1.10 instead of the What’s new in Django 1.10 where new feature are documented which makes it clear to me it's not a new feature but if you think the phrasing is inadequate please reopen with a suggested alternative.

Note: See TracTickets for help on using tickets.
Back to Top