DRF连表序列化用法

2020-02-08 13:41:55 最后一行代码 阅读:429

分类: Django

准备工作:

1、安装DRF

pip install djangorestframework

2、注册app

INSTALLED_APPS = [

    'rest_framework'

]

 

 

模型:

class BlogType(models.Model):
    type_name = models.CharField()

class Blog(models.Model):
    title = models.CharField()
    type = models.ForeignKey('BlogType')

 

 

基于模型序列化:

除该方法外还可以自定义序列化,通过继承serializers.Serializer实现,这里就不举例了。

import json
from rest_framework import serializers
from .models import Blog
class BlogSerializer(serializers.ModelSerializer):
    class Meta:
        model = Blog          # 基于序列化的模型对象
        fields = '__all__'    # 序列化模型所有字段,但是不包括反向查询字段
        # exclude = []        # 指定排除的字段,但不能与"fields"同时使用
        # depth = 0           # 深度读取,默认0。主要用来读取关联表数据
        # read_only_fields = []           # 指定只读字段,只用于序列化输出的字段
        # extra_kwargs = {字段:{选项:值}}  # 为序列化器字段添加或修改原有的选项参数,嵌套字典类型
    

s = BlogSerializer(Blog.objects.all(),many=True)     # 参数一序列化模型的QuerySet对象,参数二因为QuerySet有多个对象,所以需要指定many参数说明是否将多个数据保存在有序字典中返回。   
print(json.dumps(s.data, ensure_ascii=False))
输出:
[
    {
        'id': 1,
        'title': 'Django基础',
        'type': 1
    },
    {
        'id': 2,
        'title': 'JS基础',
        'type': 2
    }
]

s = BlogSerializer(Blog.objects.first())           #如果参数一指定的是一个模型实例对象,那么就不用指定many参数了
print(json.dumps(s.data))
输出:
{
    'id': 1,
    'title': 'Django基础',
    'type': 1
}


为什么关联字段的值是关联表的主键而不是准确值呢,其实解决起来很简单,看下面代码。

 

 

关联字段序列化方法(多对多、一对一关联用法差不多一样,这里就举例多对一关联用法):

第一种:(通过参数source获取)

import json
from rest_framework import serializers
from .models import Blog
class BlogSerializer(serializers.ModelSerializer):
    types = serializers.CharField(source="type.type_name")     # 通过参数source来获取关联表数据,语法:"关联字段.关联模型字段"
    class Meta:
        model = Blog         
        fields = '__all__'          # 注意:上面有个自定义字段"types",入果这里还使用"__all__"那么输出时"type"、"types"都会输出。一般我们都会将属性名设置为关联字段名,这样在最后输出时因为是字典对象,相同的Key会被后面的覆盖,这样就不会出现两个了。或者使用fileds=['id','title','types']这样就不会输出原有的关联字段了


s = BlogSerializer(Blog.objects.all(),many=True)  
print(json.dumps(s.data, ensure_ascii=False))
输出:
[
    {
        'id': 1,
        'title': 'Django基础',
        'type': 1,                # 注意看这里出现了两个,虽然值不相同,但多出了一个原有的关联字段
        'types': 'Django'         # 注意看这里出现了两个
    },
    {
        'id': 2,
        'title': 'JS基础',
        'type': 2,
        'types': 'JS'
    }
]


=========================拓展用法==============================
# 模型
class BlogType(models.Model):
    type_name = models.CharField()

class Blog(models.Model):
    title = models.CharField()
    type = models.ForeignKey('BlogType')

    def get_type(self):                       # 新增方法
        return self.type.type_name

# 序列化类
from rest_framework import serializers
from .models import Blog
class BlogSerializer(serializers.ModelSerializer):
    type = serializers.CharField(source="get_type")    # 模型中定义的方法同样可以执行
    class Meta:
        model = Blog         
        fields = '__all__'            # 这里就不会多输出模型原有字段,因为被自定义覆盖了

 

第二种:(通过.SlugRelatedField()方法)

import json
from rest_framework import serializers
from .models import Blog
class BlogSerializer(serializers.ModelSerializer):
    type = serializers.SlugRelatedField(slug_field="type_name", read_only=True)     # slug_field参数指定关联模型字段。read_only表示该字段仅用于序列化输出,不指定会报错。注意:属性名必须与模型的关联字段名一样
    class Meta:
        model = Blog         
        fields = '__all__'



s = BlogSerializer(Blog.objects.all(),many=True)  
print(json.dumps(s.data, ensure_ascii=False))
输出:
[
    {
        'id': 1,
        'title': 'Django基础',
        'type': 'Django'
    },
    {
        'id': 2,
        'title': 'JS基础',
        'type': 'JS'
    }
]

 

 第三种:(通过.SerializerMethodField()方法)

import json
from rest_framework import serializers
from .models import Blog
class BlogSerializer(serializers.ModelSerializer):
    type = serializers.SerializerMethodField(method_name="get_type")     # 参数method_name表示执行的函数名,参数值不能与该属性名重复,默认"get_该属性名"
    
    def get_type(self, obj):             # 该方法会被Blog每一个实例对象所执行。形参obj指向当前模型实例
        return obj.type.type_name        # 返回值返回什么,最后就输出什么

    class Meta:
        model = Blog         
        fields = '__all__'



s = BlogSerializer(Blog.objects.all(),many=True)  
print(json.dumps(s.data, ensure_ascii=False))
输出:
[
    {
        'id': 1,
        'title': 'Django基础',
        'type': 'Django'
    },
    {
        'id': 2,
        'title': 'JS基础',
        'type': 'JS'
    }
]

 

第四种:(depth深度读取)

import json
from rest_framework import serializers
from .models import Blog
class BlogSerializer(serializers.ModelSerializer):
    class Meta:
        model = Blog         
        fields = '__all__'
        depth = 1               # 深度读取关联表所有字段,看下面输出结果就明白了



s = BlogSerializer(Blog.objects.all(),many=True)  
print(json.dumps(s.data, ensure_ascii=False))
输出:
[
    {
        'id': 1,
        'title': 'Django基础',
        'type': {
            'id': 1,
            'type_name': 'Django'
        }
    },
    {
        'id': 2,
        'title': 'JS基础',
        'type': {
            'id': 2,
            'type_name': 'JS'
        }
    }
]

 

第五种:(序列化类嵌套)

import json
from rest_framework import serializers
from .models import Blog,BlogType

class BlogTypeSerializer(serializers.ModelSerializer):
    class Meta:
        model = BlogType         
        fields = '__all__'

class BlogSerializer(serializers.ModelSerializer):
    type = BlogTypeSerializer()            # 如果关联数据存在多条,需要指定参数many=True
    class Meta:
        model = Blog         
        fields = '__all__'




s = BlogSerializer(Blog.objects.all(),many=True)  
print(json.dumps(s.data, ensure_ascii=False))
输出:
[
    {
        'id': 1,
        'title': 'Django基础',
        'type': {
            'id': 1,
            'type_name': 'Django'
        }
    },
    {
        'id': 2,
        'title': 'JS基础',
        'type': {
            'id': 2,
            'type_name': 'JS'
        }
    }
]

 

 

ContentType关联序列化:

想要对ContentType关联进行序列化要注意下面几点:

1、假设表1与表2存在ContentType关联,如果表1中只有部分数据关联表2,那么在序列化时就要注意这些没有关联表2的数据在获取关联表字段时如何获取,否则报错字段不存在。

2、假设有多张表通过ContentType关联起来,表1,表2,表3,这三个表中字段各不相同,在序列化时就要考虑以哪个表字段为基准进行获取数据,否则就会存在想获取表2的字段但是写的确实表3的字段。

3、假设表1与表2存在ContenType关联,如果表1中每一条数据关联表2中多条数据,那么在序列化时就要注意关联的数据存在一条还是多条,是直接进行取值还是遍历。

from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.fields import GenericRelation

# 记录博客点赞数
class LikeCount(models.Model):
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type','object_id')
    count = models.PositiveIntegerField(default=0)    # 点赞数

# 博客
class Blog(models.Model):
    title = models.CharField()
    like_count = GenericRelation(LikeCount)    # ConentType反向查询

 

第一种方法:(通过参数source,缺点是无法获取关联表更多信息)

import json
from rest_framework import serializers
from .model import Blog

class BlogSerializer(serializers.ModelSerializer):
    like_count = serializers.CharField(source="like_count")
    class Meta:
        model = Blog          # 基于Blog序列化
        fileds = "__all__"

s = BlogSerializer(Blog.objects.all(), many=True)
print(json.dumps(s.data, ensure_ascii=False))
输出:
[
    {
        'id': 1,
        'title': 'Django基础',
        'like_count': 10
    },
    {
        'id': 2,
        'title': 'JS基础',
        'like_count': 30
    },
]
import json
from rest_framework import serializers
from .model import LikeCount

class LikeCountSerializer(serializers.ModelSerializer):
    content_object = serializers.CharField(source='content_object.title')
    class Meta:
        model = LikeCount           # 基于LikeCount序列化
        fileds = "__all__"

s = LikeCountSerializer(LikeCount.objects.all(), many=True)
print(json.dumps(s.data, ensure_ascii=False))
输出:
[
    {
        'id': 1,
        'title': 'Django',
        'content_object': 'Django基础'
    },
    {
        'id': 2,
        'title': 'JS',
        'content_object': 'JS基础'
    }
]

 

第二种方法:(通过.SerializerMethodField()获取)

import json
from rest_framework import serializers
from .model import Blog

class BlogSerializer(serializers.ModelSerializer):
    obj = serializers.SerializerMethodField()

    # 注意:该方法会被每个实例所调用,那么就存在一个问题,如果只有部分实例与LikeCount表存在关联,那么下面遍历中就会存在部分实例获取不到LikeCount表数据的情况,这时候就会用null表示(不同写法有不同的值表示,看下面举例代码)
    def get_obj(self,obj):                          
        for i in obj.like_count.all():
            return {'id':i.pk, 'id':i.count}

    class Meta:
        model = Blog                 # 基于Blog序列化
        fileds = "__all__"

s = BlogSerializer(Blog.objects.all(), many=True)
print(json.dumps(s.data, ensure_ascii=False))
输出:
[
    {
        'id': 1,
        'title': 'Django基础',
        'obj':{
            'id': 1,
            'count': 10
        }
    },
    {
        'id': 2,
        'title': 'JS基础',
        'obj':{
            'id': 2,
            'count': 30
        }
    },
    {
        'id': 3,
        'title': 'HTML基础',           
        'obj': null                 # 假设这个实例没有与LikeCount表关联
    },
]



===========================其他函数返回值写法==============================
def get_obj(self,obj):                          
    return [{'id': i.pk, 'count': i.count} for i in obj.like_count.all()]

返回:
[
    {
        'id': 1,
        'title': 'Django基础',
        'obj':[                    # 注意看此时关联表所有数据都会保存在列表中返回
            {
                'id': 1,
                'count': 10
            }
        ]
    },    
    {
        'id': 3,
        'title': 'HTML基础',
        'obj': []                  # 假设这个实例没有与LikeCount表关联
    } 
]
import json
from rest_framework import serializers
from .model import LikeCount

class LikeCountSerializer(serializers.ModelSerializer):
    obj = serializers.SerializerMethodField()

    def get_obj(self,obj):                          
        return {'id':obj.content_object.pk, 'title':obj.content_object.title}

    class Meta:
        model = LikeCount                 # 基于LikeCount序列化
        fileds = "__all__"

s = BlogSerializer(LikeCount.objects.all(), many=True)
print(json.dumps(s.data, ensure_ascii=False))
输出:
[
    {
        'id': 1,
        'count': 10,
        'obj':{
            'id': 1,
            'title': 'Django基础'
        }
    },
    {
        'id': 2,
        'count': 30
        'obj':{
            'id': 2,
            'title': 'JS基础',
        }
    }
]

 

第三种方法:(序列化类嵌套)

import json
from rest_framework import serializers
from .model import LikeCount, Blog

class LikeCountSerializer(serializers.ModelSerializer):
    class Meta:
        model = LikeCount                 # 基于LikeCount序列化
        fileds = ['id','count']           # 注意:此时就不能使用"__all__"了,因为"object_id"字段无法获取(具体为啥我也不清楚)

class BlogSerializer(serializers.ModelSerializer):
    obj = LikeCountSerializer()
    class Meta:
        model = Blog
        fileds = '__all__'

s = BlogSerializer(LikeCount.objects.all(), many=True)
print(json.dumps(s.data, ensure_ascii=False))
输出:
[
    {
        'id': 1,
        'count': 10,
        'obj':{
            'id': 1,
            'title': 'Django基础'
        }
    },
    {
        'id': 2,
        'count': 30
        'obj':{
            'id': 2,
            'title': 'JS基础',
        }
    }
]
回复:
时间排序 热度排序
  • rantrism2月前

    作者,你好!我是腾讯云+社区的小编,关注了您分享的技术文章,觉得很棒,我们诚挚邀请您加入云+社区,与我们众多的社区作者一起为开发者分享技术干货。这个是我们云+社区【腾讯云自媒体分享计划】入驻流程和权益介绍的地址:https://cloud.tencent.com/developer/support-plan。如果您愿意加入或者想了解更多的信息请联系我~微信:techou002,我们对您的加入充满期待。

老板赏瓶水呗
微信 微信 支付宝 支付宝