suggest-indexes proposes composite indexes that include the primary key or a unique field

Aperta Adatta ai principianti
#269 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

@bfs2021 ci sta già lavorando.

Dal 14/11/2026.

  • #272 di @bfs2021 — aperta

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
82/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
django, python
Ambito
cli, database

Direzione di ricerca

Inizia da cli/orm_lens/query_analyzer.py in suggest_indexes(), dove sono disponibili pk_name e target_model, poi leggi il ciclo sui composti in cima a _propose_indexes(). Costruisci un insieme di colonne singole uniche (la PK, pk, i campi con primary_key=True o unique=True, e gli OneToOneFields) e salta ogni composto che ne contiene una, lasciando invariata la logica dei campi singoli. È fatto quando il repro di Story produce solo ['status'] e ['title'], pk= si comporta come id=, e cli/tests/test_query_analyzer.py insieme a pytest, ruff e mypy passano.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

bug good first issue python

Source: weblate — trans.Unit: models.Index(fields=['id', 'translation'])

What happens

filter(id=pk, title=title) seen twice gives models.Index(fields=['id', 'title']). The primary key is unique: an equality lookup on it returns at most one row, so the PK index already answers the query and a composite led by id only costs writes and disk. The same is true for any unique=True field.

_propose_indexes() uses already_indexed for single-field proposals only. The composite loop checks the M2M/reverse filter from #252/#263 and existing Meta groups, nothing else. pk= is folded into id before that, so both spellings hit it.

On weblate trans.Unit gets models.Index(fields=['id', 'translation']).

Repro

board/models.py

from django.db import models


class Story(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    status = models.CharField(max_length=20)

board/stats.py

from .models import Story


def by_id_and_title(pk, title):
    return Story.objects.filter(id=pk, title=title).first()


def by_id_and_title_again(pk, title):
    return Story.objects.get(id=pk, title=title)


def by_pk_and_status(pk, status):
    return Story.objects.filter(pk=pk, status=status).exists()


def by_pk_and_status_again(pk, status):
    return Story.objects.filter(pk=pk, status=status).first()


def by_slug_and_status(slug, status):
    return Story.objects.filter(slug=slug, status=status).first()


def by_slug_and_status_again(slug, status):
    return Story.objects.filter(slug=slug, status=status).exists()
$ orm-lens suggest-indexes board.Story
target: board.Story
proposed Meta.indexes (5):
  models.Index(fields=['id', 'status']),  # 2 composite filter sites
  models.Index(fields=['id', 'title']),  # 2 composite filter sites
  models.Index(fields=['slug', 'status']),  # 2 composite filter sites
  models.Index(fields=['status']),  # 4 filter sites
  models.Index(fields=['title']),  # 2 filter sites

Expected: only ['status'] and ['title']. The usage table can keep listing id + title etc.

Where

cli/orm_lens/query_analyzer.py

  • line 497, _propose_indexes(); the composite loop is right at the top of the function body;
  • line 309, _indexed_single_fields(): builds already_indexed, but it also contains db_index=True fields and FKs, which are not unique, so it can't be reused as is;
  • line 598, suggest_indexes(): pk_name (line 675) and target_model are both there.
Suggested fix

In suggest_indexes(), build the set of unique single columns: pk_name, "pk", every field with primary_key=True or unique=True (the _PK_TRUE_RE / _UNIQUE_TRUE_RE regexes are already in the module), and OneToOneFields (unique by definition). Pass it to _propose_indexes() as a new keyword argument and skip a composite combo that contains any of these names.

Don't touch the single-field and order_by logic.

How to test
  • cli/tests/test_query_analyzer.py: the repro gives exactly ['status'] and ['title'].
  • filter(pk=..., ...) is dropped the same as filter(id=..., ...).
  • A model with code = models.CharField(primary_key=True) drops a ['code', 'title'] composite.
  • A composite of two non-unique fields (filter(title=..., status=...)) is still proposed.
  • cd cli && python -m pytest -q, ruff and mypy pass.

Difficulty: good first issue, one set and one if.

Lingua principale
Python
Stelle
84
Fork
13
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di FROWNINGdev/django-orm-lens

Tutte le issue di FROWNINGdev/django-orm-lens

Issue simili

Altre issue su Python

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.