【问题标题】:How to serialize some nested relational models in django using DRF?如何使用 DRF 在 django 中序列化一些嵌套的关系模型?
【发布时间】:2020-12-19 09:14:07
【问题描述】:

我有一些彼此之间具有不同关系的 django 模型(即多对多、外键)。现在,我想使用djnago-rest 对它们进行序列化。

这是我的模型及其关系的示例:

class CommonFieldsAbstarct(models.Model):
    name = models.CharField(max_length=30, unique=True)

class ServerModel(CommonFieldsAbstarct):
    server_ip = models.GenericIPAddressField(default='172.17.0.1')
    server_port = models.IntegerField(default='9001')

class SNMPLineModel(CommonFieldsAbstarct):
    ip_address = models.GenericIPAddressField()
    port = models.IntegerField(default=161)

class SNMPModel(CommonFieldsAbstarct):  # target
    line = models.ForeignKey(SNMPLineModel, on_delete=CASCADE)
    servers = models.ManyToManyField(ServerModel)

class MetaDataModel(models.Model):
    key = models.CharField(max_length=20)
    value = models.CharField(max_length=20)
    snmp_device = models.ForeignKey(SNMPModel, on_delete=CASCADE)

以前,我用这种方式手动创建json:

def meta_data_json(meta_data):
    meta_data_list = []
    for meta in meta_data:
        meta_data_list.append({
            meta.key: meta.value
        })
    return meta_data_list

def server_json(servers):
    return [{'ip': server.server_ip,
             'port': server.server_port}
            for server in servers]

def create_json():
    snmp = SNMPModel.objects.filter(name__contains='a-name')
    return {
        'name': snmp.name,
        'address': snmp.line.ip_address,
        'port': snmp.line.port,
        'servers': server_json(snmp.servers.all()),
        'meta_data': meta_data_json(MetaDataModel.objects.filter(
                snmp_device=snmp.pk
            )
        ),
        'device_pk': snmp.pk
    }

我的问题:

现在,我怎样才能通过django-rest-framework创建这样一个上面的json呢?

我对多对多没有任何问题。事实上,我的问题在于他们的foreignkey(s)。

这是我到目前为止所做的:

序列化器.py

from rest_framework import serializers

class MetaDataSerializer(serializers.ModelSerializer):
    class Meta:
        fields = [
            'id',
            'key',
            'value',
            ]
        model = MetaDataModel

class ServerSerializer(serializers.ModelSerializer):
    class Meta:
        fields = [
            'id',
            'server_ip',
            'server_port',
            ]
        model = ServerModel

class LineSerializer(serializers.ModelSerializer):
    port = serializers.RelatedField(many=True)

    class Meta:
        fields = '__all__'
        model = SNMPLineModel

class SNMPSerializer(serializers.ModelSerializer):
    servers = ServerSerializer(many=True, read_only=True)  # It is ok
    meta_data = MetaDataSerializer(many=True, read_only=True)  # It's not ok
    line = LineSerializer(many=True, read_only=True)  # It's not ok
    address = serializers.CharField(source=SNMPLineModel.ip_address)  # It's not ok
    port = serializers.CharField(source=SNMPLineModel.port)  # It's not ok

    class Meta:
        fields = [
            'id',
            'servers',
            'name',
            'address',
            'port',
            'line',
            'meta_data'
            ]
        model = SNMPModel

views.py

from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse, JsonResponse

@csrf_exempt
def snippet_detail(request, name):
    try:
        snmp_conf = SNMPModel.objects.filter(name__contains=name)
    except SNMPModel.DoesNotExist:
        return HttpResponse(status=404)

    if request.method == 'GET':
        serializer = SNMPSerializer(snmp_conf, many=True)
        return JsonResponse(serializer.data, status=200, safe=False)

urls.py

from django.urls import path

urlpatterns = [
    path('snippets/<name>/', views.snippet_detail)
]

任何帮助将不胜感激。

【问题讨论】:

    标签: python django django-models django-rest-framework django-serializer


    【解决方案1】:

    serializers.SerializerMethodField() 是添加此类关系的有用方法。 get_meta_data() 评估字段名以调用方法有点神奇。

    地址和端口似乎是一个简单的关系,line.FOO 应该可以工作。

    class SNMPSerializer(serializers.ModelSerializer):
        servers = ServerSerializer(many=True, read_only=True)  # It is ok
        meta_data = serializers.SerializerMethodField()
        line = serializers.SerializerMethodField()
        address = serializers.CharField(source="line.ip_address", read_only=True)
        port = serializers.CharField(source="line.port" , read_only=True)
    
        class Meta:
            fields = ['id', 'servers', 'name', 'address', 'port', 'line', 'meta_data']
            model = SNMPModel
    
        def get_meta_data(self, instance):
            metadatamodels = MetaDataModel.objects.filter(snmp_device=instance)
            serializer = MetaDataSerializer(instance=metadatamodels, many=True, read_only=True)
    
            return serializer.data
    
        def get_line(self, instance):
            serializer = LineSerializer(instance.line, read_only=True)
    
            return serializer.data
    

    【讨论】:

    • 感谢您的回复,我正在尝试使用您的代码 sn-p。但我对"line.port" 有点困惑,你的意思是"SNMPLineModel.port"
    • 我想如果喜欢instance.line.port,但DRF不需要我们指定instance
    • 我该如何解决这个错误:TypeError: 'SNMPLineModel' object is not iterableget_line() 方法中的这一行引起的:return serializer.data
    • 啊,那应该是一个查询集instance=instance.line ?
    • 我通过删除many=True解决了这个问题,我的意思是:serializer = LineSerializer(instance.line, read_only=True)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-13
    • 2016-07-28
    • 1970-01-01
    • 1970-01-01
    • 2020-08-31
    相关资源
    最近更新 更多