【问题标题】:Django Rest Framework: Get unique list of values from nested structureDjango Rest Framework:从嵌套结构中获取唯一的值列表
【发布时间】:2020-07-06 20:29:24
【问题描述】:

我希望能够从深度嵌套的数据结构中返回字符串列表。在这个场景中,我有一个 API 来管理连锁书店,这些书店在不同地区有很多地方。

目前,我有一个 API 端点,它接受一个地区的 ID,并返回一个嵌套的 JSON 结构,其中包含有关该地区、各个书店以及可以在每个商店中找到的书籍的详细信息。

{
  "region": [
    {
      "store": [
        {
          "book": {
            "name": "Foo"
          }
        },
        {
          "book": {
            "name": "Bar"
          }
        },
        {
          "book": {
            "name": "Baz"
          }
        }
      ],
    },
    {
      "store": [
        {
          "book": {
            "name": "Foo"
          }
        },
        {
          "book": {
            "name": "Bar"
          }
        }
      ],
    },
    {
      "store": [
        {
          "book": {
            "name": "Foo"
          }
        },
        {
          "book": {
            "name": "Baz"
          }
        },
        {
          "book": {
            "name": "Qux"
          }
        }
      ]
    }
  ]
}

我的模型如下所示。我知道这些模型对于这个人为的例子来说不是最有意义的,但它确实反映了我的真实世界代码:

class Book(TimeStampedModel):
    name = models.CharField(default="", max_length=512)


class Bookstore(TimeStampedModel):
    value = models.CharField(default="", max_length=1024)
    book = models.ForeignKey(Book, on_delete=models.CASCADE)


class Region(TimeStampedModel):
    stores = models.ManyToManyField(Bookstore)


class BookstoreChain(TimeStampedModel):
    regions = models.ManyToManyField(Region)

我为上述响应创建的序列化程序如下所示:

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"


class BookstoreSerializer(serializers.ModelSerializer):
    books = BookSerializer()

    class Meta:
        model = Bookstore
        fields = "__all__"


class RegionSerializer(serializers.ModelSerializer):
    stores = BookstoreSerializer(many=True)

    class Meta:
        model = Region
        fields = "__all__"


class BookstoreChainSerializer(serializers.ModelSerializer):
    regions = RegionSerializer(many=True)

    class Meta:
        model = BookstoreChain
        fields = "__all__"

我不确定该解决方案的视图或序列化程序应该是什么样子。我更熟悉编写原始 SQL 或使用 ORM/Linq 来获取一组结果。

虽然上述响应肯定有用,但我真正想要的是一个 API 端点来返回可以在给定区域中找到的唯一书名列表(FooBarBaz、@987654327 @)。我希望我的回复看起来像:

{
    "books": [
        "Foo",
        "Bar",
        "Baz",
        "Qux"
    ]
}

到目前为止,我的微弱尝试有一个 urls.py 和以下 path

path("api/regions/<int:pk>/uniqueBooks/", views.UniqueBooksForRegionView.as_view(), name="uniqueBooksForRegion")

我的views.py 看起来像:

class UniqueBooksForRegionView(generics.RetrieveAPIView):
    queryset = Regions.objects.all()
    serializer_class = ???

【问题讨论】:

  • 你能发布一个你想要得到的输出的例子吗?添加您的模型也可能会有所帮助。
  • @NavidZarepak,我在上面编辑了我的问题以包含请求信息。希望这足以让我了解我不理解的内容来解决我的问题。
  • 您希望该响应作为BookstoreSerializer 响应的一部分,对名为books 的字段进行响应吗?
  • 我并不特别关注响应的外观,我只需要一个易于迭代结构的书名列表。

标签: django django-rest-framework


【解决方案1】:

所以你从地区开始你必须得到商店,所以你可以过滤商店里的书,这是一个可行的解决方案。

注意:

避免在*APIView中使用.get(),因为如果请求没有ID会触发错误,你可以使用get_object_or_404(),但是你不能在Sentry中记录你的错误。

要从*APIView 中获取元素,请使用filter()

import logging as L
class UniqueBooksForRegionView(generics.RetrieveAPIView):
    lookup_field = 'pk'
    def get(self, *args, **kwargs)
       regions = Region.objects.filter(pk=self.kwargs[self.lookup_field])
       if regions.exists():
          region = regions.first()
          stores_qs = region.stores.all()
          books_qs = Book.objects.filter(store__in=stores_qs).distinct()
          # use your book serializer
          serializer = BookSerializer(books_qs, many=True)
          return Response(serializer.data, HTTP_200_OK)
       else:
          L.error(f'Region with id {self.kwargs[self.lookup_field]} not found.')
          return Response({'detail':f'Region with id {self.kwargs[self.lookup_field]} not found.'}, HTTP_404_NOT_FOUND)

注意

这是流程,代码可能需要一些调整,但我希望它可以帮助您理解流程

【讨论】:

  • 我在这条线上遇到了一些问题:stores_qs = Region.objects.filter(pk=filter(uid=self.kwargs[self.lookup_field])).stores.set()。我收到错误filter() takes no keyword argumentspk=filter(...) 应该是使用 Python 的过滤功能还是 Django 的过滤功能?
  • @roundtheworld sry man my bad Region.objects.filter(pk=self.kwargs[self.lookup_field]).stores.set() 这是一个错字
  • 谢谢,这更有意义。两个后续问题:1. 我在 stores_qs 行上收到错误消息:'QuerySet' object has no attribute 'stores' 2. set() 不带参数吗?
  • @roundtheworld 再次抱歉 mb 我试图验证代码写得很快 Region.objects.filter(pk=self.kwargs[self.lookup_field]).stores_set.all() 将返回所需的数据
  • 嗯,添加_set 会导致错误'Region' object has no attribute 'stores_set'...但是如果我从您的示例中删除_set,代码会像我希望的那样工作。
猜你喜欢
  • 2015-04-26
  • 2016-02-07
  • 2016-11-21
  • 1970-01-01
  • 1970-01-01
  • 2021-12-16
  • 2022-11-17
  • 2021-05-31
  • 2016-03-10
相关资源
最近更新 更多