prefetch_related和Prefetch对象问题,通过反向外键过滤

时间:2017-12-10 09:21:24

标签: django django-queryset

我有2个型号,公司和产品。

class Product(Meta):
    company = models.ForeignKey(Company, related_name='products', on_delete=models.CASCADE)

形成数据库我试图获取公司数据和相应的产品。

从产品中我只想获得名称,并按照updated_at,created_at降序排序。

我正在使用Prefetch对象和prefetch_related,并且最终我有多种误解,他们是如何工作的。

def get_queryset(self):
    qs = Company.objects.prefetch_related(
        Prefetch('products', queryset=Product.objects.only('name').order_by('-updated_at', '-created_at'))).get()
    return qs

我收到的错误是:

get() returned more than one Company 

因为我使用prefetch_related关闭了)))方法/功能:

  1. 我认为get()将对公司对象采取行动并使用来自网址的pk / slug来获取它(默认情况下在DetailView中执行)。似乎并非如此。

  2. 我已经在使用'产品' Prefetch对象中的相关名称,为什么在queryset中需要再次告诉模型queryset=Product.objects....

  3. 我在django文档中查看以下示例:

       Question.objects.prefetch_related(Prefetch('choice_set')).get().choice_set.all()
    

    如果有' choice_set'在Prefetch对象中为什么在最后choice_set.all()调用?

    不是Django在prefetch中将问题集附加到查询集中的问题集(question.choice_set)吗?

    我认为我的问题在于我不了解执行的顺序,而且我对方法的链接方式感到困惑,即使这些方法已经被关闭了#);'

1 个答案:

答案 0 :(得分:1)

queryset.get()仅在查询集具有单个对象时才有效。如果它包含零个或多个对象,则会出现错误。

您应该从get_queryset对象返回一个查询集。在基于类的视图中,在pk / slug上过滤的代码位于get_object

如果您想要为多个国家/地区提取产品,prefetch_related方法非常有用。 Django docs使用get()的方式在我看来令人困惑 - 如果查询集中有一个项目,则prefetch_related过于复杂。

如果您有一家公司,那么没有任何优势,如果您单独取用国家/地区,代码会更简单,例如在get_context_data

def get_context_data(self, **kwargs):
    context = super(MyView, self).get_context_data(**kwargs)
    context['products'] = Product.objects.filter(company=self.object).Product.objects.only('name').order_by('-updated_at', '-created_at')))
    return context

我已删除only('name')电话。这是您可能不需要的优化。

如果您确实想使用prefetch_related,请删除get()

qs = Company.objects.prefetch_related(
    Prefetch('products', queryset=Product.objects.order_by('-updated_at', '-created_at')))

通过指定上面的查询集,您可以更改顺序(如果愿意,可以过滤它)。如果您不想自定义查询集,则可以执行以下操作:

Company.objects.prefetch_related('products')

当您使用Question.objects.prefetch_related(...)时,查询集仍然是一个问题列表。您需要在各个实例上调用choice_set.all()来访问他们的选择。这不会导致任何其他查询,因为Django已经预先选择了这些选项。

queryset = Question.objects.prefetch_related(Prefetch('choice_set'))
for question in queryset:
    print(question) # the question
    print(question.choice_set.all())  # the related choices