How to implement method in Django REST?

Have the next Django REST question.

I have the view.

class MessageViewSet(viewsets.ModelViewSet):
    serializer_class = MessageSerializer
    queryset = Message.objects.filter(isread = False)
    def mark_read():
        queryset = Message.objects.update(isread=True)
        return Response({'read':queryset})

And router in urls.py

router = SimpleRouter() router.register(r'api/get_messages', MessageViewSet)

urlpatterns = [
    url(r'^$', MainView.as_view(), name='main'),
    url(r'^', include(router.urls)) ]

Now i have 'get_messages' page which shows all list.

How can i implement a method which would change 'isread' value of model instanse from False to True, when I visit a 'mark_read' page? As you can see, i tried to write method in the class. But when i'm trying to call it in urls in this way:

router.register(r'api/mark_read', MessageViewSet.mark_read),

Here comes an error.

assert queryset is not None, 'base_name argument not specified, and could ' \ AssertionError: base_name argument not specified, and could not automatically determine the name from the viewset, as it does not have a .queryset attribute.

Maybe i shouldnt use router, and rewrite view and urls in other way. If u know how to solve this problem, please answer. Thanks.

Upvotes: 3

Views: 151

Answers (3)

Thanks to @ivan-semochkin and @Shaumux for replies. Advices were really helpful.

That is my route. I used detail_route instead of list_route.

@detail_route(methods=['get','put'], url_name='mark_read/')
def mark_read(self, request, pk=None):
    queryset = Message.objects.filter(pk=pk).update(isread=True)
    return Response({'read':queryset})

Now 'isread' value is changing wnen i visit 'mark_read' page. Link: "api/get_messages/pk/mark_read"

Does anyone know, is it posslible to make links looking the next way: "api/get_messages" - list, "api/mark_read/pk" - changing isread value.

Is it possible to create something like this? "api/mark_read?=pk"

Upvotes: 0

Shaumux
Shaumux

Reputation: 745

Since you are using a model viewset you can directly use put or patch rest method to send the desired value for the desired field as the data. Ideally in rest get should not change model values. If you really want a different end point put the list_route or detail_route decorator on your mark_read method, and make them a valid call for only a put and/or patch call

from rest_framework.decorators import list_route

    class MessageViewSet(viewsets.ModelViewSet):

        @list_route(methods=['Patch', 'PUT'])
        def mark_read(self, request):
            queryset = Message.objects.update(isread=True)
            return Response({'read':queryset})

Upvotes: 2

Ivan Semochkin
Ivan Semochkin

Reputation: 8897

You can use detail_route or list_route decorators.

from rest_framework.decorators import list_route

class MessageViewSet(viewsets.ModelViewSet):

    @list_route()
    def mark_read(self, request):
        queryset = Message.objects.update(isread=True)
        return Response({'read':queryset})

With that mark_read method will be available at api/get_messages/mark_read. And you don't need to create separate router, just use one you created for MessageViewSet

docs reference

Upvotes: 2

Related Questions