Opened 2 hours ago
Last modified 95 minutes ago
#36607 new Uncategorized
Improve the documentation by mentioning that "filter(a,b)" may not be the same as "filter(a).filter(b)" on querysets.
Reported by: | Aaron | Owned by: | |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 5.2 |
Severity: | Normal | Keywords: | |
Cc: | Aaron, Egor R | Triage Stage: | Unreviewed |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Dear community,
I just got informed about the fact that "filter(a,b)" might not be the same as
"filter(a).filter(b)". The following example may be useful for illustrating
this. It uses some code that is inspired by our code and won't run without
context, but I hope it is illustrating enough.
The intend is to filter for MaintenanceOperations. Therefore, we create one:
op = MaintenanceOperationFactory()
These objects are children of the MaintenanceOperation:
maint_a = UnscheduledMaintenanceFactory(operation=op, execution_start="2030-03-10T12:00:00Z") maint_b = UnscheduledMaintenanceFactory(operation=op, execution_start="2030-05-10T12:00:00Z")
We construct a first queryset like this:
qs1 = MaintenanceOperation.objects.filter(maintenances__execution_start__gt="2030-04-10T12:00:00Z", maintenances__execution_start__lt="2030-04-20T12:00:00Z",)
This qs evaluates to: <MaintenanceOperationQuerySet []> (aka the empty list). This happens because
neither UnscheduledMaintenance meets both criteria. The underlying sql is this:
SELECT "maintenance_maintenanceoperation"."id" FROM "maintenance_maintenanceoperation" INNER JOIN "maintenance_maintenance" ON ("maintenance_maintenanceoperation"."id" = "maintenance_maintenance"."operation_id") WHERE ("maintenance_maintenanceoperation"."deleted_at" IS NULL AND "maintenance_maintenance"."execution_start" > 2030-04-10 12:00:00+00:00 AND "maintenance_maintenance"."execution_start" < 2030-04-20 12:00:00+00:00)
To demonstrate the second case, we build the following queryset:
qs2 = MaintenanceOperation.objects.filter( maintenances__execution_start__gt="2030-04-10T12:00:00Z",).filter( maintenances__execution_start__lt="2030-04-20T12:00:00Z",)
This qs evaluates to:<MaintenanceOperationQuerySet [<MaintenanceOperation: #1:
beauftragt>]>(This list contains the MaintenanceOperation we created at the start)
This is different from the first one, because the sql is done with another join:
SELECT "maintenance_maintenanceoperation"."id" FROM "maintenance_maintenanceoperation" INNER JOIN "maintenance_maintenance" ON ("maintenance_maintenanceoperation"."id" = "maintenance_maintenance"."operation_id") INNER JOIN "maintenance_maintenance" T3 ON ("maintenance_maintenanceoperation"."id" = T3."operation_id") WHERE ("maintenance_maintenanceoperation"."deleted_at" IS NULL AND "maintenance_maintenance"."execution_start" > 2030-04-10 12:00:00+00:00 AND T3."execution_start" < 2030-04-20 12:00:00+00:00)
I think this should be mentioned in the filter documentation, particularly in the
section on joining filters. If this is met with approval I am open to sketching a
suggestion.