我有以下Django模型
class Component(models.Model):
parent = models.ForeignKey(
"self", on_delete=models.CASCADE, null=True, blank=True, related_name="children"
)
vessel = models.ForeignKey(
Vessel, on_delete=models.CASCADE, related_name="components"
)
name = models.CharField(max_length=100)
manufacturer = models.CharField(max_length=255, null=True, blank=True)
model = models.CharField(max_length=255, null=True, blank=True)
type = models.CharField(max_length=255, null=True, blank=True)
serial_number = models.CharField(max_length=255, null=True, blank=True)
supplier = models.CharField(max_length=255, null=True, blank=True)
description = models.TextField(null=True, blank=True)
image = models.ImageField(upload_to="component_images", blank=True, null=True)
def __str__(self):
return self.name
视图集如下所示
class ComponentViewSet(viewsets.ModelViewSet):
serializer_class = ComponentSerializer
def get_queryset(self):
queryset = Component.objects.all()
vessel_id = self.kwargs.get("vessel_id", None)
if vessel_id is not None:
queryset = queryset.filter(vessel_id=vessel_id)
queryset = queryset.filter(Q(parent=None) | Q(parent__isnull=True))
return queryset
def retrieve(self, request, pk=None, vessel_id=None):
queryset = Component.objects.all()
component = get_object_or_404(queryset, pk=pk, vessel_id=vessel_id)
serializer = ComponentSerializer(component)
return Response(serializer.data)
def update(self, request, pk=None, vessel_id=None, partial=True):
queryset = Component.objects.all()
component = get_object_or_404(queryset, pk=pk, vessel_id=vessel_id)
serializer = ComponentSerializer(component, data=request.data, partial=partial)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@action(detail=True, methods=["delete"])
def delete_component(self, request, pk=None, vessel_id=None):
queryset = Component.objects.all()
component = get_object_or_404(queryset, pk=pk, vessel_id=vessel_id)
# Recursively delete all children of the component
self._delete_children(component)
# Delete the component itself
component.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
def _delete_children(self, component):
children = component.children.all()
for child in children:
self._delete_children(child)
child.delete()
串行器:
class ImageSerializerField(serializers.Field):
def to_representation(self, value):
if not value:
return None
if settings.MEDIA_URL in value.url:
return (
settings.BASE_URL
+ settings.MEDIA_URL
+ value.url[len(settings.MEDIA_URL) :]
)
return value.url
def to_internal_value(self, data):
return data
class RecursiveField(serializers.Serializer):
def to_representation(self, value):
serializer = self.parent.parent.__class__(value, context=self.context)
return serializer.data
class ComponentSerializer(serializers.ModelSerializer):
children = RecursiveField(many=True)
image = ImageSerializerField()
class Meta:
model = Component
fields = "__all__"
这是我创建组件的简单React表单
import React, { useState } from "react";
import { api } from "../../../../../userAuth/auth";
import { useParams } from "react-router";
const ComponentData = () => {
const { vessel_id } = useParams();
const [formData, setFormData] = useState({
vessel: vessel_id,
name: "",
manufacturer: "",
model: "",
type: "",
serial_number: "",
supplier: "",
description: "",
image: null,
});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData((prevFormData) => ({
...prevFormData,
[name]: value,
}));
};
const handleImageChange = (e) => {
const file = e.target.files[0];
setFormData((prevFormData) => ({
...prevFormData,
image: file,
}));
};
const handleSubmit = async (e) => {
e.preventDefault();
try {
const { children, ...data } = formData; // Exclude the 'children' field
const response = await api.post(
`/maintenance/${vessel_id}/components/`,
data
);
// TODO: Handle successful creation (e.g., show success message, redirect, etc.)
} catch (error) {
console.error("Error creating component:", error);
// TODO: Handle error (e.g., show error message, etc.)
}
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
required
/>
</label>
<br />
<label>
Manufacturer:
<input
type="text"
name="manufacturer"
value={formData.manufacturer}
onChange={handleChange}
/>
</label>
<br />
<label>
Model:
<input
type="text"
name="model"
value={formData.model}
onChange={handleChange}
/>
</label>
<br />
<label>
Type:
<input
type="text"
name="type"
value={formData.type}
onChange={handleChange}
/>
</label>
<br />
<label>
Serial Number:
<input
type="text"
name="serial_number"
value={formData.serial_number}
onChange={handleChange}
/>
</label>
<br />
<label>
Supplier:
<input
type="text"
name="supplier"
value={formData.supplier}
onChange={handleChange}
/>
</label>
<br />
<label>
Description:
<textarea
name="description"
value={formData.description}
onChange={handleChange}
/>
</label>
<br />
<label>
Image:
<input
type="file"
name="image"
accept="image/*"
onChange={handleImageChange}
/>
</label>
<br />
<button type="submit">Create Component</button>
</form>
);
};
export default ComponentData;
每当我发送帖子请求时,我都会收到错误的请求。当我检查请求控制台里面有这个。
response
:
config
:
{transitional: {…}, adapter: Array(2), transformRequest: Array(1), transformResponse: Array(1), timeout: 0, …}
data
:
children
:
['This field is required.']
[[Prototype]]
:
Object
我假设它是在谈论递归字段parent,但我已经在我的模型中声明了它不是必需的,所以我不确定我错过了什么。
1条答案
按热度按时间6jjcrrmo1#
主要问题是序列化器,其中
image
和children
字段没有指定是否需要这些,如果不需要,则默认情况下需要。对于children
,如果我理解正确,这些将仅用于读取,因此添加read_only=…
以防止在RecursiveField
中使用这些:RecursiveField
的实现也可能不适用于所有情况,例如没有many=True
,这肯定会失败,因为parent
没有parent
。你可能想使用django-rest-framework-recursive
[GitHub],它代理大多数属性[GitHub]。最后,序列化器做了太多的事情。你所做的大部分都是样板文件。例如,移除所有子项和子项等。将由Django的ORM处理,并且以更有效的方式处理。通过这样做,您也会使以后执行适当的身份验证、授权、限制等变得更加困难。
ModelViewSet
已经有了获取列表、特定项等的样板逻辑。这就是我们所需要的。