【问题标题】:django model select_related or prefetch_related child modeldjango 模型 select_related 或 prefetch_related 子模型
【发布时间】:2021-04-08 11:40:58
【问题描述】:

这里是新手,我有两个模型,下面是

class ReceipeMaster(models.Model):
    receipe_type = models.CharField(max_length=50, choices=TYPE_OPTIONS, default=TYPE_OPTIONS[0])
    item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name='receipe')
    units = models.IntegerField(default=1)
    status = models.BooleanField(default=True)

class ReceipeDetail(models.Model):
    master = models.ForeignKey(ReceipeMaster, on_delete=models.CASCADE, related_name='items')
    item_type = models.ForeignKey(Item_type, null=True, on_delete=models.PROTECT)
    item = models.ForeignKey(Item, on_delete=models.PROTECT)
    quantity = models.IntegerField()

我有详细查看DetailView

class ReceipeDetailView(PermissionRequiredMixin, DetailView):
    permission_required = 'item_management.view_receipemaster'
    model = ReceipeMaster
    template_name = 'receipes/show.html'
    context_object_name = 'receipe'

我想在ReceipeMaster 模型和ReceipeDetail 模型上使用prefetch_related 或select_related。只需在两个模型上预取即可。

问候

【问题讨论】:

    标签: python django django-models django-views django-forms


    【解决方案1】:

    您可以完全覆盖get_queryset 方法,而无需调用super():

    def get_queryset(self):
        queryset = ReceipeMaster.objects.select_related('item').prefetch_related(
            Prefetch(
                'items',
                queryset=ReceipeDetail.objects.select_related('item_type', 'item'),
            )
        )
        return queryset
    

    然后get_object 方法将过滤queryset 以获取对象。在这种情况下这不会成为问题,因为 filter() 和 select_related() 链接的顺序并不重要 (docs)。

    【讨论】:

      【解决方案2】:

      要自定义如何在通用视图中获取查询集,需要覆盖视图的get_queryset 方法。此外,如果想在预取对象上使用select_related,则应使用Prefetch objects [Django docs] 并指定其queryset。由于这是一个 DetailView 并因此处理一个对象,您将需要覆盖 get_object:

      from django.db.models import Prefetch
      from django.http import Http404
      from django.utils.translation import gettext as _
      
      class ReceipeDetailView(PermissionRequiredMixin, DetailView):
          permission_required = 'item_management.view_receipemaster'
          model = ReceipeMaster
          template_name = 'receipes/show.html'
          context_object_name = 'receipe'
          
          def get_object(self, queryset=None):
              # Use a custom queryset if provided; this is required for subclasses
              # like DateDetailView
              if queryset is None:
                  queryset = self.get_queryset()
      
              # Next, try looking up by primary key.
              pk = self.kwargs.get(self.pk_url_kwarg)
              slug = self.kwargs.get(self.slug_url_kwarg)
              if pk is not None:
                  queryset = queryset.filter(pk=pk)
      
              # Next, try looking up by slug.
              if slug is not None and (pk is None or self.query_pk_and_slug):
                  slug_field = self.get_slug_field()
                  queryset = queryset.filter(**{slug_field: slug})
      
              # If none of those are defined, it's an error.
              if pk is None and slug is None:
                  raise AttributeError(
                      "Generic detail view %s must be called with either an object "
                      "pk or a slug in the URLconf." % self.__class__.__name__
                  )
              queryset = queryset.select_related('item').prefetch_related(
                  Prefetch(
                      'items',
                       queryset=ReceipeDetail.objects.select_related('item_type', 'item')
                  )
              )
              try:
                  # Get the single item from the filtered queryset
                  obj = queryset.get()
              except queryset.model.DoesNotExist:
                  raise Http404(_("No %(verbose_name)s found matching the query") %
                                {'verbose_name': queryset.model._meta.verbose_name})
              return obj
      

      【讨论】:

      • 非常感谢@Abdul Aziz Barkat 的时间和回答,但是即使在添加该方法之后仍然得到相同的 22 个查询没有变化
      • 在html文件{% for receipe_detail in receipe.items.all %}中获取我的食谱项目@
      • 同题的你也可以看看这个帖子stackoverflow.com/questions/66950747/…,谢谢
      • @MuhammadSharif 啊,我看到这是DetailView,我在这里监督了这个特别的细节。问题是get_object 会在get_queryset 给出的查询集上调用过滤器,因此我们的预取等会浪费掉。您需要在此处覆盖 get_object。检查我的编辑,虽然实现中有很多额外的东西,是从get_object 的默认实现中复制的(处理过滤的 kwarg 是 pk 还是 slug 等)。
      • 完全是天才,非常感谢您的时间和精力被接受为答案,如果您不介意也可以看看我的其他帖子,请stackoverflow.com/questions/66950747/…
      猜你喜欢
      • 1970-01-01
      • 2013-11-23
      • 1970-01-01
      • 2023-01-06
      • 2020-08-27
      • 2021-09-06
      • 2017-02-10
      • 2023-03-10
      • 2015-01-23
      相关资源
      最近更新 更多