英文:
Field is required, when it's not defined as so
问题
我有以下的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;
每当我发送POST请求时,我收到Bad Request错误。当我检查请求控制台时,有这个错误信息。
{
"children": ["This field is required."]
}
我认为它是在谈论递归字段parent,但我已经在我的模型中声明它不是必需的,所以我不确定我漏掉了什么。
英文:
I have the following Django model
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
and the viewset looks like this
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()
The serializer:
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__"
This is my simple React form to create a component
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;
Whenever I send the post request I receive Bad request. When I check the request console inside there is this.
response
:
config
:
{transitional: {…}, adapter: Array(2), transformRequest: Array(1), transformResponse: Array(1), timeout: 0, …}
data
:
children
:
['This field is required.']
[[Prototype]]
:
Object
I assume it's talking about the recursive field parent, but I already stated in my model it's not required so I am not sure what I am missing.
答案1
得分: 1
以下是您要翻译的内容:
"The main problem is the serializer, where your image
and children
field did not specify if these are required, and if not, by default they are. For the children
, if I understand it correctly, these will only be used to read, so add <code>read_only=…</code> to prevent to use these in the RecursiveField
:
<pre><code>class ComponentSerializer(serializers.ModelSerializer):
children = RecursiveField(many=True, <b>read_only=True, required=False</b>)
image = ImageSerializerField(<b>required=False</b>)
class Meta:
model = Component
fields = '__all__'</code></pre>
The implementation for the RecursiveField
also likely does not work for all cases, for example without a many=True
, this will definitely fail since there is no parent
for the parent
. You might want to use django-rest-framework-recursive
<sup>[GitHub]</sup>, which proxies most attributes <sup>[GitHub]</sup>.
Finally the serializer does way too much. Most of what you do is boilerplate. For example removing all the children and subchildren, etc. will be handled by Django's ORM, and in a more efficient manner. By doing this, you also make it harder later to perform proper authentication, authorization, throttling, etc. The ModelViewSet
already has boilerplate logic to get a list, a specific item, etc.
<pre><code>class ComponentViewSet(viewsets.ModelViewSet):
<b>queryset = Component objects.all()</b>
serializer_class = ComponentSerializer
def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs)
vessel_id = self.kwargs.get('vessel_id')
if vessel_id is not None:
queryset = queryset.filter(vessel_id=vessel_id)
return queryset.filter(<b>parent=None</b>)</code></pre>
that is all we need."
英文:
The main problem is the serializer, where your image
and children
field did not specify if these are required, and if not, by default they are. For the children
, if I understand it correctly, these will only be used to read, so add <code>read_only=…</code> to prevent to use these in the RecursiveField
:
<pre><code>class ComponentSerializer(serializers.ModelSerializer):
children = RecursiveField(many=True, <b>read_only=True, required=False</b>)
image = ImageSerializerField(<b>required=False</b>)
class Meta:
model = Component
fields = '__all__'</code></pre>
The implementation for the RecursiveField
also likely does not work for all cases, for example without a many=True
, this will definitely fail since there is no parent
for the parent
. You might want to use django-rest-framework-recursive
<sup>[GitHub]</sup>, which proxies most attributes <sup>[GitHub]</sup>.
Finally the serializer does way too much. Most of what you do is boilerplate. For example removing all the children and subchildren, etc. will be handled by Django's ORM, and in a more efficient manner. By doing this, you also make it harder later to perform proper authentication, authorization, throttling, etc. The ModelViewSet
already has boilerplate logic to get a list, a specific item, etc.
<pre><code>class ComponentViewSet(viewsets.ModelViewSet):
<b>queryset = Component.objects.all()</b>
serializer_class = ComponentSerializer
def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs)
vessel_id = self.kwargs.get('vessel_id')
if vessel_id is not None:
queryset = queryset.filter(vessel_id=vessel_id)
return queryset.filter(<b>parent=None</b>)</code></pre>
that is all we need.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论