Parser: any class whose base name ends in `Mixin` becomes a model, even a QuerySet or a plain helper

Abierto
#249 0 comentarios 0 reacciones 0 asignados Ver en GitHub

@noahkhomer18 ya está trabajando en esto.

Desde el 14/11/2026.

  • #258 de @noahkhomer18 — abierto

Evaluación

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

Línea de trabajo

Start in cli/orm_lens/parser.py: the Mixin$ branch of _looks_like_model() (around line 442) and where _resolve_and_filter() (around line 829) seeds is_model_name from it. Run the repro from the issue to confirm that only projects.Project should be listed. Done when phantom mixins drop out, the abstract-mixin test in test_abstract_and_auth_user.py and the mezzanine golden snapshot still pass, and pytest, ruff and mypy are clean. Note that open pull request #258 already targets this issue, so check it before starting.

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

Descripción

bug good first issue python

Source: label-studio — label_studio/projects/models.py:72, label_studio/tasks/models.py; weblate — weblate/trans/mixins.py:74,81,154

What happens

_looks_like_model() returns True as soon as a base's name ends with Mixin. That's a guess for third-party bases we can't see. But it also fires when the mixin is defined right there in the workspace and is a plain class:

  • label-studio: class ProjectQuerySetWithFSM(FSMStateQuerySetMixin, ProjectQuerySet). ProjectQuerySet is a models.QuerySet, FSMStateQuerySetMixin is a plain class. orm-lens lists projects.ProjectQuerySetWithFSM as a model, and drift says blocking: "declared, but no migration creates it". Four of these, so drift fails the build.
  • weblate: class URLMixin(BaseURLMixin), LoggerMixin(BaseURLMixin), PathMixin(LoggerMixin, URLMixin) in trans/mixins.py. BaseURLMixin is class BaseURLMixin:. They're pulled in because Project inherits PathMixin, and then show up as three blocking drift entries.
Repro

projects/mixins.py

class StateQuerySetMixin:
    def with_state(self):
        return self


class URLMixin:
    def get_absolute_url(self):
        return "/"

projects/models.py

from django.db import models

from .mixins import StateQuerySetMixin, URLMixin


class ProjectQuerySet(models.QuerySet):
    pass


class ProjectQuerySetWithState(StateQuerySetMixin, ProjectQuerySet):
    pass


class PathMixin(URLMixin):
    def full_path(self):
        return self.get_absolute_url()


class Project(models.Model, PathMixin):
    title = models.CharField(max_length=100)

    objects = ProjectQuerySetWithState.as_manager()
$ orm-lens list
projects.ProjectQuerySetWithState
projects.PathMixin
projects.Project

Expected: only projects.Project.

Where

cli/orm_lens/parser.py

  • line 442, _looks_like_model(), the re.search(r"Mixin$", tail) branch;
  • line 829, _resolve_and_filter(), which seeds is_model_name from _looks_like_model() and then runs the fixed point over real definitions.
Suggested fix

Use the name guess only for bases we have no definition for. In _resolve_and_filter(), collect defined = {m.name for m in all_defs} and pass it to _looks_like_model(); skip the Mixin$ branch when tail in defined. A mixin that really is a model (class TimeStampMixin(models.Model)) still gets in through the fixed-point loop, because its own base is models.Model.

test_abstract_and_auth_user.py covers abstract mixins and should stay green. The mezzanine golden fixture has *Mixin classes too, so run the golden tests and update the snapshot if a phantom model drops out (say so in the PR).

How to test
  • A test in cli/tests/test_cross_module_bases.py (or a new file) with the repro: only Project is returned.
  • A mixin defined as class OwnedMixin(models.Model) with abstract = True, plus class Doc(OwnedMixin): Doc is still a model and inherits the fields.
  • cd cli && python -m pytest -q, ruff and mypy pass.

Difficulty: good first issue.

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.