如何使用 DRF 序列化 Django 中的一些嵌套关系模型?
How to serialize some nested relational models in django using DRF?
我有一些彼此之间关系不同的 Django 模型 — Many-to-many
和 Foreignkey
。通过这种方式,我想使用 djnago-rest
.
序列化它们
以下是模型:
class CommonFieldsAbstract(models.Model):
name = models.CharField(max_length=30, unique=True)
class ServerModel(CommonFieldsAbstract):
server_ip = models.GenericIPAddressField(default='172.17.0.1')
server_port = models.IntegerField(default='9001')
class SNMPLineModel(CommonFieldsAbstract):
ip_address = models.GenericIPAddressField()
port = models.IntegerField(default=161)
class SNMPModel(CommonFieldsAbstract): # 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).
这是我到目前为止所做的:
# serializers.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)
]
如有任何帮助,我们将不胜感激。
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
为了解决上述问题,我做了以下解决方案:
- 对于前向外键,您不需要
many=True
- 对于反向外键,您需要在尚未定义
related_name
. 时使用 related-model_set
- 对于额外的字段,您只需要确切的查询集。
因此,我得到了以下代码片段,而没有像 Michael 在他的回答中使用的那样使用 .SerializerMethodField()
:
class SNMPSerializer(serializers.ModelSerializer):
servers = ServerSerializer(many=True)
meta_data = MetaDataSerializer(many=True, source="metadatamodel_set")
line = LineSerializer()
address = serializers.CharField(source="line.ip_address")
port = serializers.CharField(source="line.port")
class Meta:
fields = (
"id",
"servers",
"name",
"address",
"port",
"line",
"meta_data",
)
model = SNMPModel
我有一些彼此之间关系不同的 Django 模型 — Many-to-many
和 Foreignkey
。通过这种方式,我想使用 djnago-rest
.
以下是模型:
class CommonFieldsAbstract(models.Model):
name = models.CharField(max_length=30, unique=True)
class ServerModel(CommonFieldsAbstract):
server_ip = models.GenericIPAddressField(default='172.17.0.1')
server_port = models.IntegerField(default='9001')
class SNMPLineModel(CommonFieldsAbstract):
ip_address = models.GenericIPAddressField()
port = models.IntegerField(default=161)
class SNMPModel(CommonFieldsAbstract): # 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).
这是我到目前为止所做的:
# serializers.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)
]
如有任何帮助,我们将不胜感激。
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
为了解决上述问题,我做了以下解决方案:
- 对于前向外键,您不需要
many=True
- 对于反向外键,您需要在尚未定义
related_name
. 时使用 - 对于额外的字段,您只需要确切的查询集。
related-model_set
因此,我得到了以下代码片段,而没有像 Michael 在他的回答中使用的那样使用 .SerializerMethodField()
:
class SNMPSerializer(serializers.ModelSerializer):
servers = ServerSerializer(many=True)
meta_data = MetaDataSerializer(many=True, source="metadatamodel_set")
line = LineSerializer()
address = serializers.CharField(source="line.ip_address")
port = serializers.CharField(source="line.port")
class Meta:
fields = (
"id",
"servers",
"name",
"address",
"port",
"line",
"meta_data",
)
model = SNMPModel