Django ORM 和 Django REST Framework:如何制作“分组”数组?

问题描述 投票:0回答:2

目前,我的 Django 服务器可以返回以下 JSON:

[
  {
    "id": 1,
    "customer": {
      "id": 1,
      "name": "John Doe"
    },
    "description1": "...",
    "description2": "...",
    "description3": "...",
    "description4": "..."
  },
  {
    "id": 2,
    "customer": {
      "id": 1,
      "name": "John Doe"
    },
    "description1": "...",
    "description2": "...",
    "description3": "...",
    "description4": "..."
  },
  {
    "id": 3,
    "customer": {
      "id": 1,
      "name": "John Doe"
    },
    "description1": "...",
    "description2": "...",
    "description3": "...",
    "description4": "..."
  },
  {
    "id": 4,
    "customer": {
      "id": 2,
      "name": "Jane Doe"
    },
    "description1": "...",
    "description2": "...",
    "description3": "...",
    "description4": "..."
  },
  {
    "id": 5,
    "customer": {
      "id": 2,
      "name": "Jane Doe"
    },
    "description1": "...",
    "description2": "...",
    "description3": "...",
    "description4": "..."
  }
]

我的模特是:

from django.db import models

class Customer(models.Model):
    name = models.CharField(verbose_name="Customer name")

from django.db import models


class PurchaseDescriptions(models.Model):
    customer =  models.ManyToManyField("customer.Customer", related_name="customer", verbose_name="Customer")
    description1 = models.CharField(verbose_name="Description 1")
    description2 = models.CharField(verbose_name="Description 2")
    description3 = models.CharField(verbose_name="Description 3")
    description4 = models.CharField(verbose_name="Description 4")

我的序列化程序是:

from rest_framework import serializers

class CustomerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Customer
        fields = [
            "id",
            "name",
        ]

class PurchaseDescriptionsSerializer(serializers.ModelSerializer):
    customer = CustomerSerializer()
    
    class Meta:
        model = PurchaseDescriptions
        fields = [
            "id",
            "customer",
            "description1",
            "description2",
            "description3",
            "description4",
        ]

我的观点是:

from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins

class ScheduleViewSet(
    mixins.ListModelMixin,
    GenericViewSet,
):
    queryset = PurchaseDescriptions.objects.all()
    serializer_class = PurchaseDescriptionsSerializer

我想重写它并根据

customer
做一些组,例如

[
  {
    "customer": {
      "id": 1,
      "name": "John Doe"
    },
    "data": [
      {
        "id": 1,
        "description1": "...",
        "description2": "...",
        "description3": "...",
        "description4": "..."
      },
      {
        "id": 2,
        "description1": "...",
        "description2": "...",
        "description3": "...",
        "description4": "..."
      },
      {
        "id": 3,
        "description1": "...",
        "description2": "...",
        "description3": "...",
        "description4": "..."
      }
    ]
  },
  {
    "customer": {
      "id": 2,
      "name": "Jane Doe"
    },
    "data": [
      {
        "id": 4,
        "description1": "...",
        "description2": "...",
        "description3": "...",
        "description4": "..."
      },
      {
        "id": 5,
        "description1": "...",
        "description2": "...",
        "description3": "...",
        "description4": "..."
      }
    ]
  }
]

但我不知道如何使用 Django 的视图和序列化程序来实现。我怎样才能得到这样的JSON?请注意,如果可能的话,我想避免在数据库中创建额外的表。

python json django django-rest-framework django-orm
2个回答
1
投票

您可以覆盖序列化器to_representation() 以获得自定义 JSON 响应。

试试这个:

class PurchaseDescriptionsSerializer(serializers.ModelSerializer):
    customer = CustomerSerializer()

    class Meta:
        model = PurchaseDescriptions
        fields = [
            "id",
            "customer",
            "description1",
            "description2",
            "description3",
            "description4",
        ]

    def to_representation(self, instance):
        data = super().to_representation(instance)
        customer_id = data["customer"]["id"]
        customer_name = data["customer"]["name"]
        del data["customer"]
        return {
            "customer": {
                "id": customer_id,
                "name": customer_name,
            },
            "data": [data],
        }

0
投票
# defined model 

class Customer(models.Model):
    name = models.CharField(verbose_name="Customer name")

class PurchaseDescriptions(models.Model):
    customer =  models.ManyToManyField("Customer", related_name="customer", verbose_name="Customer")
    description1 = models.CharField(verbose_name="Description 1")
    description2 = models.CharField(verbose_name="Description 2")
    description3 = models.CharField(verbose_name="Description 3")
    description4 = models.CharField(verbose_name="Description 4")

    
   

# serializers.py
from rest_framework.serializers import ModelSerializer, Serializer
from rest_framework.fields import CharField, SerializerMethodField
from drf_spectacular.utils import extend_schema_field


class PurchaseDescriptionSchema(ModelSerializer):
    class Meta:
        model = PurchaseDescriptions
        exclude = ("customer",)


class PurchaseDescriptionsSchema(Serializer):
    customer = SerializerMethodField(method_name="nested_customer")
    data = PurchaseDescriptionSchema(many=True)
    
    # i don't like MethodField 
    # but there is no other way because it's too wierd schema structure
    @extend_schema_field(OpenApiTypes.OBJECT)
    def nested_customer(self, obj: Customer2) -> Dict[str,Any]:
        return {
            "id": obj.id,
            "name": obj.name
        }
    


# viewSets.py
class ScheduleViewSet(
    mixins.ListModelMixin,
    GenericViewSet,
):
    queryset = Customer.objects.prefetch_related(
        Prefetch(lookup="purchasedescriptions_set", to_attr="data", queryset=PurchaseDescriptions.objects.all())
    ).all()
    serializer_class = PurchaseDescriptionsSchema

ViewSetList 的响应

"

즐코하세요

\n"

++++

如果您想以不使用 MethodField 的方式实现它,您可以在 QuerySet 中使用 Concat 来获取 JSON 格式的客户数据。

Customer.objects.annotate(customer=Concat(Value("{ \"id\": "), id, Value(",\"name\" "), name, Value("}") )).prefetch_related(~~~)

您可以通过这种方式配置查询集并使用

class PurchaseDescriptionsSchema(Serializer):
    # set customer = SerializerMethodField(method_name="nested_customer")
    customer = JsonField() # DRF SerializerField
    data = PurchaseDescriptionSchema(many=True)

如上所示实现序列化程序。

但是,上述案例的查询集太乱了,所以我使用SerializerMethodField。

© www.soinside.com 2019 - 2024. All rights reserved.