2016-10-20 16 views
2

POST、PATCH &複数のビューセットのDELETEメソッドをオーバーライドして、必須のバックエンドパラメータを追加できるようにするにはどうすればよいですか?POST、PATCH&DELETEメソッドをオーバーライド

ロジック。すべての関連するテーブルに「tenant_id」を持つマルチテナントアプリを構築していますか?このtenant_idはテナントを識別します。したがって、すべてのリクエストには、ユーザが自分のものでないコンテンツを表示または変更しないようにするために、この親キーを含める必要があります。

クエリを取得するために、私は、その後経由して、すべてのビューセットクラスにこのフィルタを追加した

class CustomFilterBackend(filters.BaseFilterBackend): 
    """ 
    Filter that only allows users to see entries related to their tenant. 
    """ 
    def filter_queryset(self, request, queryset, view): 
     tenant_id = get_tenant_id_from_token(request) 
     return queryset.filter(is_deleted=False, tenant_id=tenant_id) 

ユーザーが得ることができるものを制限するための必須フィルタオブジェクトを追加するために私を可能にするカスタムフィルタのバックエンドを作成しましたfilter_backends =()オプション

質問POST、PATCH、DELETEリクエストでも同じことを達成する方法はありますか?

私の現在の考えは、すべてのモデルでmodel.saveメソッドをオーバーライドするということですか?しかし、それはHTTP DELETEメソッドの世話をしません。

透明tenant_id:モデルで

、tenant_idはもちろん必須です。しかし、私はユーザーのJWTトークンから取得できるので、web/mobileクライアントに常にtenant_idを提供するよう強制したくありません。テナントIDはウェブ/モバイルアプリに対して透過的である必要があります。

EDIT 2

問題は、私は黙っ/静か/舞台裏はウェブ/モバイルアプリが意識することなくtenant_idを追加したい、です。意味私はtenant_id JSONキーを送信するAPIを使用してアプリをしたくない。

すべてのビューセットにfilter_backendsを追加することによりサンプルモデル

class SampleModel(models.Model): 
    """ 
    Sample model 
    """ 
    title = models.CharField(max_length=100) 
    tenant = models.ForeignKey(Tenant, on_delete=models.PROTECT) 


class CustomFilterBackend(filters.BaseFilterBackend): 
""" 
Filter that only allows users to see entries related to their tenant. 
""" 
def filter_queryset(self, request, queryset, view): 
    tenant_id = get_tenant_id_from_token(request) 
    return queryset.filter(is_deleted=False, tenant_id=tenant_id) 


class SampleViewSet(ListCreateRetrieveUpdateViewSet): 
    """ 
    Sample viewset 
    """ 
    serializer_class = SampleModelSerializer 
    permission_classes = (HasPermission) 
    queryset = SampleModel.objects.all() 
    filter_backends = (CustomFilterBackend,) 

は、すべてのGETクエリが今tenant_idが含まれます。だから私が望むのは、他のすべてのHTTPメソッド、特にPOSTとPATCHと同じものを達成することです。

シリアライザをオーバーライドする必要があると思われます。それはDRY方法でそれをすることは可能ですか?

{ 
    "exp": 1477069682, 
    "is_superuser": true, 
    "email": "[email protected]", 
    "tenant_id": 1, #THE TENANT ID 
    "user_id": 1, 
    "username": "[email protected]" 
} 

答えて

1

私はHiddenFieldを使用することをお勧めしてCurrentUserDefaultがするようテナントを取得するクラスを作成します。例えば

あなたは現在ログインしているユーザーのテナントを設定したい場合は(トークン、独立して自分の認証方法の、セッションを...):

class CurrentTenantDefault(CurrentUserDefault): 
    def __call__(self): 
     current_user = super().__call__() 
     return current_user.tenant 

あなたはDjangoの認証を使用しない場合は(あなたは!)、CurrentUserDefaultのインプリメンテーションを見て、テナントのリクエストを得る方法を確認してください。

たぶんのようなもの:

tenant = serializers.HiddenField(default=CurrentTenantDefault()) 

:あなたが名前の仕上げであなたのモデルにForeignKeyを宣言するべきではありません

class CurrentTenantDefault(): 
    def set_context(self, serializer_field): 
     request = serializer_field.context['request'] 
     tenant_id = get_tenant_id_from_token(request) 
     self.tenant = Tenant.objects.get(pk=tenant_id) 

    def __call__(self): 
     return self.tenant 

が続いてシリアライザで、同様に隠しフィールドを宣言'_id'を指定すると、instance.tenantはテナントIDでなくテナントインスタンスになります。 Djangoはtenant_idカラムに外部キーを格納しますが、これは透過的なので、気にする必要はありません。

+0

Djangoの認証を使用しています。また、 'ForeignKey'は' _id'を使って宣言されていません。与えられたサンプルモデルを修正しました。あなたの提案では、私は 'CurrentTenantDefault'をどこに置くのですか?また、テナント識別子は、JWTトークンのプライベートセクションからフェッチされます。 'django-rest-framework-jwt'を使用しています。ログイン時にtenant_idをトークンに追加しました。私が質問に追加した例を見てください。 – lukik

+0

私は通常、 'serializers'モジュールに入れています。あるいは、シリアライザがいくつかのモジュールに分割されている場合、' serializers.core'のようなものです。あなたの 'get_tenant_id_from_token'関数を使って' CurrentTenantDefault'の例で答えを更新しました。 –

+0

ありがとうございます。これは期待通りに機能し、理解しやすい – lukik

0

あなたはどのようにIDを把握しようとしている場合:これまでのところ私はtenant_idを取得していますJWTは、カスタムjwt_payload_handlerを作成した後、このように見てペイロードを持っているかに

考え出したていませんあなたの答えは簡単です。 Id is mostly passed to views through urls。以下のようにIDを持つクエリーセットにGETリクエストをお試しください。

api/url/endpoint/1 

そして1はdrfルータのビューにURLから渡されます。あなたのケースでは、適切なアクションが選択されているhttp methodに基づいています。たとえば、あなたがGET,PUT,PATCH or DELETE方法1を使用して、前述のURLを呼び出している場合reference to Id of the objectとみなされ、あなたが一般的なシナリオのモデル名で、APIエンドポイントにPOSTリクエストを渡している場合は、ORMのクエリは

model.objects.get(id=1) #if request.method is GET 
model.objects.get(id=1).delete() #if request.method is DELETE 
model.objects.get(id=1) #if request.method is PATCH or PUT and then the fields of the corresponding object will be updated. 

できることを行っていますapiエンドポイントで識別され、渡されたパラメータを使用して新しいオブジェクトが作成されます。

もう一方の部分、つまり返される内容に答えることができます。ほとんどの場合、drfはを返します。これはブラウザでもモバイルデバイスでもJSONオブジェクトとして使用できます。しかし、これらのレスポンスは、mroに従うdrfビューセットを含むシナリオでget()のようなメソッドから返されることはありません。そのような場合、通常、最後の関数はほとんどの場合render_to_response()ですが、応答を返します。

HTH

+0

IDの受け渡し方法を探していません。私はその質問を編集し、より明確にしました。その質問が今や意味をなさないかどうかを見てください。 – lukik

関連する問題