Django 分頁查詢並返回jsons數據,中文亂碼解決方法


Django 分頁查詢並返回jsons數據,中文亂碼解決方法

一、引子

Django 分頁查詢並返回 json ,需要將返回的 queryset 序列化, demo 如下:

# coding=UTF-8

import os

from django.core import serializers
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.shortcuts import render
from django.http import HttpResponse
from mypage.models import Product


# Create your views here.


def getAllProducts(request):
    products_list = Product.objects.all()
    paginator = Paginator(products_list, 10)  # Show 10 products per page
    page = request.GET.get('page', 0)
    try:
        products = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        products = paginator.page(10)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        products = paginator.page(paginator.num_pages)

    json_data = serializers.serialize("json", products, ensure_ascii=False)
    return HttpResponse(json_data, content_type='application/json; charset=utf-8')

很容易出現的一個錯誤是中文亂碼,重點在於 json_data = serializers.serialize("json", products, ensure_ascii=False) 中第三個參數。

二、Serialize----序列化django對象

官方文檔原文:https://docs.djangoproject.com/en/2.1/topics/serialization/

django的序列化框架提供了一個把django對象轉換成其他格式的機制,通常這些其他的格式都是基於文本的並且用於通過一個管道發送django對象,但一個序列器是可能處理任何一個格式的(基於文本或者不是)

django的序列化類位於django.core下面的serializers文件夾里面,base.py文件里面定義了序列器和反序列器的基類以及一些異常,init.py文件定義了如何根據格式來選擇對應的序列器等內容,我們一起來看看吧

init.py和base.py文件的函數原型如下圖

def serialize(format, queryset, **options):
"""Serialize a queryset (or any iterator that returns database objects) using
a certain serializer."""
s = get_serializer(format)()
s.serialize(queryset, **options)
return s.getvalue()
class Serializer(object):
   """    Abstract serializer base class.    """
   # Indicates if the implemented serializer is only available for
   # internal Django use.
   internal_use_only = False
   def serialize(self, queryset, **options):

那下面我們開始正式講解django的序列化操作了

序列化數據

在最高層的api,序列化數據是非常容易的操作,看上面的函數可知,serialize函數接受一個格式和queryset,返回序列化后的數據:

簡單的寫法:

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):
    do_something_with(obj)

然而,deserialize返回的的是不是簡單的django類型對象,而是DeserializedObject實例,並且這些實例是沒有保存的,請使用DeserializedObject.save()方法把這些數據保存到數據庫

序列化格式

django之處很多的序列化格式,有些需要你安裝第三方支持的模塊,xml,json和yaml是默認支持的

注意事項
如果你是使用utf-8或者其他的非ascii編碼數據,然后用json序列器,注意穿一個ensure_ascii參數進去,否則輸出的編碼將會不正常

json_serializer = serializers.get_serializer("json")()
json_serializer.serialize(queryset, ensure_ascii=False, stream=response)

序列化參數

序列化的是是可以接受額外的參數的,總共有三個參數,如下:

        self.stream = options.pop("stream", StringIO())
        self.selected_fields = options.pop("fields", None)
        self.use_natural_keys = options.pop("use_natural_keys", False)

stream

將序列化后的數據輸出到該stream流中,接上面的復雜的寫法:

out = open("file.xml", "w")
xml_serializer.serialize(SomeModel.objects.all(), stream=out)

selected_field

選擇序列化的屬性,通過制定fields參數,fields是一個元組參數,元素是選擇要序列化的屬性

from django.core import serializers
data = serializers.serialize('xml', SomeModel.objects.all(), fields=('name','size'))

use_natural_keys

是否使用自然的關鍵字,默認是false(即是使用主鍵)

默認的外鍵和多對多關系序列化策略是使用主鍵,一般情況下是很好地,但有些情況下就不是這樣了。比如外鍵到ContentType的時候,由於ContentType是django的數據庫進程同步的時候自動產生的,它們的關鍵字不是那么容易去預測的。

一個整數id也不總是最方便的索引到一個對象的方法,所以基於這些情況,django提供了use_natural_keys這個參數,

一個natural key是一個可以不使用主鍵就可以用來區分一個元素的屬性組合的元組

natural keys的反序列化

考慮這兩個模型

from django.db import models
class Person(models.Model):
    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'),)
class Book(models.Model):
    name = models.CharField(max_length=100)
    author = models.ForeignKey(Person)

默認Book 的序列化數據將會使用一個整數索引到一個作者,例如,用json的是,一個Book的序列化數據大概是這樣的,42是外鍵Author的主鍵

{
    "pk": 1,
    "model": "store.book",
    "fields": {
        "name": "Mostly Harmless",
        "author": 42
    }
}

但這不是一個很好的方法,不是嗎?你需要知道這個主鍵代表到底是哪個Author,並且要求這個主鍵是穩定和可預測的。所以,我們可以增加一個natural key的處理函數,請在對應模型的管理模型里面定義一個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 keys的序列化

如果你想在序列化的時候使用natural key,那你必須在被序列化的模型里面頂一個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', [book1, book2], use_natural_keys=True)
注意:natural_key()和get_by_natural_key()不是同時定義的,如果你只想重載natural keys的能力,那么你不必定義natural_key()方法;同樣,如果你只想在序列化的時候輸出這些natural keys,那么你不必定義get_by_natural_key()方法

序列化過程中的依賴關系

因為natural keys依賴數據庫查詢來解析引用,所以在數據被引用之前必須確保數據是存在的。看下面的例子,如果一個Book的natural key是書名和作者的組合,你可以這樣寫:

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()

那么問題來了,如果Author還沒有被序列化呢?很明顯,Author應該在Book之前被序列化,為此,我們可以添加一個依賴關系如下:

def natural_key(self):
    return (self.name,) + self.author.natural_key()
natural_key.dependencies = ['example_app.person']

這保證了Person對象是在Book對象之前被序列化的,同樣,任何一個引用Book的對象只有在Person和Book對象都被序列化之后才會被序列化

繼承的模型

如果是使用抽象繼承的時候,不必在意這個問題;如果你使用的是多表繼承,那么注意了:必須序列化所有的基類,例如:

class Place(models.Model):
    name = models.CharField(max_length=50)
class Restaurant(Place):
    serves_hot_dogs = models.BooleanField()

如果僅僅序列化Restaurant模型,那么只會得到一個serves_hot_dog屬性,基類的屬性將被忽略,你必須同時序列化所有的繼承的模型,如下:

all_objects = list(Restaurant.objects.all()) + list(Place.objects.all())
data = serializers.serialize('xml', all_objects)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM