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

Abierto Apto para principiantes
#268 0 comentarios 0 reacciones 0 asignados Ver en GitHub

@phoenixbycaselka ya está trabajando en esto.

Desde el 14/11/2026.

  • #270 de @phoenixbycaselka — abierto

Evaluación

Dificultad
1/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
88/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
django, python
Área
cli

Línea de trabajo

La línea de encabezado se construye con f"{f.operation} {f.app}.{f.model}" en la salida de texto de cli/orm_lens/cli.py, alrededor de las líneas 313-317. Haz que la parte del modelo aparezca solo cuando f.model esté definido, de modo que los hallazgos a nivel de grafo impriman solo el nombre de la app. Añade un caso a cli/tests/test_cli_subcommands_smoke.py que ejecute migration-risk sobre el repro de dos leaf y compruebe que aparece (migration graph) shop sin None, mientras que un hallazgo normal de AddField sigue imprimiendo app.model.field. Terminado cuando pasen ese test, pytest, ruff y mypy.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

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.

Lenguaje dominante
Python
Estrellas
84
Forks
13
Métricas de merge de PR
Sin PR fusionados en 30 d

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de FROWNINGdev/django-orm-lens

Todos los issues de FROWNINGdev/django-orm-lens

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.