Оптимальный способ получения связанной модели и сквозной модели вместе в 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%, но сейчас он мне нравится достаточно, чтобы начать его использовать. Если у кого-то есть лучший ответ, пожалуйста, ответьте.