#28657 closed Bug (invalid)
Strangely odd behaviour when ordering QuerySet by BooleanField.
Reported by: | Leonardo Arroyo | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 1.11 |
Severity: | Normal | Keywords: | queryset, boolean, order, ordering |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
I have recently encountered problems with duplicated results in pagination and trying to pin down the problem this seems to be a problem in the Django queryset. I'll attempt to explain the problem. Take a look at the following Queryset:
>>> p = Project.objects.all().order_by("closed") >>> p <QuerySet [<Project: Campanha de Incentivo - Alimentando a Solidariedade (Outubro)>, <Project: Campanha de Incentivo - Projeto Fora das 4 Paredes>, <Project: projeto luciele>, <Project: Campanha de Incentivo - Menos é Mais>, <Project: Campanha de Incentivo - Projeto Conviver>, <Project: Campanha de Incentivo - Aula de Música>, <Project: RPINCLUSÃO - Informações, Acessibilidade e Inclusão>, <Project: Ong ong>, <Project: Campanha de Incentivo - Projeto Alimentando Vidas>, <Project: Bola na Rede>, <Project: Campanha de Incentivo - Projeto Bolas de Pelos>, <Project: Mãos Solidárias>, <Project: Campanha de Incentivo - GRUPO DE ATITUDE SOCIAL - Moradores de Rua>, <Project: APAE pede Socorro>, <Project: Campanha de Incentivo - Anjos da Madrugada>, <Project: Pintura de Casas Emergenciais com o TETO - São Paulo>, <Project: Campanha de Incentivo - Aos Olhos do Pai>, <Project: Legião da Boa Vontade>, <Project: Campanha de Incentivo - Você é a gota que faltava, Doe Sangue!>, <Project: Campanha de Incentivo - Sonhar com Esperança>, '...(remaining elements truncated)...']>
The 'closed' field here is a Boolean. Everything seems to be correct except for this:
>>> p[0] <Project: Campanha de Incentivo - Anjos da Madrugada> >>> p[1] <Project: Bola na Rede>
When I get items from the queryset, they don't match the queryset repr order. What is even weirder is that neither the repr order nor the getitem order matches the PostgreSQL query.
>>> print(p.query) SELECT "ovp_projects_project"."id", "ovp_projects_project"."image_id", "ovp_projects_project"."address_id", "ovp_projects_project"."owner_id", "ovp_projects_project"."organization_id", "ovp_projects_project"."name", "ovp_projects_project"."slug", "ovp_projects_project"."published", "ovp_projects_project"."highlighted", "ovp_projects_project"."applied_count", "ovp_projects_project"."max_applies", "ovp_projects_project"."max_applies_from_roles", "ovp_projects_project"."public_project", "ovp_projects_project"."minimum_age", "ovp_projects_project"."hidden_address", "ovp_projects_project"."crowdfunding", "ovp_projects_project"."published_date", "ovp_projects_project"."closed", "ovp_projects_project"."closed_date", "ovp_projects_project"."deleted", "ovp_projects_project"."deleted_date", "ovp_projects_project"."created_date", "ovp_projects_project"."modified_date", "ovp_projects_project"."details", "ovp_projects_project"."description" FROM "ovp_projects_project" ORDER BY "ovp_projects_project"."closed" ASC
When hitting the database with such query, only removing a couple of fields to make it easier to read, this is what I get:
These are 3 different orders. Now, there's one little thing I can do to fix the QuerySet, which is iterating over it.
>>> for x in p: ... print(x.pk) ... 81 74 75 91 83 61 (...)
Suddenly the queryset now matches the result from my PostgreSQL query and everything is now correct.
>>> p <QuerySet [<Project: Pintura de Casas Emergenciais com o TETO - São Paulo>, <Project: Campanha de Incentivo - Anjos da Madrugada>, <Project: Ong ong>, <Project: projeto luciele>, <Project: Campanha de Incentivo - Projeto Alimentando Vidas>, <Project: Campanha de Incentivo - Menos é Mais>, <Project: Campanha de Incentivo - Alimentando a Solidariedade (Outubro)>, <Project: Campanha de Incentivo - Aula de Música>, <Project: Campanha de Incentivo - Projeto Fora das 4 Paredes>, <Project: Campanha de Incentivo - Padoca Artesanal>, <Project: Campanha de Incentivo - EmpoderA>, <Project: Campanha de Incentivo - Moto Sopa>, <Project: Campanha Incentivo - Projeto Vale Jequitinhonha>, <Project: Campanha de Incentivo - Eternas Crianças>, <Project: Campanha de Incentivo - Festa de Natal>, <Project: Treinador voluntário de futebol - Águias Livres>, <Project: Fundação Gol de Letra - Faça parte deste time!>, <Project: Campanha de Incentivo - Ganhando Almas>, <Project: Campanha de Incentivo - Acolchoados de Ponto Cruz e Patchwork>, <Project: Campanha de Incentivo - Show de Encantamento Solidário>, '...(remaining elements truncated)...']> >>> p[0] <Project: Pintura de Casas Emergenciais com o TETO - São Paulo> >>> p[1] <Project: Campanha de Incentivo - Anjos da Madrugada>
I'm trying to find out where the problem is really happening but I have limited knowledge about django ORM internals, so I haven't gotten that far yet. Currently I'm hacking around it by iterating over the queryset before paginating it and passing it to my view.
Things get even weirder if I try to order by 2 boolean fields, with issues such as:
>>> p = Project.objects.all().order_by("closed", "-highlighted") >>> p <QuerySet [<Project: Campanha Incentivo - Projeto Vale Jequitinhonha>, <Project: Campanha de Incentivo - Alimentando a Solidariedade (Outubro)>, <Project: Campanha de Incentivo - Festa do Dia Das Crianças>, <Project: Campanha de Incentivo - Cartas para o "Papai Noel">, <Project: Pintura de Casas Emergenciais com o TETO - São Paulo>, <Project: Campanha de Incentivo - Padoca Artesanal>, <Project: Campanha de Incentivo - EmpoderA>, <Project: Construção de Casas Emergenciais com o TETO - São Paulo - Estrutura e entrega da casa>, <Project: Campanha de Incentivo - Doação de Brinquedos>, <Project: Campanha de Incentivo - Pequena ajuda, grande transformação>, <Project: Campanha de Incentivo - Aula de Música>, <Project: Campanha de Incentivo - Aos Olhos do Pai>, <Project: APAE pede Socorro>, <Project: Campanha de Incentivo - GRUPO DE ATITUDE SOCIAL - Moradores de Rua>, <Project: Mãos Solidárias>, <Project: Campanha de Incentivo - Projeto Bolas de Pelos>, <Project: Campanha de Incentivo - Projeto Conviver>, <Project: Bola na Rede>, <Project: Campanha de Incentivo - Você é a gota que faltava, Doe Sangue!>, <Project: projeto luciele>, '...(remaining elements truncated)...']> >>> p[0], p[0].pk (<Project: Pintura de Casas Emergenciais com o TETO - São Paulo>, 81) >>> p[1], p[1].pk (<Project: Pintura de Casas Emergenciais com o TETO - São Paulo>, 81)
Attachments (1)
Change History (4)
by , 7 years ago
Attachment: | django-boolean-bug.png added |
---|
comment:1 by , 7 years ago
Description: | modified (diff) |
---|
comment:2 by , 7 years ago
Resolution: | → invalid |
---|---|
Status: | new → closed |
comment:3 by , 7 years ago
This was indeed the problem. For some reason I assumed ordering by a boolean field would yield consistent results when LIMITing it on PostgreSQL. It does not. Ordering by pk + boolean fixes it.
Thanks.
My hunch is that PostgreSQL is "at fault", not Django. When you execute
order_by()
with a boolean field, the results with the same value (True
orFalse
) are ordered randomly. If you want a deterministic order, you could do something like.order_by('closed', 'pk')
.In the future, please see TicketClosingReasons/UseSupportChannels to help confirm a bug before creating a ticket.