0

According to REST design guidelines it is better to map state changing actions like activate, publish, share to PUT methods body as fields.

Like that:

PUT /api/articles/32
{
   "activated": true
}

My question is how to dispatch in put method handler in the backend which action is which. How do i know if it is "activated" or it is "shared"? Any suggestions?

@detail_route(methods=['PUT'])
def put(self, request, *args, **kwargs):
    # if action == 'activate'
    #    activate()
    # if action == 'publish'
    #    publish()

    return Response(status=status.HTTP_200_OK)
Vladimir Nani
  • 2,774
  • 6
  • 31
  • 52

3 Answers3

1

My understanding is that you want to perform some action on the article. If you want to do it in a purely REST way you should add some kind of transaction for each action you have. So something like this

POST /api/publish-article-transaction
{
   articleId: 2
}

-- Response

{
  publish_article_transaction: {
    id: 123,
    articleId: 2,
    status: ok
  }
}

The logic behind the ´POST´ is that you create a transaction object rather than modifying the article itself.

Here is a good answer to your question as well REST actions and URL API design considerations

Another more general example would be

POST /api/article-transaction
{
  action: "publish",
  articleId: 2
}
Community
  • 1
  • 1
Erik Johansson
  • 1,188
  • 1
  • 8
  • 22
  • Agree but still, what i want to understand is in case im using approach with PUT method for updating resource. Where do i need to pass action, is it body, or headers or maybe i can infer that from body itself on backend, which i wont like to do. – Vladimir Nani May 20 '16 at 09:36
  • So when you update the article with some properties you want it to have some potential side effect? I.e if you update `article.published = true` you want to execute some publishing action? – Erik Johansson May 20 '16 at 09:39
  • If you want to use `PUT` the action must be inferred by the modification of the article. So everytime you update `published` an email would be sent. I wouldn't really recommend this approach though. – Erik Johansson May 20 '16 at 09:52
0

I think we can convert actions like activate, publish or share to resources(nouns) for RESTful urls.

Article Activation:

PUT /api/article-activation/<pk>/ # activate an article

DELETE /api/article-activation/<pk>/ # deactivate an article

Article Publishing:

PUT /api/article-publication/<pk>/ # publish an article

DELETE /api/article-publication/<pk>/ # unpublish an article

For reference: (Taken from the article link you mentioned in comments:)

What about actions that don't fit into the world of CRUD operations?

This is where things can get fuzzy. There are a number of approaches:

1. Restructure the action to appear like a field of a resource. This works if the action doesn't take parameters. For example an activate action could be mapped to a boolean activated field and updated via a PATCH to the resource.

2. Treat it like a sub-resource with RESTful principles. For example, GitHub's API lets you star a gist with PUT /gists/:id/star and unstar with DELETE /gists/:id/star.

3. Sometimes you really have no way to map the action to a sensible RESTful structure. For example, a multi-resource search doesn't really make sense to be applied to a specific resource's endpoint. In this case, /search would make the most sense even though it isn't a resource. This is OK - just do what's right from the perspective of the API consumer and make sure it's documented clearly to avoid confusion.

Rahul Gupta
  • 46,769
  • 10
  • 112
  • 126
  • 1
    In this case it is not like a "resource" more of a RPC. I am using following guidelines for designing api http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api#restful – Vladimir Nani May 20 '16 at 09:29
  • @VladimirNani Yes, we should not directly add an action. It won't exactly be RESTful. We should modify our URLs to actually represent a resource instead of an action in the URL. Updated the ans. – Rahul Gupta May 20 '16 at 11:35
0

I did something similar, only a little bit differently, like

{"do": "activate"}

but it can also be done your way.

First, I created a method on model which did the job, def activate(), like setting attribute activated to true. Then in the partial_update method (it's for PATCH, for put it's update (or put, depending on which APIViews you are using)) I retrieved the value like this:

action = request.query_params.get('do')
if action == 'activate':
    something.activate()

in your case it would be a little bit more different because you just set a parameter "activated" to "true" or false, but you still need this line:

activated = request.query_params.get('activated')