2016-10-20 40 views
2

如何覆蓋多個ViewSet的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當然是強制性的。但是,我不想強​​制Web /移動客戶端始終提供tenant_id,因爲我可以從用戶的JWT令牌中獲取它。即tenant_id應該對Web /移動應用程序透明。

EDIT 2

的問題是,我想默默地/悄然/幕後的添加tenant_id沒有網絡/移動應用程序意識到。這意味着我不希望使用API​​的應用發送tenant_id JSON密鑰。

標本模型

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,) 

通過添加filter_backends所有viewsets,所有的GET查詢現在包括tenant_id。所以我想要的是實現相同的所有其他HTTP方法,特別是POST和PATCH

從閱讀它似乎我必須重寫序列化器?是否可以以乾式方式進行?到目前爲止,我還沒有想出如何

的智威湯遜在那裏我得到tenant_id有一個有效載荷看起來像這樣創建自定義jwt_payload_handler後:如果您正試圖找出如何的標識

{ 
    "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的實現,看看如何獲​​得租戶形式的請求(確保返回租戶實例,而不是它的ID,這可能無法正常工作)。

也許是這樣的:

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 

然後在序列化,聲明隱藏字段,如:

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

注意:你不應該使用一個名稱整理聲明模型中的ForeignKey與'_id',因爲當你instanciate你的模型,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 is mostly passed to views through urls。嘗試GET請求與您的查詢集與id如下。

api/url/endpoint/1 

而且1將通過drf路由器從urls傳遞到視圖。然後基於http method,在你的情況下是GET選擇適當的行動。舉例來說,如果你調用使用GET,PUT,PATCH or DELETE方法1被認爲是reference to Id of the object和上述網址,然後一個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. 

如果你傳遞一個POST請求API端點,在一般情況下型號名稱將通過API端點標識,然後使用傳遞的參數創建一個新對象。

現在回答其他部分,即什麼是返回。在大多數情況下,drf返回一個object of class Response,它可以被瀏覽器和移動設備用作JSON對象。但是這些響應不會從像get()這樣的方法返回,這些方法涉及drf視圖集遵循mro。在這些情況下,通常最後一個函數(大多數情況下爲render_to_response())將返回響應。

HTH

+0

我不在尋找如何傳遞ID。我編輯了這個問題,並提供了更多的清晰度。看看問題現在是否有意義。 – lukik