Loading django/db/models/fields/related.py +4 −3 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ from .related_descriptors import ( ) from .related_lookups import ( RelatedExact, RelatedGreaterThan, RelatedGreaterThanOrEqual, RelatedIn, RelatedLessThan, RelatedLessThanOrEqual, RelatedIsNull, RelatedLessThan, RelatedLessThanOrEqual, ) from .reverse_related import ( ForeignObjectRel, ManyToManyRel, ManyToOneRel, OneToOneRel, Loading Loading @@ -672,9 +672,10 @@ class ForeignObject(RelatedField): return RelatedLessThan elif lookup_name == 'lte': return RelatedLessThanOrEqual elif lookup_name != 'isnull': elif lookup_name == 'isnull': return RelatedIsNull else: raise TypeError('Related Field got invalid lookup: %s' % lookup_name) return super(ForeignObject, self).get_lookup(lookup_name) def get_transform(self, *args, **kwargs): raise NotImplementedError('Relational fields do not support transforms.') Loading django/db/models/fields/related_lookups.py +6 −1 Original line number Diff line number Diff line from django.db.models.lookups import ( Exact, GreaterThan, GreaterThanOrEqual, In, LessThan, LessThanOrEqual, Exact, GreaterThan, GreaterThanOrEqual, In, IsNull, LessThan, LessThanOrEqual, ) Loading Loading @@ -131,3 +132,7 @@ class RelatedGreaterThanOrEqual(RelatedLookupMixin, GreaterThanOrEqual): class RelatedLessThanOrEqual(RelatedLookupMixin, LessThanOrEqual): pass class RelatedIsNull(RelatedLookupMixin, IsNull): pass docs/releases/1.9.1.txt +3 −0 Original line number Diff line number Diff line Loading @@ -64,3 +64,6 @@ Bugfixes * Restored the functionality of the admin's ``list_editable`` add and change buttons (:ticket:`25903`). * Fixed ``isnull`` query lookup for ``ForeignObject`` (:ticket:`25972`). tests/foreign_object/models/person.py +7 −6 Original line number Diff line number Diff line Loading @@ -56,19 +56,19 @@ class Membership(models.Model): date_joined = models.DateTimeField(default=datetime.datetime.now) invite_reason = models.CharField(max_length=64, null=True) person_id = models.IntegerField() group_id = models.IntegerField() group_id = models.IntegerField(blank=True, null=True) # Relation Fields person = models.ForeignObject( Person, from_fields=['membership_country', 'person_id'], to_fields=['person_country_id', 'id'], from_fields=['person_id', 'membership_country'], to_fields=['id', 'person_country_id'], on_delete=models.CASCADE, ) group = models.ForeignObject( Group, from_fields=['membership_country', 'group_id'], to_fields=['group_country', 'id'], from_fields=['group_id', 'membership_country'], to_fields=['id', 'group_country'], on_delete=models.CASCADE, ) Loading @@ -76,7 +76,8 @@ class Membership(models.Model): ordering = ('date_joined', 'invite_reason') def __str__(self): return "%s is a member of %s" % (self.person.name, self.group.name) group_name = self.group.name if self.group_id else 'NULL' return "%s is a member of %s" % (self.person.name, group_name) class Friendship(models.Model): Loading tests/foreign_object/tests.py +12 −0 Original line number Diff line number Diff line Loading @@ -395,6 +395,18 @@ class MultiColumnFKTests(TestCase): objs = [Person(name="abcd_%s" % i, person_country=self.usa) for i in range(0, 5)] Person.objects.bulk_create(objs, 10) def test_isnull_lookup(self): Membership.objects.create(membership_country=self.usa, person=self.bob, group_id=None) Membership.objects.create(membership_country=self.usa, person=self.bob, group=self.cia) self.assertQuerysetEqual( Membership.objects.filter(group__isnull=True), ['<Membership: Bob is a member of NULL>'] ) self.assertQuerysetEqual( Membership.objects.filter(group__isnull=False), ['<Membership: Bob is a member of CIA>'] ) class TestModelCheckTests(SimpleTestCase): Loading Loading
django/db/models/fields/related.py +4 −3 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ from .related_descriptors import ( ) from .related_lookups import ( RelatedExact, RelatedGreaterThan, RelatedGreaterThanOrEqual, RelatedIn, RelatedLessThan, RelatedLessThanOrEqual, RelatedIsNull, RelatedLessThan, RelatedLessThanOrEqual, ) from .reverse_related import ( ForeignObjectRel, ManyToManyRel, ManyToOneRel, OneToOneRel, Loading Loading @@ -672,9 +672,10 @@ class ForeignObject(RelatedField): return RelatedLessThan elif lookup_name == 'lte': return RelatedLessThanOrEqual elif lookup_name != 'isnull': elif lookup_name == 'isnull': return RelatedIsNull else: raise TypeError('Related Field got invalid lookup: %s' % lookup_name) return super(ForeignObject, self).get_lookup(lookup_name) def get_transform(self, *args, **kwargs): raise NotImplementedError('Relational fields do not support transforms.') Loading
django/db/models/fields/related_lookups.py +6 −1 Original line number Diff line number Diff line from django.db.models.lookups import ( Exact, GreaterThan, GreaterThanOrEqual, In, LessThan, LessThanOrEqual, Exact, GreaterThan, GreaterThanOrEqual, In, IsNull, LessThan, LessThanOrEqual, ) Loading Loading @@ -131,3 +132,7 @@ class RelatedGreaterThanOrEqual(RelatedLookupMixin, GreaterThanOrEqual): class RelatedLessThanOrEqual(RelatedLookupMixin, LessThanOrEqual): pass class RelatedIsNull(RelatedLookupMixin, IsNull): pass
docs/releases/1.9.1.txt +3 −0 Original line number Diff line number Diff line Loading @@ -64,3 +64,6 @@ Bugfixes * Restored the functionality of the admin's ``list_editable`` add and change buttons (:ticket:`25903`). * Fixed ``isnull`` query lookup for ``ForeignObject`` (:ticket:`25972`).
tests/foreign_object/models/person.py +7 −6 Original line number Diff line number Diff line Loading @@ -56,19 +56,19 @@ class Membership(models.Model): date_joined = models.DateTimeField(default=datetime.datetime.now) invite_reason = models.CharField(max_length=64, null=True) person_id = models.IntegerField() group_id = models.IntegerField() group_id = models.IntegerField(blank=True, null=True) # Relation Fields person = models.ForeignObject( Person, from_fields=['membership_country', 'person_id'], to_fields=['person_country_id', 'id'], from_fields=['person_id', 'membership_country'], to_fields=['id', 'person_country_id'], on_delete=models.CASCADE, ) group = models.ForeignObject( Group, from_fields=['membership_country', 'group_id'], to_fields=['group_country', 'id'], from_fields=['group_id', 'membership_country'], to_fields=['id', 'group_country'], on_delete=models.CASCADE, ) Loading @@ -76,7 +76,8 @@ class Membership(models.Model): ordering = ('date_joined', 'invite_reason') def __str__(self): return "%s is a member of %s" % (self.person.name, self.group.name) group_name = self.group.name if self.group_id else 'NULL' return "%s is a member of %s" % (self.person.name, group_name) class Friendship(models.Model): Loading
tests/foreign_object/tests.py +12 −0 Original line number Diff line number Diff line Loading @@ -395,6 +395,18 @@ class MultiColumnFKTests(TestCase): objs = [Person(name="abcd_%s" % i, person_country=self.usa) for i in range(0, 5)] Person.objects.bulk_create(objs, 10) def test_isnull_lookup(self): Membership.objects.create(membership_country=self.usa, person=self.bob, group_id=None) Membership.objects.create(membership_country=self.usa, person=self.bob, group=self.cia) self.assertQuerysetEqual( Membership.objects.filter(group__isnull=True), ['<Membership: Bob is a member of NULL>'] ) self.assertQuerysetEqual( Membership.objects.filter(group__isnull=False), ['<Membership: Bob is a member of CIA>'] ) class TestModelCheckTests(SimpleTestCase): Loading