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

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

@phoenixbycaselka ci sta già lavorando.

Dal 14/11/2026.

  • #270 di @phoenixbycaselka — aperta

Valutazione

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

Direzione di ricerca

La riga di intestazione è costruita con f"{f.operation} {f.app}.{f.model}" nell'output testuale di cli/orm_lens/cli.py, intorno alle righe 313-317. Fai in modo che la parte del modello compaia solo quando f.model è impostato, così che i risultati a livello di grafo stampino solo il nome dell'app. Aggiungi in cli/tests/test_cli_subcommands_smoke.py un caso che esegue migration-risk sul repro con due leaf e verifica che venga stampato (migration graph) shop senza None, mentre un normale risultato AddField continua a stampare app.model.field. Completato quando passano quel test, pytest, ruff e mypy.

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

Descrizione

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.

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.