在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})")

更新

我找到了一种让使用上述策略更愉快的方法:

class WithConnection:
def __init__(self, queryset):
self.queryset = queryset
def __iter__(self):
for model in self.queryset:
connection = model.my_connections[0] if model.my_connections else None
yield model, connection
class InvestorQuerySet(models.QuerySet):
def pov(self, advisor):
return WithConnection(
self.prefetch_related(models.Prefetch(
'connections',
queryset=Connection.objects.filter(advisor=advisor),
to_attr='my_connections',
))
)

像这样使用:

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

希望在这方面有所改进。Django的ORM非常强大,似乎应该有一种更简单的方法来实现这一点。

我认为你只是想要

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%的兴奋,但我喜欢它现在开始使用它。如果有人有更好的答案,请插话。

最新更新