vitalik / django-ninja

💨 Fast, Async-ready, Openapi, type hints based framework for building APIs
https://django-ninja.dev
MIT License
7.03k stars 421 forks source link

[BUG] use put method can not parser form-data #1296

Open pangxiaobin opened 2 weeks ago

pangxiaobin commented 2 weeks ago

Describe the bug A clear and concise description of what the bug is.

model.py

class Department(models.Model): title = models.CharField(max_length=100)

class Employee(models.Model): first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) department = models.ForeignKey(Department, on_delete=models.CASCADE, null=True, blank=True) birthdate = models.DateField(null=True, blank=True) cv = models.FileField(upload_to='cvs', null=True, blank=True)

views.py

class EmployeeIn(Schema):
    first_name: str
    last_name: str
    department_id: Union[int, str, None] = Field(
        default=None, description="Department ID", exclude_none=True
    )
    birthdate: Union[date, str, None] = Field(
        default=None, description="Birthdate in the format yyyy-mm-dd", exclude_none=True
    )

    @field_validator('department_id', 'birthdate')
    @classmethod
    def empty_str_to_none(cls, v):
        if v == '':
            return None
        return v

@router.put("/employees/{employee_id}")
def update_employee(
        request, employee_id: int, payload: Form[EmployeeIn], cv: UploadedFile = File(None)
):
    employee = get_object_or_404(Employee, id=employee_id)
    for attr, value in payload.dict().items():
        setattr(employee, attr, value)
    if cv:
        employee.cv.save(cv.name, cv)  # will save model instance as well
    else:
        employee.save()
    return {"success": True}

when is use this curl -X 'PUT' \ 'http://127.0.0.1:8000/api/employees/1' \ -H 'accept: /' \ -H 'Content-Type: multipart/form-data' \ -F 'first_name=2' \ -F 'last_name=2' \ -F 'department_id=' \ -F 'birthdate=' \ -F 'cv=' this @router.put("/employees/{employee_id}") method raise

Error: Unprocessable EntityResponse bodyDownload{   "detail": [     {       "type": "missing",       "loc": [         "form",         "first_name"       ],       "msg": "Field required"     },     {       "type": "missing",       "loc": [         "form",         "last_name"       ],       "msg": "Field required"     }   ] }
--

use @router.post("/employees/{employee_id}") will work fine

Versions (please complete the following information):

Note you can quickly get this by runninng in ./manage.py shell this line:

import django; import pydantic; import ninja; django.__version__; ninja.__version__; pydantic.__version__