Django中通过Ajax获取外键字段值失败问题排查
我搞不懂为什么通过Ajax获取的外键值缺失,我只想通过外键ID拿到对应的名称,试了select_related()这类方法但没用。控制台日志里能看到supplier表和supplier_type表的数据,但只有supplier表的数据正常返回,supplier_type的名称没出来。我尝试用supplier_type__name也不管用,是不是我的实现哪里错了?
Ajax代码
$('.get_supplier_details').click(function (event) { var patient_id = $('#selected-patient').find(":selected").val(); $.ajax({ type: "POST", url: "{% url 'supplier-details' %}", data:{ supplier: supplier_id } }).done(function(data){ console.log(data); // }); });
views.py代码
from main.models import (Supplier, SupplierType) def patientdetails(request): supplier_id = request.POST.get('supplier') clients = Supplier.objects.filter(id=supplier_id ).select_related() qs_json = serializers.serialize('json', clients, fields=['first_name', 'middle_name', 'supplier_type__name']) return JsonResponse({'data': qs_json})
models.py代码
class SupplierType(models.Model): name = models.CharField(max_length=128, blank=True, null=True) is_active = models.BooleanField(default=True) created_at = models.DateTimeField(blank=True, null=True, auto_now_add=True) updated_at = models.DateTimeField(blank=True, null=True, auto_now=True) class Meta: managed = True db_table = 'supplier_type' class Supplier(models.Model): supplier_type = models.ForeignKey(SupplierType, models.DO_NOTHING) first_name = models.CharField(max_length=128, blank=True, null=True) middle_name = models.CharField(max_length=128, blank=True, null=True) last_name = models.CharField(max_length=128, blank=True, null=True) class Meta: managed = True db_table = 'supplier'
问题原因及解决方法
核心问题
Django自带的serializers.serialize()不支持supplier_type__name这种跨模型的字段写法,它只能序列化当前模型的直接字段,外键字段默认只会返回关联对象的ID,不会自动嵌套关联模型的其他字段。另外你代码里的select_related()没指定关联模型(虽然默认会关联所有外键,但不是问题关键)。
解决方案1:手动构建序列化字典(最简单)
直接手动提取需要的字段,包括外键关联的名称,不用自带的序列化器:
修改views.py:
from main.models import Supplier from django.http import JsonResponse def patientdetails(request): supplier_id = request.POST.get('supplier') # 用select_related指定关联supplier_type,减少数据库查询次数 supplier = Supplier.objects.filter(id=supplier_id).select_related('supplier_type').first() if supplier: # 手动组装需要的数据 data = { 'first_name': supplier.first_name, 'middle_name': supplier.middle_name, 'supplier_type_name': supplier.supplier_type.name } else: data = {} return JsonResponse({'data': data})
解决方案2:用Django REST Framework序列化器(更规范)
如果项目需要更复杂的序列化场景,推荐用DRF的序列化类来处理关联字段:
- 先安装DRF(如果没装):
pip install djangorestframework
- 在
settings.py里添加rest_framework到INSTALLED_APPS:
INSTALLED_APPS = [ # ...其他应用 'rest_framework', ]
- 创建
serializers.py:
from rest_framework import serializers from main.models import Supplier, SupplierType class SupplierTypeSerializer(serializers.ModelSerializer): class Meta: model = SupplierType fields = ['name'] class SupplierSerializer(serializers.ModelSerializer): # 嵌套关联模型的序列化器 supplier_type = SupplierTypeSerializer() class Meta: model = Supplier fields = ['first_name', 'middle_name', 'supplier_type']
- 修改
views.py:
from main.models import Supplier from .serializers import SupplierSerializer from django.http import JsonResponse def patientdetails(request): supplier_id = request.POST.get('supplier') supplier = Supplier.objects.filter(id=supplier_id).select_related('supplier_type').first() if supplier: serializer = SupplierSerializer(supplier) data = serializer.data else: data = {} return JsonResponse({'data': data})
额外注意点
你的Ajax代码里有个变量名错误:定义的是patient_id,但传给后端的是supplier_id,要改成一致;另外Django的POST请求需要带CSRF Token,否则会被拦截,补充上:
修改Ajax代码:
$('.get_supplier_details').click(function (event) { var supplier_id = $('#selected-patient').find(":selected").val(); // 统一变量名 $.ajax({ type: "POST", url: "{% url 'supplier-details' %}", data: { supplier: supplier_id }, // 加入CSRF Token headers: { 'X-CSRFToken': $('input[name=csrfmiddlewaretoken]').val() } }).done(function(data){ console.log(data); }); });
内容的提问来源于stack exchange,提问作者BootCamp
相关产品推荐
相关产品推荐

