Django 分页查询返回 JSON 数据的中文乱码解决方案
问题背景
在 Django 开发中,经常需要实现分页查询并返回 JSON 格式数据。当数据包含中文时,容易遇到乱码问题。下面提供一个完整的分页查询示例:
# coding=UTF-8
import os
from django.core import serializers
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.http import HttpResponse
from mypage.models import Product
def get_products(request):
product_list = Product.objects.all()
page_size = 10
paginator = Paginator(product_list, page_size)
page_number = request.GET.get('page', 1)
try:
current_page = paginator.page(page_number)
except PageNotAnInteger:
current_page = paginator.page(1)
except EmptyPage:
current_page = paginator.page(paginator.num_pages)
json_result = serializers.serialize("json", current_page, ensure_ascii=False)
return HttpResponse(json_result, content_type='application/json; charset=utf-8')
中文乱码问题的关键在于 serializers.serialize() 方法中的 ensure_ascii=False 参数。这个参数告诉 JSON 序列化器不要将非 ASCII 字符转义为 Unicode 转义序列。
深入理解 Django 序列化
Django 的序列化框架提供了一种将模型实例转换为其他格式(通常是文本格式)的机制,便于数据传输。序列化相关代码位于 django.core.serializers 包中。
核心函数原型
serialize() 函数定义如下:
def serialize(format, queryset, **options):
"""
使用指定序列化器序列化查询集(或任何返回数据库对象的迭代器)
"""
serializer = get_serializer(format)()
serializer.serialize(queryset, **options)
return serializer.getvalue()
Serializer 基类:
class Serializer(object):
"""序列化器抽象基类"""
internal_use_only = False
def serialize(self, queryset, **options):
# 具体序列化逻辑
pass
基本序列化操作
序列化数据
使用高层 API 进行序列化:
from django.core import serializers
# 简单方式
data = serializers.serialize("xml", SomeModel.objects.all())
# 复杂方式
XMLSerializer = serializers.get_serializer("xml")
xml_serializer = XMLSerializer()
xml_serializer.serialize(queryset)
data = xml_serializer.getvalue()
反序列化数据
for obj in serializers.deserialize("xml", data):
# obj 是 DeserializedObject 实例
obj.save() # 保存到数据库
序列化参数详解
序列化方法接受三个主要参数:
self.stream = options.pop("stream", StringIO())
self.selected_fields = options.pop("fields", None)
self.use_natural_keys = options.pop("use_natural_keys", False)
stream - 流输出
直接将序列化结果写入文件流:
output_file = open("data.xml", "w")
xml_serializer.serialize(SomeModel.objects.all(), stream=output_file)
selected_fields - 选择字段
只序列化指定字段:
data = serializers.serialize('xml', SomeModel.objects.all(), fields=('name', 'size'))
use_natural_keys - 自然键
默认情况下,外键和多对多关系使用主键。但在某些场景(如引用 ContentType)下,使用自然键更合适。自然键是指一组可以唯一标识记录的非主键字段组合。
自然键的使用
定义自然键反向查找
在模型的管理器中定义 get_by_natural_key() 方法:
from django.db import models
class PersonManager(models.Manager):
def get_by_natural_key(self, first_name, last_name):
return self.get(first_name=first_name, last_name=last_name)
class Person(models.Model):
objects = PersonManager()
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
birthdate = models.DateField()
class Meta:
unique_together = (('first_name', 'last_name'),)
使用自然键后,序列化输出:
{
"pk": 1,
"model": "store.book",
"fields": {
"name": "Mostly Harmless",
"author": ["Douglas", "Adams"]
}
}
定义自然键正向输出
在模型类中定义 natural_key() 方法,并在序列化时设置 use_natural_keys=True:
class Person(models.Model):
objects = PersonManager()
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
birthdate = models.DateField()
def natural_key(self):
return (self.first_name, self.last_name)
class Meta:
unique_together = (('first_name', 'last_name'),)
# 序列化时使用
serializers.serialize('json', [person1, person2], use_natural_keys=True)
处理序列化依赖
当存在外键关系时,需要确保被引用的对象先序列化。可以通过设置依赖关系来指定序列化顺序:
class Book(models.Model):
name = models.CharField(max_length=100)
author = models.ForeignKey(Person)
def natural_key(self):
return (self.name,) + self.author.natural_key()
natural_key.dependencies = ['example_app.person'] # 指定依赖
模型继承的注意事项
对于多表继承,必须同时序列化所有基类。例如:
class Place(models.Model):
name = models.CharField(max_length=50)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField()
# 正确做法:同时序列化Place和Restaurant
all_objects = list(Restaurant.objects.all()) + list(Place.objects.all())
data = serializers.serialize('xml', all_objects)