【问题标题】:Django REST Framework: rendering form elements in list responseDjango REST Framework:在列表响应中呈现表单元素
【发布时间】:2019-09-07 06:48:06
【问题描述】:

如何使用 Django REST Framework 来呈现具有用户可编辑的特定字段的模型实例列表?

我使用 Django 几个月了,而 DRF 只用了几天。我尝试了几种不同的方法,但似乎无法理解它。

在使用 DRF 之前,我的工作流程是设置一个视图(和关联的 URL):查询我的模型,从 forms.py 中选择我的自定义表单(仅公开我需要的字段),将两者放在一起到字典中,并将其发送到模板。

然后,我可以在模板中循环遍历我的模型实例并设置我的可编辑字段,并根据需要通过 django 清晰的表单将它们传递给它们。

然后我可以通过 AJAX 获取请求调用此模板。

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",)

views.py

class ItemViewSet(viewsets.ModelViewSet):
  queryset = models.Item.objects.select_related("bought_by")
  serializer_class= serializers.ItemSerializer
  filterset_fields = ("bought_by")

序列化器.py

class ItemSerializer(serializers.HyperlinkedModelSerializer):
  class Meta:
    model = models.Item
    fields = "__all__"
    extra_kwargs = {"url": {"view_name: "myapp:item-detail"}}

urls.py

router = routers.DefaultRouter()
router.register(r"items", views.ItemViewSet)

模板.html

{% load static %}
{% load rest_framework %}

<table id="item_table" class="table">
  <thead>
    <tr>
      <th scope="col">Name</th>
      <th scope="col">Active</th>
      <th scope="col">Buyer</th>
    </tr>
  </thead>

  <tbody>
    {% for item in data %}
      <tr scope="row">
        <td>{{ item.name }}</td>
        <td>{{ item.active }}</td>
        <td>{{ item.bought_by }}</td>
      </tr>
    {% endfor %}
  </tbody>
</table>

一些Js文件

function getData(){
  updateRequest = $.ajax({
    type: "GET",
    url: "myapp/items/",
    success: function(data) {
      //....
    }
  });
}

第一种方法: 为 ItemViewSet 自定义 ListModelMixin 以呈现模板并传递序列化程序。大致如下:

def list(self,request, *args, **kwargs):
  ...
  return Response ({'data': queryset, 'serializer': serializer}, template_name = "myapp/template.html")

然后在 template.html 中将 {{ item.active }} 更改为:

<form action="{{ item.url }}" method="post">
  {% csrf_token %}
  {{ render_form serializer }}
</form>

错误:序列化程序不可迭代。有道理。改为:

{{ render_field item.bought_by }}

错误:需要“风格”,补充说。继续得到其他错误

第二种方法: 尝试修改 ListModelMixin 以收集序列化模型实例的字典,例如:

items= dict()
        for item in queryset:
            items[item] = serializers.ItemSerializer(item, data=request.data)

从未完全弄清楚这一点,因为 serializers.ItemSerializer(item, data=request.data) 似乎不是字典项,因此不能使用 data.items() 在模板中对其进行迭代。

很抱歉写了这么长的文章,但我有点不知所措,不太确定如何继续。

返回所有模型实例列表的最优雅的 DRF 方式是什么?

我总是可以使用较旧的方法,但感觉这里的 DRF 缺少一些明显的东西。

参考资料:

https://www.django-rest-framework.org/topics/html-and-forms/

Django Rest Framework serializers render form individually

Rendering a form with django rest framework's ModelViewSet class insead of APIView

【问题讨论】:

    标签: django django-rest-framework django-forms django-templates django-views


    【解决方案1】:

    已解决

    方法二最接近:有条件地选择一个渲染器并覆盖默认的 .list()。

    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(ticket), "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)
    

    这将检查请求的 url 是否有 /?format=html suffix。如果是这样,它将序列化查询集中的每个项目,并在发送到 items_list.html 的字典(上下文)中包含 {serializer:item} 字典列表。

    为了渲染字段,DRF 需要定义一个style

    如果格式后缀不是 html,或者没有指定,则优先使用 JSON 渲染器或 BrowsableAPI 渲染器(default renderers)。这样,我们的可浏览 API 和 JSON api 仍然可以轻松工作。

    要使用每个实例可编辑的 buy_by 字段来呈现此内容,请按照以下几行修改您的模板(在本例中为 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="{% url "myapp:item-detail" pair.item.pk %}" 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 %}
    

    现在,在您的 GET 请求中,只需将“/?format=html”附加到 URL。

    如果您使用 AJAX,则在发送 POST/PUT/PATCH/等时。请求,include the csrf token as described in the Django documentation

    【讨论】:

    • 这有助于在模板中手动布局表单,而不是依赖于直接的 DRF 垂直/水平/等选项。 +1
    猜你喜欢
    • 2016-05-06
    • 2018-10-20
    • 2018-06-18
    • 2013-01-27
    • 2020-07-20
    • 1970-01-01
    • 2018-05-06
    • 2018-12-30
    • 1970-01-01
    相关资源
    最近更新 更多