Adding views, not only viewsets to Browsable API via routers #7830
-
It is often desirable to be able to add views, not viewsets, to the Browsable API. For instance, see: http://stackoverflow.com/questions/18818179/routing-api-views-in-django-rest-framework. There are custom router solutions, such as this outdated HybridRouter implementation on stackoverflow, but it would be nice to have this functionality out-of-the-box and consistent with evolution of DRF |
Beta Was this translation helpful? Give feedback.
Replies: 25 comments 2 replies
-
My current solution, based on this post, is as follows: class HybridRouter(routers.DefaultRouter):
def __init__(self, *args, **kwargs):
super(HybridRouter, self).__init__(*args, **kwargs)
self._api_view_urls = {}
def add_api_view(self, name, url):
self._api_view_urls[name] = url
def remove_api_view(self, name):
del self._api_view_urls[name]
@property
def api_view_urls(self):
ret = {}
ret.update(self._api_view_urls)
return ret
def get_urls(self):
urls = super(HybridRouter, self).get_urls()
for api_view_key in self._api_view_urls.keys():
urls.append(self._api_view_urls[api_view_key])
return urls
def get_api_root_view(self):
# Copy the following block from Default Router
api_root_dict = {}
list_name = self.routes[0].name
for prefix, viewset, basename in self.registry:
api_root_dict[prefix] = list_name.format(basename=basename)
# In addition to that:
api_view_urls = self._api_view_urls
class APIRoot(views.APIView):
_ignore_model_permissions = True
def get(self, request, *args, **kwargs):
ret = OrderedDict()
namespace = request.resolver_match.namespace
for key, url_name in api_root_dict.items():
if namespace:
url_name = namespace + ':' + url_name
try:
ret[key] = reverse(
url_name,
args=args,
kwargs=kwargs,
request=request,
format=kwargs.get('format', None)
)
except NoReverseMatch:
# Don't bail out if e.g. no list routes exist, only detail routes.
continue
# In addition to what had been added, now add the APIView urls
for api_view_key in api_view_urls.keys():
namespace = request.resolver_match.namespace
if namespace:
url_name = namespace + ":" + api_view_key
ret[api_view_key] = reverse(url_name,
args=args,
kwargs=kwargs,
request=request,
format=kwargs.get('format', None))
return response.Response(ret)
return APIRoot.as_view() |
Beta Was this translation helpful? Give feedback.
-
Closing given lack of interest. We may reconsider if this is subsequently raised by other developers. Not sure I see the value over simply adding views explicitly to the URL conf. |
Beta Was this translation helpful? Give feedback.
-
It seems to me that the value is that it would add those urls to the 'browsable' api? Having just tried it and cast around on SO, it appears to me that something in the vein of the solution shown by BurkovBA above (or some other significant customisation) is still necessary in order to include certain 'single' views in the browsable API - for example a url that returns a single detail view and which doesn't require any kind of id to be passed (e.g. because its an aggregate view, or a singleton model). Apologies if I'm missing something obvious. Thanks for DRF btw @tomchristie - it's great! |
Beta Was this translation helpful? Give feedback.
-
It feels quite complex to me while writing the same view as a |
Beta Was this translation helpful? Give feedback.
-
@xordoquy True, but pure views (especially function-based) give you much more flexibility. E.g. you want to add api schema view to the browsable api, what do you do? |
Beta Was this translation helpful? Give feedback.
-
On revisiting today, I quickly realised actually I don't have to customise to avoid passing an id if I set (or leave) detail as false on the @action decorator. Last night I was a bit whacked and I guess I had wrongly assumed that if I didn't set detail = True it would only allow the result to be passed back as an array. Thought I'd mention this for anyone else as dozy, happening on this thread. I don't know whether or not leaving detail = False is broadly inline with the 'idea' behind the flag when I'm presenting a view that isn't a collection of anything - however even it wasn't quite, that's probably quibbling... For me this helps and would address many of my use cases even if occasionally I had to create an otherwise unnecessary viewset and I do get that it might keep the internals simpler.. However obviously there may be some cases (such as the api schema one just cited by @BurkovBA) where it might be necessary/really helpful to be able to install things that aren't viewset @action s at all and/or have them registered at root, - so just FWIW personally I still think it might be great to have that supported :) |
Beta Was this translation helpful? Give feedback.
-
I would like to add my token auth endpoint to the API browser so my users actually know where to find it, but since ObtainAuthToken inherits from APIView I can't find a good way to do that. Am i supposed to just reimplement that whole thing as a viewset then just to be able to use DefaultRouter and get the URL to show up? |
Beta Was this translation helpful? Give feedback.
-
It's not an ideal solution, but you should be able to create a viewset that inherits class AuthTokenViewSet(ObtainAuthToken, ViewSet):
def create(self, request, *args, **kwargs):
# note that we need to call super here, so that we call `ObtainAuthToken.post`
# instead of the `post` method set by `ViewSet.as_view()`
return super().post(request, *args, **kwargs) Let me know if that does/doesn't work. |
Beta Was this translation helpful? Give feedback.
-
That sees to work nicely for my use case thanks! Wouldnt have thought to use multiple inheritance myself. |
Beta Was this translation helpful? Give feedback.
-
Or, subclass |
Beta Was this translation helpful? Give feedback.
-
I too like the idea of adding views to the browseable API using the router. But this would be solved if there was a way to implement complex path_url in the action inside a ModelViewSet. For example, let's say I have an UserViewSet and that I would like to have two additional endpoints: -
The fields uidb64 and token are used to confirm that the user clicked the reset_password_link that was sent to his/her email. The previous is a common use case scenario. You could argue that given your are doing a POST request to users/reset_password///, you could simplify the url to users/reset_password/ and send uidb64 and token as POST params. But in a similar case, where you wouldn't use POST, like in users/activate_account/// (GET request), that wouldn't be possible. That's why I think it would be great to allow to enable complex url_path in the action decorator function. |
Beta Was this translation helpful? Give feedback.
-
this could be moved to the discussion section |
Beta Was this translation helpful? Give feedback.
-
I'd like to broadly add my support for this kind of use case. The overall issue seems to be that people want to add things to the api root view that is produced by
My own use case is that I want to be able to use It's true that I'm a newbie to DRF and so perhaps what I want can be easily handled using The above solution might work in my case where I already want the default functionality of a |
Beta Was this translation helpful? Give feedback.
-
I would also like to be able to add Views and not just Viewsets to the Browseable Api. |
Beta Was this translation helpful? Give feedback.
-
I came here to say that I would like to be able to add Views to the Browseable API too. |
Beta Was this translation helpful? Give feedback.
-
Being able to add APIViews to the browsable API would be awesome! Tried to do just that some moments ago and ended up here. :( |
Beta Was this translation helpful? Give feedback.
-
I would also use this feature on day one of release. My use case is a bit more complicated that most probably, but I use If I instead could write an
all with minimal hacks. Edit: just wanted to add that of course I don't expect that DRF adds explicit support for my use-case, but I did want to highlight that while using |
Beta Was this translation helpful? Give feedback.
-
I ALSO want this. can please add this as it would do so much to increase the capability of DRF while not requiring too much work on the DRF team |
Beta Was this translation helpful? Give feedback.
-
I am happy to join this party too - my project has a series of custom endpoints that complement the Model Viewsets and would look nice in the browsable API grouped together. |
Beta Was this translation helpful? Give feedback.
-
Supporting this, simplify sharing knowledge about endpoints within the teams without having to write superfluous documentation (that needs to be updated anytime something changes…) |
Beta Was this translation helpful? Give feedback.
-
Me too. Would like to be able to add APIView url to the DRF browsable API in some simple way. |
Beta Was this translation helpful? Give feedback.
-
I think this is a big shortcoming for the DRF. Defaultrouter and browsable API is a great and functional solution to make the project controlled by front-end developers. Please take action. |
Beta Was this translation helpful? Give feedback.
-
Another vote from me. My use case - I would like to use DRF permissions / throttles / authentication / routing for an auth_check view that should just return status codes. I would like this to be in our browsable API & URL path (/api/v2/). I thought I would just be able to subclass APIView and get on it but apparently not. I think it's confusing for DRF to provide a view called "APIView" that cannot be added to the default API router. Most SO snippets for a hybrid router are not working anymore either - looks like |
Beta Was this translation helpful? Give feedback.
-
I'm in the exact use case as andre above, so another vote from me. |
Beta Was this translation helpful? Give feedback.
-
Found a working solution to put non-viewset views on the root page of a DefaultRouter, based on carltongibson's suggestion above: In urls.py:
|
Beta Was this translation helpful? Give feedback.
Or, subclass
DefaultRouter
and customise theAPIRootView
to explicitly add the auth view, similar to how we manually define that in the tutorial.