You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用django-ninja-extra创建含ForeignKey记录时遇ValueError的解决方法

问题:Django-Ninja-Extra创建含ForeignKey的Employee记录报错

使用django-ninja-extra开发API时,创建包含ForeignKey的employee记录时触发以下错误:

ValueError: Cannot assign "1": "employee.department_id" must be a "department" instance.

相关代码及请求示例如下:

models.py

# Create your models here.
from django.db import models
from django.conf import settings


class department(models.Model):
    name = models.CharField(max_length=50)
    last_modified = models.DateTimeField(auto_now=True, auto_now_add=False)
class employee(models.Model):
    name = models.CharField(max_length=50)
    last_modified = models.DateTimeField(auto_now=True, auto_now_add=False)
    department_id = models.ForeignKey(department, on_delete=models.CASCADE)

api.py

from ninja_extra import api_controller, route, NinjaExtraAPI, permissions
from django.shortcuts import get_object_or_404
from ninja import Schema, constants
from datetime import datetime
from .models import department, employee
from typing import List
from ninja_jwt.controller import NinjaJWTDefaultController
from ninja_jwt.authentication import JWTAuth

api = NinjaExtraAPI()

class DepartmentIn(Schema):
    name: str
    last_modified: datetime

class DepartmentOut(Schema):
    id: int
    name: str
    last_modified: datetime

class EmployeeIn(Schema):
    name: str
    last_modified: datetime
    department_id: int

class EmployeeOut(Schema):
    id: int
    name: str
    last_modified: datetime

@api_controller('', permissions=[])
class ForeignkeyhandlingController:
  
    @route.post("department/create", auth=constants.NOT_SET)
    def create_department(self, payload: DepartmentIn):
        department.objects.create(**payload.dict())

    @route.post("employee/create", auth=constants.NOT_SET)
    def create_employee(self, payload: EmployeeIn):
        employee.objects.create(**payload.dict())

api.register_controllers(NinjaJWTDefaultController, ForeignkeyhandlingController)

请求示例

curl -X 'POST' \
  'http://127.0.0.1:8000/api/employee/create' \
  -H 'accept: */*' \
  -H 'Content-Type: application/json' \
  -d '{
  "name": "string",
  "last_modified": "2024-12-12T16:44:33.897Z",
  "department_id": 1
}'

解决方案

错误核心原因:Django的ForeignKey字段,当模型中定义的字段名为department_id时,它实际期望接收的是department模型实例,而非整数ID。要实现传入整数ID创建记录,有两种处理方式:

方法一:修改模型字段名(推荐,符合Django规范)

Django中定义ForeignKey时,字段名无需加_id后缀,框架会自动为该字段生成字段名_id的数据库列。调整后逻辑更清晰:

  1. 修改models.py中的employee模型:
class employee(models.Model):
    name = models.CharField(max_length=50)
    last_modified = models.DateTimeField(auto_now=True, auto_now_add=False)
    department = models.ForeignKey(department, on_delete=models.CASCADE)  # 去掉_id后缀
  1. 修改api.py中的创建逻辑:
class EmployeeIn(Schema):
    name: str
    last_modified: datetime
    department_id: int  # 保持请求参数名为department_id不变

@api_controller('', permissions=[])
class ForeignkeyhandlingController:
    # ... 保留其他方法
    @route.post("employee/create", auth=constants.NOT_SET)
    def create_employee(self, payload: EmployeeIn):
        payload_dict = payload.dict()
        # 取出ID并转换为department实例
        dept_id = payload_dict.pop('department_id')
        dept = get_object_or_404(department, id=dept_id)
        employee.objects.create(department=dept, **payload_dict)

方法二:不修改模型,直接处理ID赋值

如果不想改动模型字段名,只需在创建记录时手动将整数ID转为模型实例:

修改api.py中的create_employee方法:

@route.post("employee/create", auth=constants.NOT_SET)
def create_employee(self, payload: EmployeeIn):
    payload_dict = payload.dict()
    dept_id = payload_dict.pop('department_id')
    dept = get_object_or_404(department, id=dept_id)
    employee.objects.create(department_id=dept, **payload_dict)

额外提示

Django模型类名建议使用大驼峰命名(如Department而非department、Employee而非employee),这是通用编码规范,能避免潜在的命名冲突。

内容的提问来源于stack exchange,提问作者Robin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 14:22:19