【问题标题】:Add data to the list returned by Django REST Framework's ModelViewSet将数据添加到 Django REST Framework ModelViewSet 返回的列表中
【发布时间】:2016-08-01 23:11:27
【问题描述】:

有没有办法向使用 Django REST Framework 的 ModelViewSet 的视图的列表 API 调用返回的结果数据添加自定义数据?

我的 ModelViewSet 视图附加到 Items 模型。

除了由 LIST API 调用返回的项目列表之外,我还想查询一个单独的 Seen 模型并与项目一起返回每个项目被看到的次数。

我还想在返回的数据中添加“今日报价”。

我已经搜索了 DRF 文档,但找不到任何关于如何执行此操作的提及。

这是我的代码。 Item 序列化器是:

class ItemSerializer(serializers.ModelSerializer):
    username = serializers.SerializerMethodField()

    def get_username(self, obj):
        """
        Note that query params can be accessed here as follows:
        request = self.context['request']
        print request.query_params.get['fields']
        """
        value = str(obj.owner)
        return value

    def get_keywords(self, obj):
        value = str(obj.keywords)
        return value

    class Meta:
        model = Item
        fields = ('id', 'url', 'item_type', 'title', 'credits_applied', 'credits_left', 'credits_gifted', 'username', 'liked', 'disliked')

视图如下所示:

class ItemViewSet(viewsets.ModelViewSet):
    queryset = Item.objects.all().order_by('-date_added')
    serializer_class = ItemSerializer

    # When POSTing (creating) a new item add appropriate User instance to the serializer
    def perform_create(self, serializer):
        creator = User.objects.get(pk=self.request.data['owner_id'])
        serializer.save(owner=creator)

     def get_queryset(self):
        this_user = self.request.query_params.get('user', None)
        restrict_to_items_from_user_id = self.request.query_params.get('from', None)
        quantity = self.request.query_params.get('num', 20)

        # Use query params to determine what to return

        if restrict_to_items_from_user_id is not None:
            # API: /api/items/from=<id>
            # Return items owned by a specific user. Used by someone to get a list of the items they have added

            queryset = Item.objects.filter(owner=restrict_to_items_from_user_id, active=True).order_by('-date_added')[0:int(quantity)]

        elif this_user is not None:
            # API: /api/items/user=<id>
            # Return unseen items for the given user's browse feed

            # queryset = Item.objects.all().order_by('-date_added')[0:int(quantity)]
            queryset = Item.objects.filter(active=True, credits_left__gt=0).exclude(pk__in=Seen.objects.filter(user_id=this_user).values_list('item_id', flat=True))[0:int(quantity)]

        # :TO DO: Add option to list the items a user has liked!

        else:
            # API: /api/items
            # Return items not specific to a particular user (used for testing the app or when user wants to see stuff they have seen before)

            queryset = Item.objects.filter(active=True, credits_left__gt=0)[0:int(quantity)]

        return queryset

Item 和 Seen 模型是:

class Item(models.Model):

    ITEM_TYPES = (
        ('V', 'Vine'),
        ('Y', 'YouTube'),
        ('P', 'Photo'),         # Photo is stored by us on a CDN somewhere
        ('F', 'Flickr'),
        ('I', 'Instagram'),
        ('D', 'DeviantArt'),
        ('5', '500px'),
    )
    owner           = models.ForeignKey(User, on_delete=models.CASCADE)     # Id of user who owns the item
    title           = models.CharField(max_length=60, default='')           # URL of where item resides (e.g. Vine or YouTube url)
    url             = models.CharField(max_length=250, default='', unique=True)
                                                                            # URL of where item resides (e.g. Vine or YouTube url)
    item_type       = models.CharField(max_length=1, choices=ITEM_TYPES)    # Type of item (e.g. Vine|YoutTube|Instagram|etc.)
    keywords        = models.ManyToManyField(Keyword, related_name='keywords')
                                                                            # E.g. Art, Travel, Food, etc.
    credits_applied = models.IntegerField(default=10, help_text='Total number of credits applied to this item including any given by VeeU admin')
                                                                            # Records the total number of credits applied to the Item
    credits_left    = models.IntegerField(default=10, help_text='The number of credits still remaining to show the item')
                                                                            # Number of credits left (goes down each time item is viewed
    credits_gifted  = models.IntegerField(default=0, help_text='The number of credits this item has been gifted by other users')
                                                                            # Number of credits users have gifted to this item
    date_added      = models.DateTimeField(auto_now_add=True)               # When item was added
    liked           = models.IntegerField(default=0)                        # Number of times this item has been liked
    disliked        = models.IntegerField(default=0)                        # Number of times this item has been disliked
    active          = models.BooleanField(default=True, help_text='If you mark this item inactive please say why in the comment field. E.g. "Inapproriate content"')
                                                                            # True if item is available for showing
    comment         = models.CharField(max_length=100, blank=True)          # Comment to be applied if item is inactive to say why

    # Add defs here for model related functions

    # This to allow url to be a clickable link
    def item_url(self):
        return u'<a href="%s">%s</a>' % (self.url, self.url)
    item_url.allow_tags = True

    def __str__(self):
        return '%s: Title: %s, URL: %s' % (self.owner, self.title, self.url)

# Record of which items have been viewed, when, and whether they were liked or not
class Seen(models.Model):
    item_seen           = models.ForeignKey(Item, on_delete=models.CASCADE)     # id of the item that has been seen
    who_saw             = models.ForeignKey(User, on_delete=models.CASCADE)     # id of user who viewed it
    date_seen           = models.DateTimeField(auto_now_add=True)               # When item was viewed
    liked               = models.BooleanField(help_text='If the item was liked this is set to true')

    class Meta:
        unique_together = ('item_seen', 'who_saw',)

【问题讨论】:

    标签: django django-rest-framework


    【解决方案1】:

    如果您想将新数据附加到常规响应中,您可以这样做:

    class ItemViewSet(viewsets.ModelViewSet):
        ...
    
        def list(self, request, *args, **kwargs):
            custom_data = {
                'list_of_items': ItemSerializer(self.get_queryset(),many=true).data  # this is the default result you are getting today
                'quote_of_the_day': <code to compute Quote of the day>
                'number_of_times': <code to compute number of times>
            }
            return Response(custom_data)
        ...
    

    尽管如此,从您的评论来看:“每个项目被看到的次数。”时间的数量看起来像是每个项目的新属性,所以你也可以这样做:

    class ItemViewSet(viewsets.ModelViewSet):
        ...
    
        def list(self, request, *args, **kwargs):
            custom_data = {
            'list_of_items': ItemSerializer(self.get_queryset(), many=true).data  # this is the default result you are getting today
             'quote_of_the_day': <code to compute Quote of the day>
            })
            return Response(custom_data)
    ...
    
    class ItemSerializer(serializers.ModelSerializer):
        ...
        number_of_times = serializers.SerializerMethodField()
        ...
    
        def get_number_of_times(self):
            # compute and return the number of times the Item has been seen.
        ...
    

    【讨论】:

    • 你能指出我的任何示例代码吗?我不知道该怎么做。我已经有一个标准列表结果的序列化程序。
    • 能否提供涉及的模型和序列化程序?那我可以帮你定制
    • 谢谢@trinchet 我已将我的代码添加到问题中。希望它现在更有意义!
    • 是的,我现在有了一个更好的主意,我更新了我的回复,为您提供了两种可能的方法。
    • 是否需要在 ItemSerializer 类元字段中添加“number_of_times”?
    【解决方案2】:

    如果你使用分页器,你可以使用这个:

    from collections import OrderedDict
    
    from rest_framework.pagination import LimitOffsetPagination
    from rest_framework.response import Response
    from rest_framework import viewsets
    
    class CustomLimitOffsetPagination(LimitOffsetPagination):
    
        def get_paginated_response(self, data):
    
            return Response(
                OrderedDict([
                    ('count', self.count),
                    ('next', self.get_next_link()),
                    ('previous', self.get_previous_link()),
                    ('results', data),
                    ('extra_field', <your_value>),
                ]))
    
    class SomeViewset(viewsets.ModelViewSet):
        pagination_class = CustomLimitOffsetPagination
    

    【讨论】:

      猜你喜欢
      • 2015-11-08
      • 2021-07-18
      • 1970-01-01
      • 2020-12-17
      • 2014-08-27
      • 2021-10-02
      • 1970-01-01
      • 1970-01-01
      • 2014-08-24
      相关资源
      最近更新 更多