Django视图开发:实现返回JSON/JSONP格式的国家数据
关于Django视图返回JSON/JSONP的测试与实现优化建议
我来帮你梳理下这两个问题的解决方案和优化点:
1. 测试视图返回数据的标准方法
测试Django视图的核心是模拟请求并验证响应,这里有几种常用的标准方法:
- 使用Django自带的TestCase框架:这是最规范的自动化测试方式,能覆盖大部分场景,示例代码如下:
from django.test import TestCase, Client import json class CountryViewTest(TestCase): def setUp(self): # 预先创建测试用的大洲和国家数据 self.continent = Continent.objects.create(code='EU', name='Europe') self.country = Country.objects.create( code='FI', name='Finland', continent=self.continent, area=337030, population=5244000, capital='Helsinki' ) self.client = Client() def test_country_json_response(self): response = self.client.get('/your/view/path/EU/FI/') # 断言响应状态码正常 self.assertEqual(response.status_code, 200) # 断言内容类型为JSON self.assertEqual(response['Content-Type'], 'application/json') # 解析响应并验证数据准确性 data = json.loads(response.content) self.assertEqual(data['area'], 337030) self.assertEqual(data['capital'], 'Helsinki') def test_country_jsonp_response(self): response = self.client.get('/your/view/path/EU/FI/?callback=myCallbackFunction') self.assertEqual(response.status_code, 200) self.assertEqual(response['Content-Type'], 'application/javascript') # 验证JSONP格式是否正确 self.assertIn('myCallbackFunction({', response.content.decode())
手动测试工具:
- 用
curl命令快速验证:比如curl http://localhost:8000/your/view/path/EU/FI/,或者带callback参数的curl http://localhost:8000/your/view/path/EU/FI/?callback=testFunc - 用Postman、Insomnia等GUI工具,直观发送请求并查看响应格式和数据。
- 用
如果使用Django REST Framework:可以用DRF提供的
APITestCase和APIClient,更便捷地处理序列化和API场景的测试。
2. 当前实现的问题与优化建议
你的当前代码存在不少需要修正的细节,我逐一拆解:
首先是Country模型的严重错误
你在模型的__init__方法里定义字段是完全错误的!Django模型的字段必须定义为类属性,否则ORM无法识别这些字段,数据库迁移时也不会创建area、population、capital这些列。正确的模型定义应该是:
class Country(models.Model): name = models.CharField(max_length=60, default='', unique=True) code = models.CharField(max_length=3, default='', unique=True) continent = models.ForeignKey(Continent, related_name='countries', on_delete=models.CASCADE) # 把字段移到类属性层面! area = models.PositiveIntegerField(default=0) population = models.PositiveIntegerField(default=0) capital = models.CharField(max_length=60, default='') def __str__(self): return f'{self.name} {self.code} {self.capital} {self.population} {self.area} {self.continent}' class Meta: ordering = ["name"] verbose_name_plural = "countries"
注意:Django 2.0+中外键必须指定
on_delete参数,比如on_delete=models.CASCADE
视图实现的不良/错误实践
- 遍历所有Country匹配code效率极低:当数据库中有大量国家数据时,循环遍历的方式会严重拖慢响应速度。正确做法是直接用ORM查询:
try: # 同时过滤大洲和国家代码,逻辑更严谨 country = Country.objects.get(code=country_code, continent__code=continent_code) except Country.DoesNotExist: return HttpResponse(status=404)
- 手动构造序列化字典易出错:手动提取字段容易遗漏或写错键名,建议用Django自带序列化工具或DRF序列化器。比如用
django.core.serializers:
from django.core.serializers import serialize # 只序列化需要的字段 serialized_data = serialize('json', [country], fields=('area', 'population', 'capital')) # 转换为字典格式 data_dict = json.loads(serialized_data)[0]['fields'] json_str = json.dumps(data_dict, indent=4)
- 缺少JSONP处理逻辑:需要检查请求的GET参数中是否存在
callback,如果有就把JSON数据包裹在回调函数里,并修改内容类型:
callback = request.GET.get('callback') if callback: response_content = f"{callback}({json_str})" return HttpResponse(response_content, content_type="application/javascript") else: return HttpResponse(json_str, content_type="application/json")
- 初始代码的错误操作:你最初的代码里直接
country = Country(code=country_code)然后save(),这会创建一个新的国家对象存入数据库,完全不符合“查询已有数据”的需求,一定要删除这类代码。
优化后的完整视图示例
import json from django.http import HttpResponse from django.core.serializers import serialize from .models import Country def country_json(request, continent_code, country_code): try: country = Country.objects.get(code=country_code, continent__code=continent_code) except Country.DoesNotExist: return HttpResponse(status=404) # 序列化指定字段 serialized_data = serialize('json', [country], fields=('area', 'population', 'capital')) data_dict = json.loads(serialized_data)[0]['fields'] json_str = json.dumps(data_dict, indent=4) # 处理JSONP请求 callback = request.GET.get('callback') if callback: response_content = f"{callback}({json_str})" return HttpResponse(response_content, content_type="application/javascript") else: return HttpResponse(json_str, content_type="application/json")
内容的提问来源于stack exchange,提问作者Grak
相关产品推荐
相关产品推荐

