使用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的数据库列。调整后逻辑更清晰:
- 修改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后缀
- 修改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
相关产品推荐
相关产品推荐

