2016-10-20 7 views
2

Как переопределить POST, PATCH & УДАЛИТЬ методы для нескольких ViewSets, чтобы я мог добавить обязательный параметр backend?Переопределить методы POST, PATCH & DELETE

Логика. Я создаю приложение с несколькими арендаторами, которое имеет «tenant_id» во всех соответствующих таблицах. Этот tenant_id идентифицирует арендатора, и поэтому все запросы должны включать этот родительский ключ, чтобы пользователи не видели/модифицировали контент, который не принадлежит им.

Для Get запросов, я создал пользовательский фильтр бэкенд, который позволит мне добавить обязательный объект фильтра, чтобы ограничить то, что пользователь может получить

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) 

Я потом добавил этот фильтр ко всем классам Viewset ПОСРЕДСТВОМ filter_backends =() опция

Вопрос в том, есть ли способ достичь того же для POST, PATCH, DELETE запросов?

Мое настоящее мышление заключается в том, что можно было бы переопределить метод model.save для всех моделей? Но это не позаботится об HTTP-методе DELETE.

Прозрачный tenant_id:

В моделях tenant_id, конечно, обязательно. Тем не менее, я не хочу принуждать веб-клиента/мобильного клиента всегда предоставлять tenant_id, поскольку я могу получить его из токена JWT пользователя. т. е. tenant_id должен быть прозрачным для веб-приложения.

EDIT 2

Проблема, я хочу, чтобы тихо/спокойно/за кадром добавить tenant_id без веб/мобильное приложение быть в курсе. Значение Я не хочу, чтобы приложения, использующие API, отправляли ключ JSON tenant_id.

образца Модель

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

От чтения кажется, что мне нужно переопределить сериализаторы? Можно ли это сделать СУХОЙ? До сих пор я не понял, как

JWT, где я получаю 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, чтобы узнать, как получить запрос арендатора (обязательно верните экземпляр арендатора, а не его идентификатор, который может не работать).

Может быть что-то вроде:

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', потому что, когда вы создаете свою модель, instance.tenant является экземпляром Арендатора, а не идентификатором арендатора. Django хранит внешний ключ в столбце tenant_id, но это прозрачно, и вам не нужно заботиться об этом.

+0

Использует аутентификацию Django. Кроме того, 'ForeignKey' не объявляются с помощью' _id', и я исправил модель образца, которую я дал. В вашем предположении, где бы я разместил «CurrentTenantDefault»? Кроме того, идентификатор арендатора должен быть извлечен из частного раздела токена JWT. Я использую 'django-rest-framework-jwt' и при входе в систему я добавил tenant_id к токену. См. Пример, который я добавил в вопрос. – lukik

+0

Обычно я храню его в модуле 'serializers' или когда сериализаторы разделены на несколько модулей, что-то вроде' serializers.core'. Я обновил ответ на примере «CurrentTenantDefault», используя функцию 'get_tenant_id_from_token'. –

+0

Спасибо, сэр. Это работает так, как ожидалось, и его просто понять – lukik

0

Если вы пытаетесь выяснить, как идентификатор передается для просмотра, ваш ответ прост. Id is mostly passed to views through urls. Попробуйте GET запрос на ваш запрос с идентификатором, как показано ниже.

api/url/endpoint/1 

И 1 будет передан из URL-адресов на виды маршрутизаторами drf. Затем на основе http method, который в вашем случае GET выбрано соответствующее действие.Например, если вы вызываете вышеупомянутый URL с помощью GET,PUT,PATCH or DELETE методов 1 рассматривается как reference to Id of the object и затем запрос ОРМ выполняется, что может быть

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, а затем новый объект будет создан с использованием переданных параметров.

Теперь ответьте на другую часть, то есть на то, что возвращается. В большинстве случаев drf возвращает object of class Response, который может использоваться как объект JSON браузерами, так и мобильными устройствами. Но эти ответы не будут возвращены из таких методов, как get() в сценариях, которые включают в себя виды просмотра drf, чтобы следовать за mro. В таких случаях обычно возвращается последняя функция, которая в большинстве случаев равна render_to_response().

НТН

+0

Я не ищу, как проходит идентификатор. Я отредактировал вопрос и дал больше ясности. Посмотрите, есть ли сейчас вопрос. – lukik

Смежные вопросы