Django Rest-Framework 嵌套序列化器顺序

Posted

技术标签:

【中文标题】Django Rest-Framework 嵌套序列化器顺序【英文标题】:Django Rest-Framework nested serializer order 【发布时间】:2018-06-23 04:03:27 【问题描述】:

有没有办法订购嵌套序列化程序_set,例如按pktime-stamp 订购。

所以基本上按照下面 json 数据中显示的song_set 从最近到最新创建的对象排序,在本例中为order_by('-timestamp')order_by('-pk')

Json 数据


    "pk": 151,
    "album_name": "Name",
    "song_set": [
         
           pk: 3,
           timestamp: '5 seconds'
         ,
         
           pk: 2,
           timestamp: '10 seconds'
         ,
         
           pk: 1,
           timestamp: '15 seconds'
         
    ]

型号

class Album(models.Model):
    album_name     = models.CharField(max_length=100, blank=True)


class Song(models.Model):
    album          = models.ForeignKey('album.Album', default=1)
    timestamp      = models.DateTimeField(auto_now_add=True, auto_now=False)

序列化器

class SongListSerializer(HyperlinkedModelSerializer):
    class Meta:
        model = Song
        fields = [
            'pk',
            'timestamp'
        ]

class AlbumSerializer(HyperlinkedModelSerializer):
    song_set = SongListSerializer(many=True, read_only=True)
    class Meta:
        model = Album
        fields = [
            'pk',
            'timestamp',
            'song_set'
        ]

【问题讨论】:

【参考方案1】:

为您的 Song 模型添加 ordering 元参数:

class Song(models.Model):
    album = models.ForeignKey('album.Album', default=1)
    timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)

    class Meta:
        ordering = ['timestamp', 'pk']

【讨论】:

这样省了很多力气!谢谢 这是更好的解决方案 当我不想采用 SerializerMethodField 解决方案时,这解决了我的问题 很好的解决方案!【参考方案2】:

您可以使用SerializerMethodField 并为此编写自定义方法。

class AlbumSerializer(HyperlinkedModelSerializer):
    song_set = serializers.SerializerMethodField()
    class Meta:
        model = Album
        fields = [
            'pk',
            'timestamp',
            'song_set'
        ]

    def get_song_set(self, instance):
        songs = instance.song_set.all().order_by('-timestamp')
        return SongListSerializer(songs, many=True).data

【讨论】:

如何传递变量来过滤字段中的值? 您可以在序列化程序上下文中传递数据。 我是这样做的,但是我想如果我可以将过滤后的实例集与“AlbumSerializer(data,song_instances)”之类的数据一起传递,这是错误的,但是否有类似的方式直接将实例传递给嵌套列表序列化程序而不使用 serializermethod()? 如果您不想根据实例过滤数据,则可以将过滤后的实例传递给您的序列化程序。如果您想将数据直接传递给您的嵌套序列化,那么该数据必须是您的查询集的一部分。【参考方案3】:

在您的ViewSet 中,您可以指定一个带有自定义Prefetch 对象的查询集,您可以根据需要对其进行过滤和排序。预取只导致一个额外的数据库查询(而不是在使用 SerializerMethodField 时每个父对象一个),从而大大提高了性能。

from rest_framework import viewsets
from django.db.models import Prefetch

class AlbumViewSet(viewsets.ModelViewSet):
    queryset = Album.objects.prefetch_related(Prefetch('song_set',
        queryset=Song.objects.order_by('-timestamp')))

【讨论】:

【参考方案4】:

旧线程,但由于它仍在 Google 上弹出,我也想分享我的答案。尝试覆盖Serializer.to_representation 方法。现在您基本上可以做任何您想做的事情,包括自定义响应的排序。在你的情况下:

class AlbumSerializer(HyperlinkedModelSerializer):
    song_set = SongListSerializer(many=True, read_only=True)
    class Meta:
        model = Album
        fields = [
            'pk',
            'timestamp',
            'song_set'
        ]

    def to_representation(self, instance):
        response = super().to_representation(instance)
        response["song_set"] = sorted(response["song_set"], key=lambda x: x["timestamp"])
        return response

【讨论】:

我确信有更好的方法来做到这一点。我认为通过 ModelManager 更改订单可能是一种更好的方法。不过不确定。 这看起来可能会对运行时性能产生影响,因为排序似乎留给了服务器而不是数据库。

以上是关于Django Rest-Framework 嵌套序列化器顺序的主要内容,如果未能解决你的问题,请参考以下文章

Django rest-framework框架-组件之渲染器

django rest-framework-2没有名为apps的模块[关闭]

Django rest-framework框架-访问频率控制

Django rest-framework框架-组件之路由

如何解决 django rest-framework 中的错误请求

如何在不登录 jwt django rest-framework 的情况下获取请求数据