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

时间:2019-04-17 05:29:34

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

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

我在Django中工作了几个月,而在DRF中只有几天。我尝试了几种不同的方法,但似乎无法将其包裹住。

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

然后在模板中,我可以遍历模型实例并设置我的可编辑字段,并根据需要将它们通过django crispy表单进行管道传递。

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

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

serializers.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)

template.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()对其进行迭代。 / p>

很抱歉写了很长时间,但是我有点不知所措,还不确定如何进行。

返回所有模型实例具有可编辑字段的列表的最优雅的DRF方法是什么(类似于在django admin中指定list_editable的方式)?

我总是可以使用较旧的方法,但是感觉好像我在这里缺少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

1 个答案:

答案 0 :(得分:0)

已解决

方法二是最接近的:有条件地选择一个渲染器并覆盖默认的.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请求中,只需在URL后面附加“ /?format = html”。

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