Skip to main content
 首页 » 编程设计

python之Django 嵌套序列化器不序列化内部模型

2025年01月19日25zdz8207

我正在尝试使用 django-rest-framework 将自定义 action 添加到我在 Django2 中的 ViewSet。问题是我的序列化程序没有序列化嵌套模型,因此出现错误:

{ 
    "labels": [ 
        { 
            "non_field_errors": [ 
                "Invalid data. Expected a dictionary, but got Label." 
            ] 
        }, 
        { 
            "non_field_errors": [ 
                "Invalid data. Expected a dictionary, but got Label." 
            ] 
        } 
    ] 
} 

我有两个具有 M:N 关系的模型。

标签模型:

class Label(models.Model): 
    name = models.CharField(max_length=30, help_text='Name of Label') 
    desc = models.CharField(max_length=200, help_text='Description of Label') 
 
    def __str__(self): 
        return self.name 

标签集模型:

class LabelSet(models.Model): 
    labels = models.ManyToManyField(Label, blank=True, help_text='ManyToMany field of corresponding labels') 
    name = models.CharField(max_length=30, help_text='Name of Label Set') 
    desc = models.CharField(max_length=200, help_text='Description of Label Set') 
 
    def __str__(self): 
        return self.name 

机器型号:

class Machine(models.Model): 
    name = models.CharField(max_length=30, help_text='Name of machine') 
    desc = models.CharField(max_length=200, help_text='Description of machine') 
    location = models.ForeignKey(Location, null=True, blank=True, on_delete=models.CASCADE, help_text='ID of machine location') 
    labelset = models.ForeignKey(LabelSet, null=True, blank=True, on_delete=models.DO_NOTHING, help_text='ID of set of labels relevant for this machine') 
 
    def __str__(self): 
        return self.name 

序列化器:

class LabelSerializer(serializers.ModelSerializer): 
    class Meta: 
        model = Label 
        fields = '__all__' 
 
class LabelSetSerializer(serializers.ModelSerializer): 
    qs = Label.objects.all().values() 
    labels = LabelSerializer(qs, many=True) 
    class Meta: 
        depth = 1 
        model = LabelSet 
        fields = ('name', 'desc', 'labels') 

viewsets.py 中的自定义操作(我想通过机器检索可用标签,所以路径是 /machines/{id}/labels

class MachineViewSet(viewsets.ModelViewSet): 
    ''' 
    A viewset used for retrieving and editing Machine instances. 
    ''' 
    #permission_classes = (DRYPermissions,) 
    serializer_class = MachineSerializer 
    queryset = Machine.objects.all() 
 
    # /api/v1/machines/{machine_id}/labels 
    @action(detail=True) 
    def labels(self, request, pk=None): 
        # Get labelset id 
        ls = Machine.objects.get(pk=pk).labelset 
 
        # Get LabelSet instance 
        serializer = LabelSetSerializer(data=model_to_dict(ls)) 
        if serializer.is_valid(): 
            return Response(serializer.data) 
        else: 
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) 

端点工作正常,但在查询 /machines/1/labels 时,我得到的响应是第一个片段:

"Invalid data. Expected a dictionary, but got Label."

我完全没有想法,甚至尝试在 Serializer 中从 qs = Label.objects.all().values() 制作字典,但没有成功。

感谢@Jerin Peter George,现在的输出是:

HTTP 200 OK 
Allow: GET, HEAD, OPTIONS 
Content-Type: application/json 
Vary: Accept 
 
{ 
    "name": "TestSet", 
    "desc": "asd", 
    "labels": [ 
        { 
            "id": 1, 
            "name": "OK", 
            "desc": "desc" 
        }, 
        { 
            "id": 2, 
            "name": "Broken", 
            "desc": "asd" 
        } 
    ] 
} 

所以 /api/v1/machines/1/labels 有效,但突然 /api/v1/machines/ 无效。 (502 Bad Gateway with error TypeError: 'LabelSet' object is not iterable)

APP 级别的 url:

from django.conf.urls import url 
from devices.viewsets import * 
from rest_framework.routers import DefaultRouter 
 
router = DefaultRouter() 
router.register(r'devices', DeviceViewSet, base_name='device') 
router.register(r'projects', ProjectViewSet, base_name='project') 
router.register(r'locations', LocationViewSet, base_name='location') 
router.register(r'industries', IndustryViewSet, base_name='industry') 
router.register(r'companies', CompanyViewSet, base_name='companies') 
router.register(r'project_types', ProjectTypeViewSet, base_name='project_type') 
router.register(r'device_types', DeviceTypeViewSet, base_name='device_type') 
router.register(r'machines', MachineViewSet, base_name='machine') 
router.register(r'records', RecordViewSet, base_name='record') 
router.register(r'labels', LabelViewSet, base_name='label') 
router.register(r'labelsets', LabelSetViewSet, base_name='label_set') 
urlpatterns = router.urls 

应用级 urls.py

from django.contrib import admin 
from django.conf.urls import url 
from django.urls import include, path 
from rest_framework.documentation import include_docs_urls 
from rest_framework_expiring_authtoken import views 
from devices.views import AudioUploadView 
 
API_PREFIX = 'api/v1/' 
 
urlpatterns = [ 
    url(API_PREFIX + 'docs/', include_docs_urls(title='API Docs')), 
    url(API_PREFIX + 'admin/', admin.site.urls), 
    url(API_PREFIX + 'api-token-auth/', views.obtain_expiring_auth_token), 
    path(API_PREFIX, include('devices.urls')) 
] 

编辑:已解决 显然我向 MachineSerializer

添加了一个更多的嵌套序列化程序
class MachineSerializer(serializers.ModelSerializer): 
    labelsets = LabelSetSerializer(many=True) 
    class Meta:             
        model = Machine 
        fields = '__all__' 

所以删除行 labelsets = LabelSetSerializer(many=True) 就可以了。 这就是错误的来源,现在一切都按预期工作,谢谢:)

请您参考如下方法:

将您的 labels() 替换为以下代码段,

@action(detail=True) 
def labels(self, request, pk=None): 
    # Get labelset id 
    ls = Machine.objects.get(pk=pk).labelset 
 
    # Get LabelSet instance 
    serializer = LabelSetSerializer(ls) 
    return Response(serializer.data)