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

オープン 初心者向け
#269 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

@bfs2021 がすでに取り組んでいます。

2026年11月14日 から。

  • #272 @bfs2021 による — オープン

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
82/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
django, python
領域
cli, database

調査の方向性

cli/orm_lens/query_analyzer.py の suggest_indexes() から始めてください。ここでは pk_name と target_model が利用できます。その後、_propose_indexes() の先頭にある複合インデックスのループを読んでください。単一列のユニークな集合(PK、pk、primary_key=True または unique=True のフィールド、OneToOneFields)を作り、そのいずれかを含む複合インデックスはスキップしてください。単一フィールドのロジックには手を加えないでください。Story の再現で ['status'] と ['title'] のみが得られ、pk= が id= と同様に動作し、cli/tests/test_query_analyzer.py と pytest、ruff、mypy がすべて通れば完了です。

索引モデルが issue の本文から書いたものです。

説明

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.

主要言語
Python
スター
84
フォーク
13
PR マージ指標
30日以内にマージされた PR はありません

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

FROWNINGdev/django-orm-lens のほかの issue

FROWNINGdev/django-orm-lens の issue をすべて見る

似ている issue

Python の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。