45

Django REST Framework を使用して、作成の関連フィールドで使用できる値を制限したいと考えています。

たとえば、次の例を考えてみましょう ( 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 レンダラーのフォームのドロップダウンに入力される唯一の値であることを確認するにはどうすればよいですか?

4

10 に答える 10

41

私はカマイレオンがここで提案したことと同様のことをしました。基本的に、シリアライザーを変更してリクエストを覗き込むようにしましたが、これは間違ったにおいがしますが、仕事は完了します...これがどのように見えるかです(購入の例で例示されています):

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

allowed_objects は、ユーザーとクエリを受け取り、ユーザーがリンクする権限を持つオブジェクトのみを含むフィルター処理されたクエリを返す関数です。これは、検証とブラウズ可能な API ドロップダウン フィールドの両方で機能するようです。

于 2013-03-12T18:03:04.150 に答える
16

これが私がそれを行う方法です:

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)
于 2013-12-19T10:54:40.260 に答える
8

クエリセットを制限するために、実行時にユーザーデータまたはインスタンスにアクセスする必要があるすべての場所で init メソッドをオーバーライドする必要があるスタイルが嫌いでした。そこで、このソリューションを選択しました。

ここにインラインコードがあります。

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_frameworkにあると思います。

于 2015-11-03T02:27:15.027 に答える
3

最初に、着信 http POST/PUT がある場合にのみ「self.request.user」を許可することを確認します (これは、シリアライザーとモデルのプロパティが文字どおり「user」と名付けられていることを前提としています)。

def validate_user(self, attrs, source):
    posted_user = attrs.get(source, None)
    if posted_user:
        raise serializers.ValidationError("invalid post data")
    else:
        user = self.context['request']._request.user
        if not user:
            raise serializers.ValidationError("invalid post data")
        attrs[source] = user
    return attrs

上記をモデルシリアライザーに追加することにより、 request.user のみがデータベースに挿入されるようになります。

2) -上記のフィルターについて (filter purchaser=user) 実際には、カスタム グローバル フィルターを使用することをお勧めします (これがグローバルにフィルター処理されるようにするため)。私は自分のサービスアプリとしてのソフトウェアのために何かをしており、各 http 要求がフィルター処理されていることを確認するのに役立ちます (誰かが最初にアクセスできない「オブジェクト」を検索しようとしたときの http 404 を含む) )

私は最近 master ブランチでこれにパッチを当てたので、リストと単一のビューの両方がこれをフィルタリングします

https://github.com/tomchristie/django-rest-framework/commit/1a8f07def8094a1e34a656d83fc7bdba0efff184

3) - API レンダラーについて - 顧客に直接使用してもらっていますか? そうでない場合は、避けてください。これが必要な場合は、フロントエンドでの入力を制限するのに役立つカスタム シリアライザーを追加できる可能性があります。

于 2013-03-11T20:42:51.910 に答える
0

私は次のことをしました:

class MyModelSerializer(serializers.ModelSerializer):
    myForeignKeyFieldName = MyForeignModel.objects.all()

    def get_fields(self, *args, **kwargs):
        fields = super(MyModelSerializer, self).get_fields()
        qs = MyModel.objects.filter(room=self.instance.id)
        fields['myForeignKeyFieldName'].queryset = qs
        return fields
于 2015-06-25T15:37:37.303 に答える