从django的特定模型中获取子One To Many关系



我有两个模型UserSale,每个销售有一个买家和一个卖家。我想从特定用户my_user中获得所有买家(也包括用户)。

class User(models.Model):
name = models.CharField(max_length=250, default='', blank=True)
# more fields....
class Sale (models.Model):
buyer = models.ForeignKey(
User,
blank=False,
null=False,
related_name='purchases',
on_delete=models.CASCADE,
)
seller = models.ForeignKey(
User,
blank=False,
null=False,
related_name='sales', 
on_delete=models.CASCADE,
)
# more fields....

User.objects.filter(purchases__seller = my_user)

但是我想知道是否有一种方法可以直接使用my_user对象来做到这一点。像这样

my_user.sales.buyer

提前感谢各位

你的台词和seokmin的解决方案有很大的不同:

my_user.sales.values_list('buyer', flat=True)  # returns a list of ids
User.objects.filter(purchases__seller=my_user)  # returns a QuerySet

你可以做一个更难看的理解(select_related确保你有所有的买家数据,不会发送一个查询每个销售,更多在这里):

buyers = {sale.buyer for sale in my_user.sales.select_related('buyer')}

从多个用户获取

更糟糕的情况是,当你不是只有一个用户,而是有多个用户时,我认为最终会出现这种情况。如果你想让Users出来,以防止调用DB太多次,你必须在这里做一些更神奇的事情。

受您原始查询的启发,将是一个非常简单的2行代码片段:

user_ids = my_user_qs.values_list('id', flat=True)
buyers = User.objects.filter(purchases__seller__id__in=user_ids)

容易。


现在假设你想重新使用buyers = {sale.buyer...作为用户的方法,我们称之为user.get_buyers():

buyers = [user.get_buyers() for user in my_users]

这将为每个用户运行一个对DB的查询,并且即使只有数百个用户也可能非常慢。

如何优化?

首先你必须定义一个Prefetch——它告诉Django在最初为用户运行查询时下载额外的数据。

from django.db.models import Prefetch
sales_prefetch = Prefetch('sales', Sale.objects.select_related('buyer', 'seller')
# no query runs yet

预取说"当计算这个预取应用于的查询集时,同时获取Sale查询并将其附加到'sales'子字段。此外,当您处理它时,将销售表与买方和卖方">上的用户表连接起来。

那么你必须确保当你运行获取my_users的查询时,你使用这个预取。

my_users = Users.objects.prefetch_related(sales_prefetch)

这允许你做以下事情:

buyers = [user.get_buyers() for user in my_users]
# sends a single query and returns a list of lists of User objects 
# you still have to flatten the list of lists

结论

总的来说,通过User模型使用原始查询会更好。使用Prefetch,你仍然有使用用户的相关字段的风险,你没有运行Prefetch/select_related,你会为最终列表中的每个项目触发一个DB查询。

我强烈推荐阅读和理解select_relatedprefetch_related,因为它们是Django正确执行join的方式。

https://docs.djangoproject.com/en/4.0/ref/models/querysets/select-relatedhttps://docs.djangoproject.com/en/4.0/ref/models/querysets/prefetch-related

您可以像这样访问特定用户的销售和购买:

q = my_user.sale_set.all()

q = my_user.sale_set.filter(your fileter)

则可以使用:

访问买方或卖方
buyer = q.buyer 
seller = q.seller

,但要注意使用q.last(),因为你不能访问查询集列表中的买方或卖方。

我希望这对你有帮助。

你可以使用django values_list

https://docs.djangoproject.com/en/dev/ref/models/querysets/值列表

所以,你可以使用下面的代码
buyer_list = my_user.sales.values_list('buyer', flat=True)

另外,如果要删除重复的值,请查找不同的选项。

https://docs.djangoproject.com/en/4.0/ref/models/querysets/截然不同的

有人推荐values_list选项只带来值而不是对象。谢谢你的推荐。

所以,我找到了select_related选项。

https://docs.djangoproject.com/en/4.0/ref/models/querysets/select-related

如果你想使用select_related,也许这个例子类似于django文档的工作

buyers = set()
for sale in my_user.sales.select_related('buyer').all():
buyers.add(sale.buyer)

另一个解决方案是使用'values'方法。但是它必须写你想要使用的所有用户列。

如何组合select_related()和value()?(2016)

最新更新