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



如何使用Django REST 框架来渲染具有用户可编辑的特定字段的模型实例列表?

我进入Django几个月,进入DRF只有几天。我已经尝试了几种不同的方法,但似乎无法理解它。

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

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

然后,我可以通过 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)

模板.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")

然后在模板中.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)

从来没有完全弄清楚这是序列化程序。ItemSerializer(item, data=request.data) 似乎不是字典项,因此无法使用 data.items() 在模板中遍历它。

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

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

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

引用:

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

Django Rest Framework 序列化程序单独呈现表单

使用 django rest 框架的 ModelViewSet 类渲染表单 APIView 的 insead

已解决

方法二最接近:有条件地选择一个渲染器并覆盖默认的 .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 后缀。 如果是这样,它将序列化查询集中的每个项目,并在发送到items_list.html的字典(上下文)中包含 {serializer:item} 字典的列表。

为了render_field,DRF 需要定义样式。

如果格式后缀不是 html,或者未指定,它将优先考虑 JSON 渲染器或可浏览 API 渲染器(默认渲染器)。 这样,我们的可浏览 API 和 JSON API 仍然可以轻松工作。

要使用每个实例的bought_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/等请求时,请包含 Django 文档中描述的 csrf 令牌。

最新更新