Как получить все объекты модели, связанные с исходным, даже если они находятся дальше, чем непосредственно связаны?

Возможно, название немного сбивает с толку, но я считаю, что объяснение будет намного понятнее.

У меня есть две модели

class AgrParameterUnits(models.Model):
    Name = models.CharField(max_length=50, verbose_name=_("Name"))
    ShortName = models.CharField(max_length=50, verbose_name=_("ShortName"))

class AgrParameterUnitConversions(models.Model):
    ParrentParameterUnitId = models.ForeignKey(AgrParameterUnits, on_delete=models.DO_NOTHING, null=False, blank=False, db_column='ParrentParameterUnitId', related_name='ParrentParameterUnitId', primary_key=True, verbose_name=_("Parrent Parameter Unit Id"))
    ChildParameterUnitId = models.ForeignKey(AgrParameterUnits, on_delete=models.DO_NOTHING, null=False, blank=False, db_column='ChildParameterUnitId', related_name='ChildParameterUnitId', verbose_name=_("Child Parameter Unit Id"))
    ConversionCoefficent = models.DecimalField(max_digits=18, decimal_places=7, null=False, blank=False, db_column='ConversionCoefficent', verbose_name=_("Conversion Coefficent"))

Речь идет о преобразованиях между единицами измерения.

Сценарий следующий.

Я получаю одну единицу, и мне нужны все, которые так или иначе связаны с начальной единицей.

Пример:

в базе данных есть 3 AgrParameterUnits записи и 2 AgrParameterUnitConversions записи

AgrParameterUnits записи: Гц, килогерц, мегагерц.

<>>

AgrParameterUnitConversions записи: 1 килогерц = 1000 Гц 1 Мегагерц = 1000 КилоГерц

<>>>

Как бы выглядел запрос, если бы я хотел, задавая Герц, найти Килогерц и Мегагерц?

Это очень похоже на задачу рекурсии, с которой я пока не могу справиться. Вот что у меня есть на данный момент.

def _get_units(base_unit):

    try:
        """Try to filter unit conversions where this the received unit is the parent unit"""
        unit_choices = AgrParameterUnitConversions.objects.filter(ParrentParameterUnitId=base_unit).annotate(
            unit_name=F('ParrentParameterUnitId__Name'),
            unit_id=F('ParrentParameterUnitId'),
        ).values("unit_id", "unit_name")
        related_units = AgrParameterUnitConversions.objects.filter(
            ChildParameterUnitId=unit_choices[0]["unit_id"]).annotate(
            unit_name=F('ParrentParameterUnitId__Name'),
            unit_id=F('ParrentParameterUnitId'),
        ).values("unit_id", "unit_name")
        units_this_round = (unit_choices | related_units).distinct()

    except IndexError as e:
        """If the above filter fails on list index out of range, it means there is no conversion where this unit is a parent unit
        In that cate try to filter for conversions, where it is as a child parameter unit.
        
        If one of the two is found, repeat until no more conversions are to be found"""
        unit_choices = AgrParameterUnitConversions.objects.filter(ChildParameterUnitId=base_unit).annotate(
            unit_name=F('ParrentParameterUnitId__Name'),
            unit_id=F('ParrentParameterUnitId'),
        ).values("unit_id", "unit_name")
        related_units = AgrParameterUnitConversions.objects.filter(
            ChildParameterUnitId=unit_choices[0]["unit_id"]).annotate(
            unit_name=F('ParrentParameterUnitId__Name'),
            unit_id=F('ParrentParameterUnitId'),
        ).values("unit_id", "unit_name")
        units_this_round = (unit_choices | related_units).distinct()
    available_units = (related_units | units_this_round).distinct()
    if related_units:
        return _get_units(related_units[0]["unit_id"])
    return available_units

Даже подсказка по использованию инструмента/процедуры была бы очень полезна.

Вернуться на верх