32.关系字段的序列化

很多时候我们的模型设计并不只是单一的字段数据,还有多对多、1对多、关联等
序列化与单一的字段数据有所不同
 
模型类
'''
如下三个模型类
Text分别与auth和category外键关联和多对多关联
'''
# 作者
class Auth(models.Model):
    username = models.CharField(max_length=130)


# 分类
class Category(models.Model):
    name = models.CharField(max_length=130)


# 文章
class Text(models.Model):
    title = models.CharField(max_length=130)
    content = models.TextField()

    '''
    定义外键关联和多对多
    '''

    auth = models.ForeignKey(Auth, on_delete=models.CASCADE)
    category = models.ManyToManyField(Category, blank=True)

    class Meta:
        ordering = ['created']
 
序列化器
from rest_framework import serializers
from .models import Auth, Category, Text


# 作者
class AuthSerializer(serializers.ModelSerializer):
    class Meta:
        model = Auth
        fields = ('id', 'username',)


# 分类
class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = ('id', 'username',)


# 文本
class TextSerializer(serializers.ModelSerializer):
    class Meta:
        model = Text
        fields = ('id', 'title', 'content', 'auth', 'category')
 
视图
from .models import Auth, Text, Category
from .serializer import AuthSerializer, TextSerializer, CategorySerializer
from rest_framework.viewsets import ModelViewSet


class AuthViewSet(ModelViewSet):
    queryset = Auth.objects.all()
    serializer_class = AuthSerializer


class CategoryViewSet(ModelViewSet):
    queryset = Category.objects.all()
    serializer_class = CategorySerializer


class TextViewSet(ModelViewSet):
    queryset = Text.objects.all()
    serializer_class = TextSerializer
路由
from . import views

from rest_framework.routers import DefaultRouter

router = DefaultRouter()

router.register(r'auth', views.AuthViewSet,basename='auth')
router.register(r'category', views.Category,basename='category')
router.register(r'text', views.Text,'text')

urlpatterns = router.urls
 
字段处理
遵循restful规范,一个名词url只处理一个资源,就像上面代码
不在嵌套的关系字段中同时创建对象
比如创建auth,我们用auth去处理,而不是创建text对象同时创建可auth
前后端协商接口的时候,就要将逻辑分开
# 如果非要在Text中去对auth、category去做处理,这种设计不主张,但是也可以实现
class TextSerializer(serializers.ModelSerializer):
    # 在text序列化类中声明auth的序列化类
    # 有auth的处理,会使用AuthSerializer进行
    # 在序列化中的实例,本身也是一种字段
    # 如果不声明直接就使用text对auth进行操作,会请求出错
    auth = AuthSerializer() 
    
    class Meta:
        model = Text
        fields = ('id', 'title', 'content', 'auth', 'category','created')
        
    # 声明atuh序列化类之后直接请求会抛出non_field_error
    # 在反向序列化的时候,源码默认的create方法不支持嵌套的字段
    # 所以需要我们重写create方法
    '''
    请求的validated_data 数据:
    {'title': 'python', 'content': 'test', 'auth': OrderedDict([('username', '嵌套用户请求')])}
    可以看出auth是的数据是嵌套在对应层级内
    '''
    def create(self, validated_data):
        auth_data = validated_data.pop('auth')  # 通过pop拿出auth对应的值
    
        # 我们要把对应内容创建给Auth,需要调用对应模型的创建
        # 有就获取用户,如果没有就创建用户,get_or_create除了返回结果还会返回对应状态 True /False
        auth,flag = Auth.objects.get_or_create(username=auth_data['username'])
        # 上面我们通过pop把auth拿出来单独传入,其他的字典数据解包传入
        text = Text.objects.create(auth=auth, **validated_data)
        return text
            
        
    ''' 
    上面create只处理了关联的user外键,
    如果处理category的多对多字段,还需要单独处理
    '''        
    def create(self, validated_data):
        # 在text处理关联的多对多字段是跟外键一样的嵌套关系
        category_data = [] # 定义一个多对多空列表
        if validated_data['category']: # 如果有传入多对多字段数据
            category_data = validated_data.pop('category') # 将对应数据拿出
    
        auth_data = validated_data.pop('auth')
        auth = Auth.objects.get_or_create(username=auth_data['username'])
        text = Text.objects.create(auth=auth, **validated_data)
    
        # 待其他数据处理完毕之后判断是否有传入多对多字段
        if category_data:
            # text关联的category使用add方法进行字段追加
            # add 就是orm针对多对多处理的语法
            text.category.add(*category_data)
    
        return text
        
        
        
    # 更新数据重写update方法
    
    def update(self, instance, validated_data):
        category_data = validated_data.pop('category') 
        auth_data = validated_data.pop('auth')
        # 如果多对多字段 category有值
        if category_data:
            # 更新对应的值
            instance.category.set(category_data)
    
        # 先更新没有关联外键、多对多关联的数据
        instance.title = validated_data.get('title', instance.title)
        instance.title = validated_data.get('content', instance.content)
        instance.created = validated_data.get('created', instance.created)
        instance.id = validated_data.get('id', instance.id)
        instance.content = validated_data.get('content', instance.content)
        # auth 外键更新
        auth = instance.auth  # 获取到auth 模型
        auth.username = auth_data.get('username', auth.username)
        auth.save()
    
        return instance
 
 

作者:木子七

出处:https://www.cnblogs.com/Mickey-7/p/16718532.html

posted @   木子七  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示
workspaces
keyboard_arrow_up dark_mode palette
选择主题