如何覆盖POST,PATCH&多个ViewSet的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应该对网络/移动应用程序透明。
编辑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添加到所有视图集,所有GET查询现在都包含tenant_id。所以我想要的是实现所有其他HTTP方法,特别是POST和PATCH
从阅读开始,似乎我必须覆盖序列化器?是否有可能以干燥的方式进行?到目前为止,我还没弄明白如何
在创建自定义jwt_payload_handler
后,获取tenant_id的JWT的Payload看起来像这样:
{
"exp": 1477069682,
"is_superuser": true,
"email": "test-email@gmail.com",
"tenant_id": 1, #THE TENANT ID
"user_id": 1,
"username": "test-email@gmail.com"
}
答案 0 :(得分: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())
注意:您不应在模型中使用'_id'结尾的名称声明ForeignKey
,因为当您实例化模型时,instance.tenant
是租户实例,不是租户ID。 Django确实将外键存储在tenant_id列中,但这是透明的,您无需关心它。
答案 1 :(得分:0)
If you are trying to figure out how an Id is passed to view, Your answer is simple. Id is mostly passed to views through urls
. Try GET
request to your queryset with id as below.
api/url/endpoint/1
And 1 will be passed from urls to views by drf routers. Then based on the http method
, which in your case is GET appropriate action is chosen. For instance, if you are calling aforementioned url using GET,PUT,PATCH or DELETE
methods 1 is considered as reference to Id of the object
and then an orm query is performed that can be
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.
If you are passing a POST
request to api endpoint, in general scenario model name will be identified through api endpoint and then a new object will be created using the params passed.
Now to answer the other part, ie what is returned. In most case drf return a object of class Response
that can be used as JSON
object by browsers as well as mobile devices. But these Responses wont be returned from methods like get() in scenarios that involve drf viewsets to follow an mro. In those cases usually the last function, which in most cases is render_to_response()
will be returning the response.
HTH