`migration-risk` text output prints `app.None` for graph-level findings

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

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

2026年11月14日 から。

  • #270 @phoenixbycaselka による — オープン

評価

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

調査の方向性

ヘッダー行は cli/orm_lens/cli.py のテキスト出力で、313〜317行目付近の f"{f.operation} {f.app}.{f.model}" によって作られています。グラフレベルの検出結果ではアプリ名だけが表示されるように、f.model が設定されている場合にのみモデル部分が表示されるようにしてください。cli/tests/test_cli_subcommands_smoke.py にケースを追加し、2つの leaf を持つ再現ケースに対して migration-risk を実行して、None なしで (migration graph) shop と表示されることを確認してください。通常の AddField の検出結果は引き続き app.model.field を表示する必要があります。そのテスト、pytest、ruff、mypy がすべて通れば完了です。

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

説明

bug good first issue python

Source: pretix sendmail, weblate trans (seen while checking the conflicting_migration_leaves reports from #246 and #247)

What happens

The text output of migration-risk prints f"{f.operation} {f.app}.{f.model}". Graph-level findings have no model: conflicting_migration_leaves and alembic_multiple_heads both set model=None. So the header line reads (migration graph) shop.None.

--format sarif and --format github are fine, only the text format has it. Same family as #207.

Repro

shop/models.py

from django.db import models


class Item(models.Model):
    name = models.CharField(max_length=100)
    price = models.IntegerField(default=0)
    sku = models.CharField(max_length=20, default="")

shop/migrations/: 0001_initial.py creates Item(id, name), then two branches that both depend on it:

# 0002_item_price.py
class Migration(migrations.Migration):
    dependencies = [("shop", "0001_initial")]
    operations = [migrations.AddField(model_name="item", name="price", field=models.IntegerField(default=0))]

# 0002_item_sku.py
class Migration(migrations.Migration):
    dependencies = [("shop", "0001_initial")]
    operations = [migrations.AddField(model_name="item", name="sku", field=models.CharField(max_length=20, default=""))]

This is a real conflict, so the finding itself is right. Only the header line is off:

$ orm-lens migration-risk
shop/migrations/0002_item_price.py:1  [critical/high]  (migration graph) shop.None
  rule:      conflicting_migration_leaves
  risk:      App 'shop' has 2 leaf migrations (0002_item_price, 0002_item_sku). ...
shop/migrations/0002_item_sku.py:1  [critical/high]  (migration graph) shop.None
  rule:      conflicting_migration_leaves
  ...

Expected: (migration graph) shop.

Where
  • cli/orm_lens/cli.py, lines 313–317: the header line of the text format.
  • cli/orm_lens/migrations_parser.py, line 1522, and cli/orm_lens/alembic_risk.py, line 467: the two places that create a finding with model=None (for reference, no change needed there).
Suggested fix

Build the target from the parts that are set: f.app, then .{f.model} only when f.model is set, then .{f.field} as now. A small helper next to the print keeps it readable.

How to test
  • cli/tests/test_cli_subcommands_smoke.py: run migration-risk on the repro tree, check the output has (migration graph) shop and no None.
  • A normal finding (AddField NOT NULL without default) still prints app.model.field.
  • cd cli && python -m pytest -q, ruff and mypy pass.

Difficulty: good first issue, one line and a test.

主要言語
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 を短くまとめたダイジェスト。