简体   繁体   中英

Remove specific actions from existing drf model viewset

I re-use an existing drf model viewset but there are some custom actions (assigned with @action label) that i don't need. How can I hide/remove it from django rest framework without modifying the origional model viewset?

for example

class MyViewSet(viewsets.ModelViewSet):
    @action(["get",], detail=False)
    def custom_a(self, request):
        # some stuff

    @action(["get",], detail=False)
    def custom_b(self, request):
        # some stuff

    @action(["get",], detail=False)
    def custom_c(self, request):
        # some stuff

My router

router = routers.SimpleRouter()
router.register("dummies", views.MyViewSet)


urlpatterns = [
    path('', include(router.urls)),
]

Then I will have these endpoints

GET /dummies/
GET /dummies/{id}/
POST /dummies/
PUT /dummies/{id}/
PATCH /dummies/{id}/
DELETE /dummies/{id}/
GET /dummies/custom_a/
GET /dummies/custom_b/
GET /dummies/custom_c/

Now how can I just keep 5 first views and GET /dummies/custom_a/ ?

Thanks.

There are a few ways to do this, but the "cleanest" seems overriding.

Override & Ignore

Decorators are not "inherited", so you can just re-declare the method on your derived class. This new method takes precedence over the base class method, so DRF no longer sees them as @action s.

class View1(viewsets.ModelViewSet):
    @action(['get'], detail=False)
    def act_up(self, request):
        pass

    class Meta:
        model = Client
        fields = "__all__"

class View2(View1):

    # redefine and don't add the @action decorator
    def act_up(self, request):
        pass

    class Meta:  
        model = View1.Meta.model
        fields = View1.Meta.fields

Use a router and just removed the methods you don't want

Router URLs are calculated once and then cached. You could make this happen, then filter out the ones you don't want (by name)

router = SimpleRouter()
router.register("view1", View1, basename="v1")
router._urls = [
    r for r in router.urls
    if not any(r.name.endswith(bad) for bad in ['-act-up', '-other-rt']
]

Manually route to the actions

You can manually create the routes you need, in the same manner that the base SimpleRouter does. This is a lot more work, and most certainly not worth it

list_paths = View1.as_view({"get": "list"})
detail_paths = View1.as_view({"get": "retrieve", "patch": "partial_update"})
urlpatterns = [
    path("view1/", list_paths, name="view1-list"),
    path("view1/<int:pk>/", detail_paths, name="view1-detail")
]

What this does is bind a url + http method (eg get ) to a specific DRF "action" (list/retrieve/destroy,etc). So GET view11/ calls your viewset with action=list , which the viewset then dispatches to the correct method internally.

Since you only map the things you want, there is no way for those bad "other" methods to be called.

It is a bit trickier, and confusing, and makes you responsible for the routing which, together, make it more work to maintain & understand. If there is no other way, or if you only want 1 or 2 methods from the ViewSet, then its an OK option.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM