Оптимальный способ получения связанной модели и сквозной модели вместе в ManyToManyField

Данная схема:

class Investor(models.Model):
    name = models.CharField(max_length=30)
    advisors = models.ManyToManyField("Advisor", related_name="investors", through="Connection")

class Advisor(models.Model):
    name = models.CharField(max_length=30)

class Connection(models.Model):
    investor = models.ForeignKey("Investor", related_name="connections", on_delete=models.CASCADE)
    advisor  = models.ForeignKey("Advisor",  related_name="connections", on_delete=models.CASCADE)
    blocked  = models.BooleanField(default=False)

А если дан советник "a", то какой оптимальный способ получения списка всех инвесторов и их связи с "a", если он существует?

Лучшее, что я выяснил на данный момент, это:

from django.db.models import Prefetch

for investor in Investor.objects.prefetch_related(
    Prefetch(
        'connections',
        queryset=Connection.objects.filter(advisor=a),
        to_attr='connection',
    )
):
    name = investor.name
    blocked = None
    if investor.connection:
        blocked = investor.connection[0].blocked
    print(f"{name} (blocked={blocked})")

Я полагаю, что вы просто хотите

a.investor_set.all()

После долгих исследований и многочисленных итераций я пришел к следующему решению:

class WithConnectionQuerySet(models.QuerySet):
    def __iter__(self):
        return map(
            lambda m: (m, m.my_connections[0]) if m.my_connections else (m, None),
            super().__iter__(),
        )

class Investor(models.Model):
    ...
    @classmethod
    def viewed_by(cls, advisor):
        manager = cls._default_manager.__class__.from_queryset(WithConnectionQuerySet)()
        manager.model = cls
        return manager.prefetch_related(models.Prefetch(
            'connections',
            queryset=Connection.objects.filter(advisor=advisor),
            to_attr='my_connections',
        ))

Используется следующим образом:

advisor = Advisor.objects.first()
for investor, connection in Investor.viewed_by(advisor).filter(age__gt=40):
    # do stuff with investor
    if connection:
        # do stuff with connection

Я все еще не в восторге от него на 100%, но сейчас он мне нравится достаточно, чтобы начать его использовать. Если у кого-то есть лучший ответ, пожалуйста, ответьте.

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