Fix reverse relation support for ___ filter operator

fix_request_path_info
Diederik van der Boor 2015-12-29 14:42:24 +01:00
parent 137139f2bb
commit b0657ef9c7
3 changed files with 40 additions and 6 deletions

View File

@ -1,6 +1,13 @@
Changelog Changelog
========== ==========
Version 0.8.1 (2015-12-29)
--------------------------
* Fixed support for reverse relations for ``relname___field`` when the field starts with an ``_`` character.
Otherwise, the query will be interpreted as subclass lookup (``ClassName___field``).
Version 0.8 (2015-12-28) Version 0.8 (2015-12-28)
------------------------ ------------------------

View File

@ -4,11 +4,25 @@
""" """
from __future__ import absolute_import from __future__ import absolute_import
import django
from django.db import models from django.db import models
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db.models import Q, FieldDoesNotExist from django.db.models import Q, FieldDoesNotExist
from django.db.models.fields.related import RelatedField # Django 1.8 from django.db.models.fields.related import RelatedField
if django.VERSION < (1, 6):
# There was no common base class in Django 1.5, mention all variants here.
from django.db.models.fields.related import RelatedObject, ManyToOneRel, ManyToManyRel
REL_FIELD_CLASSES = (RelatedField, RelatedObject, ManyToOneRel, ManyToManyRel) # Leaving GenericRel out.
elif django.VERSION < (1, 8):
# As of Django 1.6 there is a ForeignObjectRel.
from django.db.models.fields.related import ForeignObjectRel, RelatedObject
REL_FIELD_CLASSES = (RelatedField, ForeignObjectRel, RelatedObject)
else:
# As of Django 1.8 the base class serves everything. RelatedObject is gone.
from django.db.models.fields.related import ForeignObjectRel
REL_FIELD_CLASSES = (RelatedField, ForeignObjectRel)
from functools import reduce from functools import reduce
@ -154,9 +168,13 @@ def translate_polymorphic_field_path(queryset_model, field_path):
# Test whether it's actually a regular relation__ _fieldname (the field starting with an _) # Test whether it's actually a regular relation__ _fieldname (the field starting with an _)
# so no tripple ClassName___field was intended. # so no tripple ClassName___field was intended.
try: try:
# rel = (field_object, model, direct, m2m) if django.VERSION >= (1, 8):
# This also retreives M2M relations now (including reverse foreign key relations)
field = queryset_model._meta.get_field(classname) field = queryset_model._meta.get_field(classname)
if isinstance(field, RelatedField): else:
field = queryset_model._meta.get_field_by_name(classname)[0]
if isinstance(field, REL_FIELD_CLASSES):
# Can also test whether the field exists in the related object to avoid ambiguity between # Can also test whether the field exists in the related object to avoid ambiguity between
# class names and field names, but that never happens when your class names are in CamelCase. # class names and field names, but that never happens when your class names are in CamelCase.
return field_path # No exception raised, field does exist. return field_path # No exception raised, field does exist.

View File

@ -159,7 +159,8 @@ class ModelUnderRelParent(PolymorphicModel):
class ModelUnderRelChild(PolymorphicModel): class ModelUnderRelChild(PolymorphicModel):
parent = models.ForeignKey(ModelUnderRelParent) parent = models.ForeignKey(ModelUnderRelParent, related_name='children')
_private2 = models.CharField(max_length=10)
class MyManagerQuerySet(PolymorphicQuerySet): class MyManagerQuerySet(PolymorphicQuerySet):
@ -734,12 +735,20 @@ class PolymorphicTests(TestCase):
def test_polymorphic___filter_field(self): def test_polymorphic___filter_field(self):
p = ModelUnderRelParent.objects.create(_private=True, field1='AA') p = ModelUnderRelParent.objects.create(_private=True, field1='AA')
ModelUnderRelChild.objects.create(parent=p) ModelUnderRelChild.objects.create(parent=p, _private2=True)
# The "___" filter should also parse to "parent" -> "_private" as fallback. # The "___" filter should also parse to "parent" -> "_private" as fallback.
objects = ModelUnderRelChild.objects.filter(parent___private=True) objects = ModelUnderRelChild.objects.filter(parent___private=True)
self.assertEqual(len(objects), 1) self.assertEqual(len(objects), 1)
def test_polymorphic___filter_reverse_field(self):
p = ModelUnderRelParent.objects.create(_private=True, field1='BB')
ModelUnderRelChild.objects.create(parent=p, _private2=True)
# Also test for reverse relations
objects = ModelUnderRelParent.objects.filter(children___private2=True)
self.assertEqual(len(objects), 1)
def test_delete(self): def test_delete(self):
self.create_model2abcd() self.create_model2abcd()