动态限制相关字段的queryset

2024-04-25 08:39:20 发布

您现在位置:Python中文网/ 问答频道 /正文

使用Django REST框架,我想限制哪些值可以在创建的相关字段中使用。

例如,考虑以下示例(基于http://django-rest-framework.org/api-guide/filtering.html上的筛选示例,但已更改为ListCreateAPIView):

class PurchaseList(generics.ListCreateAPIView)
    model = Purchase
    serializer_class = PurchaseSerializer

    def get_queryset(self):
        user = self.request.user
        return Purchase.objects.filter(purchaser=user)

在本例中,如何确保在创建时买方可能只等于self.request.user,并且这是可浏览API呈现程序中表单下拉列表中填充的唯一值?


Tags: djangoorgself框架resthttp示例request
3条回答

我是这样做的:

class PurchaseList(viewsets.ModelViewSet):
    ...
    def get_serializer(self, *args, **kwargs):
        serializer_class = self.get_serializer_class()
        context = self.get_serializer_context()
        return serializer_class(*args, request_user=self.request.user, context=context, **kwargs)

class PurchaseSerializer(serializers.ModelSerializer):
    ...
    def __init__(self, *args, request_user=None, **kwargs):
        super(PurchaseSerializer, self).__init__(*args, **kwargs)
        self.fields['user'].queryset = User._default_manager.filter(pk=request_user.pk)

我不喜欢在需要访问用户数据或运行时实例来限制queryset的每个地方都必须重写init方法的风格。所以我选择了this solution

这是内联代码。

from rest_framework import serializers


class LimitQuerySetSerializerFieldMixin:
    """
    Serializer mixin with a special `get_queryset()` method that lets you pass
    a callable for the queryset kwarg. This enables you to limit the queryset
    based on data or context available on the serializer at runtime.
    """

    def get_queryset(self):
        """
        Return the queryset for a related field. If the queryset is a callable,
        it will be called with one argument which is the field instance, and
        should return a queryset or model manager.
        """
        # noinspection PyUnresolvedReferences
        queryset = self.queryset
        if hasattr(queryset, '__call__'):
            queryset = queryset(self)
        if isinstance(queryset, (QuerySet, Manager)):
            # Ensure queryset is re-evaluated whenever used.
            # Note that actually a `Manager` class may also be used as the
            # queryset argument. This occurs on ModelSerializer fields,
            # as it allows us to generate a more expressive 'repr' output
            # for the field.
            # Eg: 'MyRelationship(queryset=ExampleModel.objects.all())'
            queryset = queryset.all()
        return queryset


class DynamicQuersetPrimaryKeyRelatedField(LimitQuerySetSerializerFieldMixin, serializers.PrimaryKeyRelatedField):
    """Evaluates callable queryset at runtime."""
    pass


class MyModelSerializer(serializers.ModelSerializer):
    """
    MyModel serializer with a primary key related field to 'MyRelatedModel'.
    """
    def get_my_limited_queryset(self):
        root = self.root
        if root.instance is None:
            return MyRelatedModel.objects.none()
        return root.instance.related_set.all()

    my_related_model = DynamicQuersetPrimaryKeyRelatedField(queryset=get_my_limited_queryset)

    class Meta:
        model = MyModel

这样做的唯一缺点是需要显式设置相关的序列化程序字段,而不是使用由ModelSerializer提供的自动字段发现。不过,我希望默认情况下rest_框架中会包含类似的内容。

我最后做了类似于Khamaileon suggested here的事情。基本上,我修改了序列化程序以窥视请求,哪种味道不对,但它完成了任务。。。下面是它的外观(以购买示例为例):

class PurchaseSerializer(serializers.HyperlinkedModelSerializer):
    def get_fields(self, *args, **kwargs):
        fields = super(PurchaseSerializer, self).get_fields(*args, **kwargs)
        fields['purchaser'].queryset = permitted_objects(self.context['view'].request.user, fields['purchaser'].queryset)
        return fields

    class Meta:
        model = Purchase

permitted_objects是一个函数,它接受一个用户和一个查询,并返回一个筛选查询,该查询只包含用户有权链接到的对象。这似乎对验证和可浏览的API下拉字段都有效。

相关问题 更多 >