django 字段是必需的,如果它未定义为必需字段

yshpjwxd  于 2023-05-30  发布在  Go
关注(0)|答案(1)|浏览(175)

我有以下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,但我已经在我的模型中声明了它不是必需的,所以我不确定我错过了什么。

6jjcrrmo

6jjcrrmo1#

主要问题是序列化器,其中imagechildren字段没有指定是否需要这些,如果不需要,则默认情况下需要。对于children,如果我理解正确,这些将仅用于读取,因此添加read_only=…以防止在RecursiveField中使用这些:

class ComponentSerializer(serializers.ModelSerializer):
    children = RecursiveField(many=True, read_only=True, required=False)
    image = ImageSerializerField(required=False)

    class Meta:
        model = Component
        fields = '__all__'

RecursiveField的实现也可能不适用于所有情况,例如没有many=True,这肯定会失败,因为parent没有parent。你可能想使用django-rest-framework-recursive [GitHub],它代理大多数属性[GitHub]。
最后,序列化器做了太多的事情。你所做的大部分都是样板文件。例如,移除所有子项和子项等。将由Django的ORM处理,并且以更有效的方式处理。通过这样做,您也会使以后执行适当的身份验证、授权、限制等变得更加困难。ModelViewSet已经有了获取列表、特定项等的样板逻辑。

class ComponentViewSet(viewsets.ModelViewSet):
    queryset = Component.objects.all()
    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(parent=None)

这就是我们所需要的。

相关问题