Django序列化模型类,指定序列化字段。(从源码解读)

Django自定义json序列化内容

当我们在做前后端分离项目的时候,有时候需要给前端返回一些字段,但是有的字段是不需要给前端返回的,所以这时候我们就可以自定义一下字段

只序列化某个字段

from django.core.serializers import serialize
from app01.models import MyModelShow
from django.http.response import JsonResponse


def return_json():
    """
    1. (必传)第一个参数传入的是序列化后的类型,可以是传入
        1. python
        2. xml
        3. json
        4. jsonl
        5. yaml
    2. (必传)第二个参数是你要序列的数据
    3. (非必传入)第三个参数传入的是你要序列化的字段
    """
    data = serialize('json', MyModelShow.objects.all(), fields=('id', 'title'))
    """
    返回的结果:
    [{'model': 'app01.mymodelshow', 'pk': 2, 'fields': {'title': '皮卡丘模型'}}]
    """
    return JsonResponse({'data': data})

去掉烦人的model字段

我们可能不希望别人知道我们的模型类,这是我们就可以通过重写Serializer类来实现

from django.core.serializers import Serializer


class JsonSerializer(Serializer):
    def get_dump_object(self, obj):
        data = {}
        if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'):
            data["pk"] = self._value_from_field(obj, obj._meta.pk)
        data['data'] = self._current
        return data

    def main(self, queryset, **options):
        self.serialize(queryset, **options)
        return self.getvalue()

源码分析

# 序列化代码
serialize('json', MyModelShow.objects.all(), fields=('id', 'title'))

# serialize的内容
'''
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()
'''

当我们进入到serialize里面可以看最后是返回一个s.getvalue()。

这个方法内部的两个表达式的作用是

  1. 查看我们要序列化的格式是否合法,并根据你要序列化的返回返回相对应的类
  2. 对我们要序列的数据进行序列化 由于第一个是验证的,返回值是已经序列化好的数据,我们这次就不看了,直接到s.serialize(queryset, **options)这个方法里面内部

s.serialize方法内部。在下面就统一将这个类叫做s.serialize了,因为三个类的类名都是Serializer
我们可以看到get_dump_object方法。这下面可以用得上。

from django.core.serializers.python import (
    Deserializer as PythonDeserializer, Serializer as PythonSerializer,
)


# 注意 此时的Serialize是实际上继承的是Serializer,并不是PythonSerializer类。
# 还有这个Serializer的文件路径是在这里 External -> python3.8 -> django -> lib -> site-packages -> django -> core -> serializers -> json.py
class Serializer(PythonSerializer):
    """Convert a queryset to JSON."""
    internal_use_only = False

    def _init_options(self):
        self._current = None
        self.json_kwargs = self.options.copy()
        self.json_kwargs.pop('stream', None)
        self.json_kwargs.pop('fields', None)
        if self.options.get('indent'):
            # Prevent trailing spaces
            self.json_kwargs['separators'] = (',', ': ')
        self.json_kwargs.setdefault('cls', DjangoJSONEncoder)
        self.json_kwargs.setdefault('ensure_ascii', False)

    def start_serialization(self):
        self._init_options()
        self.stream.write("[")

    def end_serialization(self):
        if self.options.get("indent"):
            self.stream.write("\n")
        self.stream.write("]")
        if self.options.get("indent"):
            self.stream.write("\n")

    def end_object(self, obj):
        # self._current has the field data
        indent = self.options.get("indent")
        if not self.first:
            self.stream.write(",")
            if not indent:
                self.stream.write(" ")
        if indent:
            self.stream.write("\n")
        json.dump(self.get_dump_object(obj), self.stream, **self.json_kwargs)
        self._current = None

    def getvalue(self):
        # Grandparent super
        return super(PythonSerializer, self).getvalue()

由于他本身是没有serialize这个方法的,所以我们就要去她的父类去寻找,下面是他父类的代码。
在后续我们就将这个类叫做这个了PythonSerializer

from django.core.serializers import base


class Serializer(base.Serializer):
    """
    Serialize a QuerySet to basic Python objects.
    """

    internal_use_only = True

    def start_serialization(self):
        self._current = None
        self.objects = []

    def end_serialization(self):
        pass

    def start_object(self, obj):
        self._current = {}

    def end_object(self, obj):
        self.objects.append(self.get_dump_object(obj))
        self._current = None

    def get_dump_object(self, obj):
        data = {'model': str(obj._meta)}
        if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'):
            data["pk"] = self._value_from_field(obj, obj._meta.pk)
        data['fields'] = self._current
        return data

    def _value_from_field(self, obj, field):
        value = field.value_from_object(obj)
        # Protected types (i.e., primitives like None, numbers, dates,
        # and Decimals) are passed through as is. All other values are
        # converted to string first.
        return value if is_protected_type(value) else field.value_to_string(obj)

    def handle_field(self, obj, field):
        self._current[field.name] = self._value_from_field(obj, field)

    def handle_fk_field(self, obj, field):
        if self.use_natural_foreign_keys and hasattr(field.remote_field.model, 'natural_key'):
            related = getattr(obj, field.name)
            if related:
                value = related.natural_key()
            else:
                value = None
        else:
            value = self._value_from_field(obj, field)
        self._current[field.name] = value

    def handle_m2m_field(self, obj, field):
        if field.remote_field.through._meta.auto_created:
            if self.use_natural_foreign_keys and hasattr(field.remote_field.model, 'natural_key'):
                def m2m_value(value):
                    return value.natural_key()
            else:
                def m2m_value(value):
                    return self._value_from_field(value, value._meta.pk)
            m2m_iter = getattr(obj, '_prefetched_objects_cache', {}).get(
                field.name,
                getattr(obj, field.name).iterator(),
            )
            self._current[field.name] = [m2m_value(related) for related in m2m_iter]

    def getvalue(self):
        return self.objects

由于父类还是没有serialize方法,所以我们在去他的父类寻找。

class Serializer:
    """
    Abstract serializer base class.
    """

    # Indicates if the implemented serializer is only available for
    # internal Django use.
    internal_use_only = False
    progress_class = ProgressBar
    stream_class = StringIO

    def serialize(self, queryset, *, stream=None, fields=None, use_natural_foreign_keys=False,
                  use_natural_primary_keys=False, progress_output=None, object_count=0, **options):
        """
        Serialize a queryset.
        """
        self.options = options

        self.stream = stream if stream is not None else self.stream_class()
        self.selected_fields = fields
        self.use_natural_foreign_keys = use_natural_foreign_keys
        self.use_natural_primary_keys = use_natural_primary_keys
        progress_bar = self.progress_class(progress_output, object_count)

        self.start_serialization()
        self.first = True
        for count, obj in enumerate(queryset, start=1):
            self.start_object(obj)
            # Use the concrete parent class' _meta instead of the object's _meta
            # This is to avoid local_fields problems for proxy models. Refs #17717.
            concrete_model = obj._meta.concrete_model
            # When using natural primary keys, retrieve the pk field of the
            # parent for multi-table inheritance child models. That field must
            # be serialized, otherwise deserialization isn't possible.
            if self.use_natural_primary_keys:
                pk = concrete_model._meta.pk
                pk_parent = pk if pk.remote_field and pk.remote_field.parent_link else None
            else:
                pk_parent = None
            for field in concrete_model._meta.local_fields:
                if field.serialize or field is pk_parent:
                    if field.remote_field is None:
                        if self.selected_fields is None or field.attname in self.selected_fields:
                            self.handle_field(obj, field)
                    else:
                        if self.selected_fields is None or field.attname[:-3] in self.selected_fields:
                            self.handle_fk_field(obj, field)
            for field in concrete_model._meta.local_many_to_many:
                if field.serialize:
                    if self.selected_fields is None or field.attname in self.selected_fields:
                        self.handle_m2m_field(obj, field)
            self.end_object(obj)
            progress_bar.update(count)
            self.first = self.first and False
        self.end_serialization()
        return self.getvalue()

这里就是序列化的基类了,也在这里顺利的找到了serialize方法。

查看serialize方法

  • 是在self.end_object(obj)这个方法放入的数据
    我们进入这个方法里面查看。这里有个地方需要注意。这个方法是存在于s.serialize这个类里面。我们进入到这个方法里面
    end_object这个方法是在他的子类上面,并不是在它本身上面
import json
def end_object(self, obj):
    # self._current has the field data
    indent = self.options.get("indent")
    if not self.first:
        self.stream.write(",")
        if not indent:
            self.stream.write(" ")
    if indent:
        self.stream.write("\n")
    json.dump(self.get_dump_object(obj), self.stream, **self.json_kwargs)
    self._current = None

可以看到这里是调用了一个get_dump_object方法。这个方法是在PythonSerializer这个类上面。

def get_dump_object(self, obj):
    data = {'model': str(obj._meta)}
    if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'):
        data["pk"] = self._value_from_field(obj, obj._meta.pk)
    data['fields'] = self._current
    return data

你可能感兴趣的:(Django源码分析,python,django,json)