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
==========
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)
------------------------

View File

@ -4,11 +4,25 @@
"""
from __future__ import absolute_import
import django
from django.db import models
from django.contrib.contenttypes.models import ContentType
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
@ -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 _)
# so no tripple ClassName___field was intended.
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)
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
# 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.

View File

@ -159,7 +159,8 @@ class ModelUnderRelParent(PolymorphicModel):
class ModelUnderRelChild(PolymorphicModel):
parent = models.ForeignKey(ModelUnderRelParent)
parent = models.ForeignKey(ModelUnderRelParent, related_name='children')
_private2 = models.CharField(max_length=10)
class MyManagerQuerySet(PolymorphicQuerySet):
@ -734,12 +735,20 @@ class PolymorphicTests(TestCase):
def test_polymorphic___filter_field(self):
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.
objects = ModelUnderRelChild.objects.filter(parent___private=True)
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):
self.create_model2abcd()