mirror of
https://github.com/django-polymorphic/django-polymorphic.git
synced 2026-01-15 04:18:41 +03:00
Support Q expressions that contain subquery expressions.
Co-authored-by: CelestialGuru <45701317+CelestialGuru@users.noreply.github.com> Co-authored-by: Brian Kohan <bckohan@gmail.com>
This commit is contained in:
parent
0d820d0641
commit
6372ce1a5b
|
|
@ -1,6 +1,10 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
v4.3.0 (202X-XX-XX)
|
||||
-------------------
|
||||
* Fixed `Support Q expressions that contain subquery expressions <https://github.com/jazzband/django-polymorphic/pull/572>`_
|
||||
|
||||
v4.2.0 (2025-12-04)
|
||||
-------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ def translate_polymorphic_Q_object(queryset_model, potential_q_object, using=DEF
|
|||
)
|
||||
if new_expr:
|
||||
node.children[i] = new_expr
|
||||
else:
|
||||
# this Q object child is another Q object, recursively process this as well
|
||||
elif isinstance(child, models.Q):
|
||||
# this Q object child is another Q object, recursively process
|
||||
tree_node_correct_field_specs(my_model, child)
|
||||
|
||||
if isinstance(potential_q_object, models.Q):
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 4.2.26 on 2025-12-03 23:29
|
||||
# Generated by Django 4.2.26 on 2025-12-04 12:57
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
|
@ -12,8 +12,8 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
('auth', '0012_alter_user_first_name_max_length'),
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
|
@ -1097,7 +1097,7 @@ class Migration(migrations.Migration):
|
|||
fields=[
|
||||
('inlinemodela_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='tests.inlinemodela')),
|
||||
('field2', models.CharField(max_length=30)),
|
||||
('plain_a', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='tests.plaina')),
|
||||
('plain_a', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='inline_bs', to='tests.plaina')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
|
|
|
|||
|
|
@ -452,7 +452,12 @@ class InlineModelB(InlineModelA):
|
|||
field2 = models.CharField(max_length=30)
|
||||
|
||||
plain_a = models.ForeignKey(
|
||||
PlainA, null=True, blank=True, default=None, on_delete=models.SET_NULL
|
||||
PlainA,
|
||||
null=True,
|
||||
blank=True,
|
||||
default=None,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="inline_bs",
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import uuid
|
|||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import models, connection
|
||||
from django.db.models import Case, Count, FilteredRelation, Q, Sum, When, F
|
||||
from django.db.models import Case, Count, FilteredRelation, Q, Sum, When, Exists, OuterRef
|
||||
from django.db.utils import IntegrityError, NotSupportedError
|
||||
from django.test import TransactionTestCase
|
||||
from django.test.utils import CaptureQueriesContext
|
||||
|
|
@ -25,6 +25,9 @@ from polymorphic.tests.models import (
|
|||
CustomPkInherit,
|
||||
Enhance_Base,
|
||||
Enhance_Inherit,
|
||||
InlineParent,
|
||||
InlineModelA,
|
||||
InlineModelB,
|
||||
InitTestModelSubclass,
|
||||
Model2A,
|
||||
Model2B,
|
||||
|
|
@ -1422,3 +1425,76 @@ class PolymorphicTests(TransactionTestCase):
|
|||
assert pur.color == "blue"
|
||||
# issues/615 fixes following line:
|
||||
assert pur.home == "Duckburg"
|
||||
|
||||
def test_subqueries(self):
|
||||
PlainA.objects.all().delete()
|
||||
InlineParent.objects.all().delete()
|
||||
InlineModelA.objects.all().delete()
|
||||
InlineModelB.objects.all().delete()
|
||||
|
||||
pa1 = PlainA.objects.create(field1="plain1")
|
||||
PlainA.objects.create(field1="plain2")
|
||||
|
||||
ip1 = InlineParent.objects.create(title="parent1")
|
||||
ip2 = InlineParent.objects.create(title="parent2")
|
||||
|
||||
ima1 = InlineModelA.objects.create(parent=ip1, field1="ima1")
|
||||
ima2 = InlineModelA.objects.create(parent=ip2, field1="ima2")
|
||||
imb1 = InlineModelB.objects.create(parent=ip1, field1="imab1", field2="imb1", plain_a=pa1)
|
||||
imb2 = InlineModelB.objects.create(parent=ip2, field1="imab2", field2="imb2")
|
||||
|
||||
results = InlineModelA.objects.filter(
|
||||
Exists(PlainA.objects.filter(inline_bs=OuterRef("pk")))
|
||||
| Exists(InlineParent.objects.filter(inline_children=OuterRef("pk")))
|
||||
)
|
||||
|
||||
assert ima1 in results
|
||||
assert ima2 in results
|
||||
assert imb1 in results
|
||||
assert imb2 in results
|
||||
|
||||
results = InlineModelA.objects.filter(
|
||||
Exists(PlainA.objects.filter(inline_bs=OuterRef("pk"), field1="plain1"))
|
||||
| Exists(InlineParent.objects.filter(inline_children=OuterRef("pk"), title="parent2"))
|
||||
)
|
||||
|
||||
assert ima1 not in results
|
||||
assert ima2 in results
|
||||
assert imb1 in results
|
||||
assert imb2 in results
|
||||
|
||||
results = InlineModelA.objects.filter(
|
||||
Exists(PlainA.objects.filter(inline_bs=OuterRef("pk")))
|
||||
)
|
||||
|
||||
assert ima1 not in results
|
||||
assert ima2 not in results
|
||||
assert imb1 in results
|
||||
assert imb2 not in results
|
||||
|
||||
results = InlineModelA.objects.filter(
|
||||
Exists(PlainA.objects.filter(inline_bs=OuterRef("pk"))), field1="imab1"
|
||||
)
|
||||
|
||||
assert ima1 not in results
|
||||
assert ima2 not in results
|
||||
assert imb1 in results
|
||||
assert imb2 not in results
|
||||
|
||||
results = InlineModelA.objects.filter(
|
||||
Exists(PlainA.objects.filter(inline_bs=OuterRef("pk"))), InlineModelB___field2="imb2"
|
||||
)
|
||||
|
||||
assert not results
|
||||
|
||||
results = InlineModelA.objects.filter(
|
||||
~Exists(PlainA.objects.filter(inline_bs=OuterRef("pk"))), InlineModelB___field2="imb2"
|
||||
)
|
||||
|
||||
assert len(results) == 1
|
||||
assert imb2 in results
|
||||
|
||||
PlainA.objects.all().delete()
|
||||
InlineParent.objects.all().delete()
|
||||
InlineModelA.objects.all().delete()
|
||||
InlineModelB.objects.all().delete()
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user