Django非REST框架API中无法添加外键关联的联系电话
解决Django API添加外键关联联系电话的问题
我最近在搞一个不用REST Framework的Django API,遇到个头疼的问题:POST新增联系人时,除了外键关联的联系电话(想支持加一个或者多个),其他数据都能正常进数据库,唯独联系电话死活加不上。折腾了半天,终于找到问题所在,给大家分享下解决方案:
先看我之前的错误代码
views.py
@method_decorator(csrf_exempt) def phonebook_list(request): if request.method == 'GET': phonebooklist = PhoneBook.objects.all() serialized_data = [pb.to_json() for pb in phonebooklist] return JsonResponse(serialized_data, safe=False) elif request.method == 'POST': data= request.body.decode('utf8') data= json.loads(data) try: new_contact=PhoneBook(name=data["name"], address=data["address"], email=data["email"], note=data["note"]) new_contact.save() new_contact_number=ContactNumber( contact_number=data["contact_number"], #No Contact Numbers Added # It should add one or more contact numbers number_id=data[PhoneBook.id] #It should add the contact number/s to the contact name added together ) contact_number.save() return JsonResponse({"created": data}, safe= False) except: return JsonResponse({"error":"not valid data"}, safe=False)
models.py
class PhoneBook(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=100, default='address') email = models.CharField(max_length=50, default='email') note = models.CharField(max_length=100, default='note') def to_json(self): contact_numbers = [c.contact_number for c in self.contact_numbers.all()] return { 'name': self.name, 'email': self.email, 'address': self.address, 'note': self.note, 'contact_numbers': contact_numbers } def __str__(self): return self.name class ContactNumber(models.Model): number = models.ForeignKey(PhoneBook, related_name="contact_numbers") contact_number= models.CharField(max_length=30) def __str__(self): return self.contact_number
问题出在哪?
- 外键关联错误:
number_id=data[PhoneBook.id]这写法完全错了!PhoneBook.id是模型的字段类,根本不是请求数据里的键,应该用刚保存的new_contact.id——毕竟new_contact.save()之后就有主键ID了,直接关联这个ID才对。 - 变量名写错+不支持多个电话:创建的对象是
new_contact_number,但保存的却是contact_number.save(),这直接会报变量未定义的错误;而且代码只处理单个电话,没法支持多个。 - 异常处理太敷衍:直接一个
except:捕获所有异常,连具体错在哪都不知道,调试起来全靠猜。
修复后的正确代码
先补全models.py的ForeignKey参数(Django2.0+必须加on_delete)
class ContactNumber(models.Model): # 加上on_delete参数,这里用CASCADE表示删除联系人时同步删除电话 number = models.ForeignKey(PhoneBook, related_name="contact_numbers", on_delete=models.CASCADE) contact_number= models.CharField(max_length=30) def __str__(self): return self.contact_number
重写后的views.py(改用类视图更规范,也兼容函数视图写法)
import json from django.views.decorators.csrf import csrf_exempt from django.http import JsonResponse from .models import PhoneBook, ContactNumber from django.utils.decorators import method_decorator from django.views import View @method_decorator(csrf_exempt, name='dispatch') class PhoneBookListView(View): def get(self, request): phonebooklist = PhoneBook.objects.all() serialized_data = [pb.to_json() for pb in phonebooklist] return JsonResponse(serialized_data, safe=False) def post(self, request): try: # 解析请求体 data = json.loads(request.body.decode('utf8')) # 创建主联系人:用objects.create更简洁,用get带默认值避免可选字段未传报错 new_contact = PhoneBook.objects.create( name=data["name"], address=data.get("address", "address"), email=data.get("email", "email"), note=data.get("note", "note") ) # 处理联系电话:兼容单个字符串和列表两种输入 contact_numbers = data.get("contact_numbers", []) if isinstance(contact_numbers, str): contact_numbers = [contact_numbers] # 批量创建联系电话,效率更高,还能过滤空号码 number_objects = [ ContactNumber(contact_number=num.strip(), number=new_contact) for num in contact_numbers if num.strip() ] ContactNumber.objects.bulk_create(number_objects) # 返回包含联系电话的完整创建结果 return JsonResponse(new_contact.to_json(), safe=False) # 捕获必填字段缺失的错误 except KeyError as e: return JsonResponse({"error": f"缺少必填字段: {str(e)}"}, status=400) # 捕获其他异常,返回具体错误信息 except Exception as e: return JsonResponse({"error": f"创建失败: {str(e)}"}, status=500)
测试用的请求体示例
Postman里选POST,请求体用JSON格式,支持单个或多个电话:
多个电话的情况
{ "name": "张三", "address": "北京市朝阳区", "email": "zhangsan@example.com", "note": "好朋友", "contact_numbers": ["13800138000", "13900139000"] }
单个电话的情况
{ "name": "李四", "address": "上海市浦东新区", "email": "lisi@example.com", "note": "同事", "contact_numbers": "13700137000" }
这样修改后,联系电话就能正常关联到对应的联系人,不管是一个还是多个都能存入数据库啦~
内容的提问来源于stack exchange,提问作者bossajie
相关产品推荐
相关产品推荐

