减少为呈现具有相同内嵌可编辑字段的项目列表而进行的查询次数

Posted

技术标签:

【中文标题】减少为呈现具有相同内嵌可编辑字段的项目列表而进行的查询次数【英文标题】:Reduce number of queries made to render list of items with same in-line editable field 【发布时间】:2019-09-13 00:41:20 【问题描述】:

在呈现项目表时,如果未定义字段 X 的值,则将其呈现为选择元素。

Django 对每个选择元素进行查询,这些元素加起来会导致大表延迟。

减少查询次数的最佳方法是什么?

views.py

from rest_framework import renderers
from rest_framework.response import Response

class ItemViewSet(viewsets.ModelViewSet):
  queryset = models.Item.objects.select_related("bought_by")
  serializer_class= serializers.ItemSerializer
  filterset_fields = ("bought_by")
  renderer_classes = [renderers.JSONRenderer, renderers.BrowsableAPIRenderer, renderers.TemplatehtmlRenderer]

  def list(self, request, *args, **kwargs):
    queryset = self.filter_queryset(self.get_queryset())

    if request.accepted_renderer.format == "html":
      items = list()
      for item in queryset:
        items.append("serializer": self.get_serializer(item), "item": item)


      return Response(
        
          "items_info": items,
          "style": "template_pack": "rest_framework/inline/",
        ,
        template_name="myapp/items_list.html",
      )
    else:
     page = self.paginate_queryset(queryset)
     if page is not None:
       serializer = self.get_serializer(page, many=True)
       return self.get_paginated_response(serializer.data)

      serializer = self.get_serializer(queryset, many=True)

    return Response(serializer.data)

items_list.html

% load static %
% load rest_framework %

% if items_info %
  % csrf_token %

  <table id="Items_Table" class="table">
    <thead>
      <tr>
       <th scope="col">Name</th>
       <th scope="col">Active</th>
       <th scope="col">Bought By</th>
      </tr>
    </thead>

    <tbody>
      % for pair in items_info %

        <tr scope="row">
          <td> pair.item.name </td>
          <td> pair.item.active </td>
          <td>
            <form action=" item.url " method="PATCH">
            % render_field pair.serializer.bought_by style=style %
            </form>
          </td>
        </tr>

      % endfor %
    </tbody>
  </table>

% else %
  <p class="text-center">No items to show.</p>
% endif %

如果有三个项目,将分别进行一个查询以获取 serializer.bought_by。我以为 Django/DRF 只会重用该值,但它正在查询每个循环。

尝试在响应中传递"serializer-bought-by": self.get_serializer().bought_by,我得到一个AttributeError: 'ItemSerializer' object has no attribute'bought_by'

打印我可以看到的序列化程序:

>>>print(self.get_serializer())
ItemSerializer(context='request': <rest_framework.request.Request object>, 'format': None, 'view': <myapp.views.ItemViewSet object>):
    url = HyperlinkedIdentityField(view_name='myapp:item-detail')
    name= CharField(unique=True, max_length=50)
    active = BooleanField(required=False)
    bought_by = SlugRelatedField(allow_null=True, queryset=<QuerySet [<Buyer: James>, <Buyer: John>, ...]>, required=False, slug_field='name')

有没有办法将 buy_by 传递给模板? 还是我必须使用JS;在循环外渲染表单字段并以某种方式克隆/复制?

---编辑---

根据 Endre 的要求:models.py

class Buyer(models.Model):
  name = models.CharField(unique=True, max_length = 20)

class Item(models.Model):
  name = models.CharField(unique=True, max_length = 50)
  active = models.BooleanField(default=True)
  bought_by = models.ForeignKey(Buyer, null=True, blank=True, to_field="name",)

【问题讨论】:

【参考方案1】:

您不能直接在序列化程序中访问字段的值。您应该改用serializer.data['bought_by']。更好的是,不要将序列化程序传递给模板,而是传递serializer.data。 至于模板中的查询,由于它已经包含在序列化程序数据中,因此不会对数据库进行查询以获取 bought_by。您只显示来自bought_by 的一个字段名称,该名称已预加载到序列化程序数据中。您应用的select_related 还确保在序列化期间不会进行多个查询以获取bought_by

【讨论】:

对不起,我最初的问题包含来自早期版本的非工作代码。如果我使用serializer.data,我会得到一个AttributeError: "noneType" object has no atribute '_field' 问题不在于已经定义了买家的商品,而在于那些没有买家的商品。我需要一个允许用户定义的 HTML 选择。 仅从这条评论很难理解。发布一个包含新问题详细信息的新问题

以上是关于减少为呈现具有相同内嵌可编辑字段的项目列表而进行的查询次数的主要内容,如果未能解决你的问题,请参考以下文章

Django Admin ForeignKey字段小部件选项和不一致的默认值

Django REST Framework:在列表响应中呈现表单元素

反应原生平面列表如何强制列表项具有相同的高度?

具有浮点形式输入的 django 整数模型字段

Yii2:在具有两个相同模型实例的表单中进行验证

具有可编辑字段的 FLTK 选择小部件