【问题标题】:Serializing one to many relationship using ModelSerializer使用 ModelSerializer 序列化一对多关系
【发布时间】:2020-09-22 08:02:36
【问题描述】:

我的目标是嵌套 JSON 响应。我的目标输出是这样的;

[
 {
   list_id: <list_id_1>,
   list_name: <list_name_1>,
    {
       item_id: <item_id_1>,
       item_name: <item_name_1>,
    },
    {
       item_id: <item_id_2>,
       item_name: <item_name_2>,
    }
 }
]

我想让我的输出成为一个嵌套的 JSON。到目前为止,这是我的进步;

models.py

class List(models.Model):
    list_name = models.CharField(max_length=30, blank=True, null=True, default='My List')

    def __str__(self):
        return ("List ID: " + str(self.id) + ", " + self.list_name)


class Item(models.Model):
    list = models.ForeignKey('List', related_name='items', on_delete=models.CASCADE)
    item_name = models.CharField(max_length=30, blank=False, null=False)
    item_desc = models.CharField(max_length=50, blank=True, null=True)
    item_category = models.CharField(max_length=20, blank=True, null=True)
    item_price = models.DecimalField(max_digits=5, decimal_places=2, blank=False, null=False, default=0.00)
    item_qty = models.IntegerField(blank=False, null=False, default=1)
    is_complete = models.BooleanField(default=False)

    def __str__(self):
        return ("Item ID: " + str(self.id) + ", " + self.item_name)

    @property
    def item_tot_price(self):
        return (self.item_price * self.item_qty)

序列化器.py

class ItemSerializer(serializers.ModelSerializer):
    list_name = serializers.CharField(read_only=True, source = "list.list_name")

    class Meta:
        model = models.Item
        fields = ['id', 'list', 'list_name', 'item_name', 'item_desc', 'item_category', 'item_price', 'item_qty', 'is_complete', 'item_tot_price']

views.py

class ItemListAPIView(generics.ListAPIView):
    serializer_class = serializers.ItemSerializer

    def get_queryset(self):
        """
        This APIView should return a list of all the items 
        that are related to a List as determined by the 
        list portion of the URL.
        """
        list = self.kwargs['list']
        return models.Item.objects.filter(list__id=list)

urls.py

urlpatterns = [
    path('Items/<int:list>/', views.ItemListAPIView.as_view()),
]

使用我当前进度的输出:

[
    {
        "item_id": 10,
        "list_id": 4,
        "list_name": "TestName",
        "item_name": "TestName",
        "item_desc": "TestName",
        "item_category": "TestName",
        "item_price": "2.00",
        "item_qty": 9,
        "is_complete": true,
        "item_tot_price": 18.0
    },
    {
        "item_id": 12,
        "list_id": 4,
        "list_name": "TestName",
        "item_name": "TestName",
        "item_desc": "TestName",
        "item_category": "TestName",
        "item_price": "0.00",
        "item_qty": 1,
        "is_complete": false,
        "item_tot_price": 0.0
    },

]

从我当前的输出 JSON 中可以看出,它会将项目排序到相应的列表中。但是我想将 list_id 和 list_name 显示为父项,然后显示项目的数据。

简单来说,我想做一个能够获取 list_id 并显示与列表相关的所有项目的端点。

免责声明:我使用 many=True agruments、related_fields、HyperlinkedModelSerializer 弄乱了序列化程序。它们都没有按我想要的方式工作。

【问题讨论】:

  • 你可以创建一个新的序列化器,例如“ListSerializer”的构造方式与您使用 ItemSerializer 的方式相同。 “ListSerializer”包含具有 many=True 的 ItemSerializer。也许你可以试试这个并发布你的解决方案。如果没有帮助,请再次询问。
  • 我的错误。在此之前,我制作了 ItemSerializer 并将 many=True 用于列表。据我了解,有很多项目记录,所以我立即认为它会显示该列表中的所有项目,假设我制作了一个自定义 get_queryset。看来我弄错了。谢谢你指出。干杯

标签: python python-3.x django django-rest-framework


【解决方案1】:

编辑您的serializers.py,如下所示。不要序列化Item,而是使用List,所以在你的views.py中你将使用ListSerializer而不是ItemSerializer


class ItemSerializer(serializers.ModelSerializer):
    list_name = serializers.CharField(read_only=True, source = "list.list_name")

    class Meta:
        model = models.Item
        fields = ['id', 'list', 'list_name', 'item_name', 'item_desc', 'item_category', 'item_price', 'item_qty', 'is_complete', 'item_tot_price']

class ListSerializer(serializers.ModelSerializer):
    items = ItemSerializer(many=True, read_only=True)
    class Meta:
        model = List
        fields = ['id','list_name','items']

【讨论】:

  • 只是好奇,因为我还在玩序列化器和视图。如果我想将名称“列表”放在嵌套 JSON 响应的顶部,是否可以?像这样; [“列表”:{“id”:7,“list_name”:“list_name”,“items”:[{“id”:18,},
  • 我的前端开发人员想知道是否可以在 JSON 响应的开头添加名称“Lists”。
  • 我想这就是你想要的 -> stackoverflow.com/questions/20424521/…
  • 正是我需要的。我一直想知道渲染器的目的是什么。现在我知道制作 API 的重要性了。这样我就可以操纵了。再次感谢。干杯伙伴。
【解决方案2】:

感谢@Yves Hary 和@adnan kaya,

根据 Yves 所说,我修改了我的 ListSerializer 以包含

items = ItemSerializer(many=True, read_only=True)

因此,

class ItemSerializer(serializers.ModelSerializer):
    list_name = serializers.CharField(read_only=True, source = "list.list_name")

    class Meta:
        model = models.Item
        fields = ['id', 'list', 'list_name', 'item_name', 'item_desc', 'item_category', 'item_price', 'item_qty', 'is_complete', 'item_tot_price']


class ListSerializer(serializers.ModelSerializer):
    """New Line Added Here"""
    items = ItemSerializer(many=True, read_only=True) 

    class Meta:
        model = models.List
        fields = ['id', 'list_name', 'items']

我之前做了调整,根据adnan给出的答案,确认方法是正确的。

使用当前的序列化程序,如果我使用 ModelViewSet,它将显示所有列表及其所有项目。像这样;

class ListViewSet(viewsets.ModelViewSet):
    queryset = models.List.objects.all()
    serializer_class = serializers.ListSerializer

我的目标是显示带有项目的特定列表。所以,我像这样使用 generics.ListAPIView 实现了一个视图;

class ListItemAPIView(generics.ListAPIView):
    serializer_class = serializers.ListSerializer

    def get_queryset(self):
        list = self.kwargs['list']
        return models.List.objects.filter(id=list)

最后,我的 urls.py 看起来像这样;

urlpatterns = [
    path('List/<int:list>/', views.ListItemAPIView.as_view()),
]

只有这样我的输出才会这样;

[
    {
        "list_id": <list_id>,
        "list_name": "<list_name>",
        "items": [
            {
                "item_id": <item_id>,
                "list_id": <list_id>,
                "list_name": "<list_name>",
                "item_name": "TestName",
                "item_desc": "TestName",
                "item_category": "TestName",
                "item_price": "2.00",
                "item_qty": 3,
                "is_complete": false,
                "item_tot_price": 6.0
            },
            {
                "item_id": <item_id>,
                "list_id": <list_id>,
                "list_name": "<list_name>",
                "item_name": "TestName",
                "item_desc": "TestName",
                "item_category": "TestName",
                "item_price": "2.00",
                "item_qty": 3,
                "is_complete": false,
                "item_tot_price": 6.0
            }
        ]
    }
]

编辑:封装 JSON 响应并添加关键字“lists”;

renderers.py

from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer


class BaseFlutterJSONRenderer(JSONRenderer):

    def render(self, data, accepted_media_type=None, renderer_context=None):
        data = {'lists': data}
        return super(BaseFlutterJSONRenderer, self).render(data, accepted_media_type, renderer_context)


class FlutterJSONRenderer(BrowsableAPIRenderer):
    def get_default_renderer(self, view):
        return BaseFlutterJSONRenderer()

【讨论】:

  • 注1:我会清理ItemSerializer,使它不会显示太多的冗余数据。注2:特别感谢@Yves Hary&adnan kaya
  • 欢迎您如果答案有用,您可以接受。
猜你喜欢
  • 1970-01-01
  • 2015-08-13
  • 2011-08-11
  • 2019-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多