-
-
Notifications
You must be signed in to change notification settings - Fork 6.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support "extra actions" for routing in the HTML forms for the browsable API #2062
Comments
This will probably be included as part of the 'admin-style browsable API'. This is all a little way off at the moment, right now so not going to get into specifics just yet. |
Right now it looks like it just falls back to rendering the form for the view's base serializer which is a little odd. |
Setting something like object = self.get_object()
context = self.get_serializer_context()
serializer = MySerializer(object, context=context) Instead of: object = self.get_object()
serializer = self.get_serializer(object) # context set automatically This would actually be a very nice improvement for both the usability of the actions and the browsable API, because when I have |
I'm about to close this issue as I feel the title doesn't match the content. |
How does DRF know what Serializer to use to generate the form for extra actions? |
They are included in the
|
I'm not sure I follow. Let's take this case:
I assume that |
@cancan101 if you have a post only, you won't get any browsable API anyway. This is the same as if you define a viewset that has a create but doesn't define a list.
|
Ignoring the case of extra actions, I don't see why a form can't be generated even if there is no list. All of the information needed to do so should be available on the View. Further I think it would be nice to support asymmetric Serializers (#2898). |
@cancan101 if you don't have a get, you don't have a form representation |
I understand, but that seems like a silly restriction. |
@cancan101 What restriction are you referring to ? |
That you need to have a |
@cancan101 how would a user fill a form without an initial get ? |
What is needed from the initial GET in order for a user to fill a form when POSTing? |
The user needs a form before he could post the data. This is a browsable API. As with every website, users get the page, fill the form and then post it. |
I slightly amended my workshop project to showcase this: |
Let's say I have an endpoint Certainly in order to render the browseable API, the browser will GET that endpoint. I would still like some way to show a nice form with the fields in the POST without having to enable GETing. |
DRF Swagger does handle this by introspecting the various actions on an end point and resolving the associated Serializer in each case. It then generates a UI based on the fields of that Serializer. |
@cancan101 Your use case looks suspicious to me. You don't want to create through |
Perhaps the example I provided of users and companies was not the best case of POST without GET. For a better example, take a look at https://developer.github.com/v3/repos/merging/#perform-a-merge. |
@cancan101 my workaround for the time being (in DRF 2.4) for declaring the serializer class for a custom route such that it renders a relevant form in the browseable API looks like this:
|
Interesting. The solution in DRF3 (and maybe 2) is even simpler: def get_serializer_class(self):
if self.action == 'myaction':
return MyCustomSerializer
return super(MyViewset, self).get_serializer_class() no more hard coding paths. |
@cancan101 looking forward to that! |
@smcoll Although I see this if
|
@cancan101 with a json request, right? With the browseable API (and an HTTP request), i'm getting the customary form and documentation that DRF provides. |
No I see that on the browseable API (using DRF3). If I add GET as a method then the form renders correctly. On Wed, May 13, 2015 at 2:34 PM smcoll [email protected] wrote:
|
@cancan101 ok, then that must be a difference between the implementations of 2.4 and 3.0 |
I don't really understand why When I have
|
So I tracked down the issue in DRF3. It actually should work where the The issue is that even though the |
There is actually an incredibly simple fix for this! This line in @tomchristie Any reason that I put up PR #2933. |
@xordoquy I think if #2933 gets merged this issue is totally solved by using something like: def get_serializer_class(self):
if self.action == 'myaction':
return MyCustomSerializer
return super(MyViewset, self).get_serializer_class() or for the more hardcore: def get_serializer_class(self):
if self.action == MyViewset.myaction.__name__:
return MyCustomSerializer
return super(MyViewset, self).get_serializer_class() |
We may get onto this at somepoint, but I don't see it as critical to 3.3.0. Dropping the milestone. |
Not sure if I'm missing something, but overriding the serializer for an action does affect the browsable API correctly. @detail_route(methods=['post'],
url_path='change-password',
serializer_class=PasswordChangeSerializer)
def change_password(self, request, pk):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
...
@xordoquy - not sure if this changed, but it works fine for us. The browsable api returns a 405, but the serializer form is rendered below the embedded response. |
Suggest we link to available actions rather than displaying them on the page itself, as described in #2934. |
A simple workaround is to include links to the custom endpoints in your object representation. I have a model with a revision log, and its serialization has a link to itself, its parent resource and its revisions (a detail route that returns a list of records). In this case, hypermedia serves the client dev and serves me :) It’s not perfect though: If I configure other methods than just GET for the custom detail view, the browsable API doesn’t show anything different than the regular object detail view. |
Just thinking out loud for a possible solution. Ideally when you make a detail route like shown below (to set activities to done), with baseview = /activities/, detail = /activities/1/
When you go to the endpoint in the browsable api (/activities/), you would get an additional html saying:
This view has a detail route: /set_done/. More info (from the comment in the detail route) . Or something like this.When you go to the detail endpoint /activities/1/, you would see the detailed view (with the url etc), plus the additional url: {"set_done_url": "http.../activities/1/set_done/}. When then GETting to http.../activities/1/set_done/ you would see: More info (from the comment in the detail route) . And because the view already shows that only PATCH, OPTIONS is allowed the user of the API would have all necessary information.What do you think? |
Hi all. For anyone still tracking this issue, I've started #5605. It's an initial implementation, and your feedback would be greatly appreciated. |
Great work!
I am definitely still interested!
|
Hi all. Aside from documentation, #5605 is now ready for review. If you have the time, extra eyes would be appreciated. |
Add support for creating HTML forms for the browsable API for "extra actions" for routing.
For example using the example from the linked page:
creating a form for calling
set_password
. Likely this would require additional arguments passed to thedetail_route
decorator (such as the serializer used,PasswordSerializer
).See: https://groups.google.com/forum/#!topic/django-rest-framework/e75osh1Wcyg
The text was updated successfully, but these errors were encountered: