Including only selected urls in django - django

I have a third party app with the following url configuration:
urlpatterns = [
url(r'^webhook/(?P<token>[-_:a-zA-Z0-9]+)', TelegramView.as_view(), name='webhook'),
url(r'^bots', BotViewSet, name='api'),
]
Now I want to include only the second url (with namespace 'api') because I have overridden the first one in my own app (It will be handled by my own views). How do I use include so that it only includes the second url?

Related

Set up DRF Browsable API Root with all api urls

In my urls.py I have many rest framework urls:
path(
"api/",
include([
path("users/", api_views.users, name="users"),
path("proposals/", api_views.Proposals.as_view(), name="proposals"),
path("requests/", api_views.Requests.as_view(), name="requests"),
#...
])
)
I can visit the individual endpoints in the browser and access the browsable API, but I want to set up a browsable API Root where I can see all the available endpoints. The DRF tutorial has an example in which they list the urls they want in the root, but I have a lot of urls and I want all of them to show up. Is there a way to include all the urls in my "api/" path?
You should try to use drf_yasg package. Here's a documentation for that.

Custom Grouping on OpenAPI endpoints with Django Rest Framework

I have a Django project and I am using Django REST framework. I am using drf-spectacular
for OpenAPI representation, but I think my problem is not tied to this package, it's seems a more generic OpenAPI thing to me (but not 100% sure if I am right to this).
Assume that I have a URL structure like this:
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include([
path('v1/', include([
path('auth/', include('rest_framework.urls', namespace='rest_framework')),
path('jwt-auth/token/obtain', CustomTokenObtainPairView.as_view(), name='token_obtain_pair'),
path('jwt-auth/token/refresh', CustomTokenRefreshView.as_view(), name='token_refresh'),
path('home/', include("home.urls"))
]))
])),
# OpenAPI endpoints
path('swagger/', SpectacularSwaggerView.as_view(url_name='schema-swagger-json'), name='schema-swagger-ui'),
path('swagger.yaml/', SpectacularAPIView.as_view(), name='schema-swagger-yaml'),
path('swagger.json/', SpectacularJSONAPIView.as_view(), name='schema-swagger-json'),
path('redoc/', SpectacularRedocView.as_view(url_name='schema-swagger-yaml'), name='schema-redoc'),
]
In the corresponding swagger UI view, I get all endpoints grouped under api endpoint, e.g.:
If add more endpoints under v1, all go under the api endpoint.
What I want to achieve is, to have the endpoints in Swagger grouped differently, e.g. by app. So I'd have home, jwt, another_endpoint, instead of just api, so it will be easier to navigate in Swagger (when I add more endpoints, with the current method it's just showing a massive list of URLs, not very user friendly).
I've read that those groups are being extracted from the first path of a URL, in my case this is api, so if I change the URL structure, I could achieve what I need.
But isn't there another way of doing this? I want to keep my URL structure, and customize how I display this with OpenAPI, so in the end I have a swagger that groups the endpoints by app, so it's easier to navigate and find what you are looking for.
you are making it harder than it needs to be. In the global settings you can specify a common prefix regex that strips the unwanted parts. that would clean up both operation_id and tags for you. In your case that would probably be:
SPECTACULAR_SETTINGS = {
'SCHEMA_PATH_PREFIX': r'/api/v[0-9]',
}
that should result in tags: home, jwt-auth, swagger.json, swagger.yaml
the tags on #extend_schema is merely a convenience to deviate from the default where needed. it would be cumbersome to do this for every operation. see the settings for more details:
https://drf-spectacular.readthedocs.io/en/latest/settings.html
for even more elaborate tagging you can always subclass AutoSchema and override get_tags(self) to your liking. cheers!
Turns out that you can control this by changing the tags in a view, as per OpenAPI specification: https://swagger.io/docs/specification/grouping-operations-with-tags/
So, with drf-spectacular, you can use the extend_schema decorator to achieve this, e.g.:
from drf_spectacular.utils import extend_schema
class CustomTokenObtainPairView(TokenObtainPairView):
"""
Takes a set of user credentials and returns an access and refresh JSON web
token pair to prove the authentication of those credentials.
"""
#extend_schema(
operation_id="jwt_obtain",
....
tags=["aTestTag"]
)
def post(self, request, *args, **kwargs):
# whatever
So you have to use this decorator to extend the schema in each view that you want to put into a custom group.

RESTful API: is it good to have foreign key which sends to different version of the API?

We are developing a RESTful API using Django REST framework, and we decided to handle foreign keys showing a URL to the relation's resource, for example:
GET https://url_to_api/api/v2/foo/1:
{
"id": 1,
"bar": "https://url_to_api/api/v2/bar/6/",
"baz": "https://url_to_api/api/v3/baz/4/"
}
This is a GET request for the foo's ID 1 on the version 2 of the API.
Django REST framework returns the URL to the v2 for the relation with the bar entity because /api/v2/bar/6/ sends to a view which is only used by this version, but it returns an URL to v3 (https://url_to_api/api/v3/baz/4/) for the relation with the baz entity, because its view is the same as the v2's one, and Django REST framework reverse engine returns the first result for the URL which sends to the same view.
I have a couple of questions about this behaviour:
is it a normal behaviour or we are missing or doing something wrong?
is it good to have URLs which sends to a different version of the API?
Any other ideas on how to manage this?
This is a normal behaviour in DRF. So either you can specify like this or else you specify custom for everything.
# bookings/urls.py
urlpatterns = [
url(r'^$', bookings_list, name='bookings-list'),
url(r'^(?P<pk>[0-9]+)/$', bookings_detail, name='bookings-detail')
]
# urls.py
urlpatterns = [
url(r'^v1/bookings/', include('bookings.urls', namespace='v1')),
url(r'^v2/bookings/', include('bookings.urls', namespace='v2'))
]
For further reference, refer this

URL conflict in django1.8 and DRF 2.4.8

Project level urls.py
urlpatterns += [
url(r'^machines/api/', include('core.urls')),
url(r'', include('apps.api.urls')),
url(r'^machines', include('apps.machines.urls')),]
App level urls.py
urlpatterns = [
url(r'^user/edit/(?P<pk>[0-9]+)/$', core_view.ProfileEdit.as_view()),
url(r'^group/', core_view.GroupList.as_view()),
url(r'^groups/add/', core_view.GroupCreate.as_view()),]
when i hit
http://localhost:8000/machines/api/groups/add
it is calling GroupList view instead of GroupCreate.
I am not getting any reason why this is happening?
Current url list triggered GroupList on each url started with group/. You should add $ at the end of GroupList pattern to limit url triggered by GroupList only with group:
url(r'^group/$', core_view.GroupList.as_view()),
url(r'^groups/add/', core_view.GroupCreate.as_view()),]

Django and service workers - serve "sw.js" at application's root url

So I'm building a Django progressive web app with offline support using service workers.
According to google's documentation, the sw.js file should be at the root of the app's url:
You need to do this because the scope of a service worker (the set of
urls that the ServiceWorker will load for) is defined by the directory
where it resides.
At the moment, I'm serving all static assets from http://example.com/static/ folder. But I need to serve this specific file at a url like: http://example.com/sw.js.
Any idea how I can achieve this? I could make a specific nginx rule to do this redirection, but I don't know if it's the cleanest way of doing this. Maybe this setting should reside in urls.py?
Note: I've seen this question which suggests using the static() method from django.conf.urls.static. But django's docs say that the static method is only for development use so not good for me.
Note (2): I guess I could change the STATIC_URL setting, but I'm happy with my files being served from /static directory. I only want this one file to be at url's root.
You can serve javascript as a view, not just html. Put this in your projects urls.py
url(r'^service-worker.js', cache_control(max_age=2592000)(TemplateView.as_view(
template_name="service-worker.js",
content_type='application/javascript',
)), name='service-worker.js'),
Then put your service-worker.js in your templates directory.
Bonus is that you can now also use template tags like static in your javascript file.
Django 2.2
project structure
myproj/
|-app/
| |-templates/
| |-app/
| -sw.js
|-myproj/
-urls.py
urls.py (project)
from django.views.generic import TemplateView
urlpatterns = [
...
path('sw.js', (TemplateView.as_view(template_name="app/sw.js",
content_type='application/javascript', )), name='sw.js'),
]
In Django 1.11 urls.py should look:
from django.views.generic import TemplateView
urlpatterns = [
url(r'^sw.js', (TemplateView.as_view(template_name="sw.js", content_type='application/javascript', )), name='sw.js'),
]
I was getting all the time the error DOMException: The script resource is behind a redirect, which is disallowed.
I spent hours trying to figure out the solution.
Apart from adding at urls.py:
from django.views.generic import TemplateView
urlpatterns = [
...,
url(r'^service-worker.js', (TemplateView.as_view(template_name="service-worker.js", content_type='application/javascript', )), name='service-worker.js'),
]
There was also another step needed.
Instead of
<script>
if ('serviceWorker' in navigator) {
console.log("Will the service worker register?");
navigator.serviceWorker.register('service-worker.js')
.then(function(reg){
console.log("Yes, it did.");
}).catch(function(err) {
console.log("No it didn't. This happened:", err)
console.log("err.message:", err.message)
});
}
</script>
I used:
<script>
if ('serviceWorker' in navigator) {
console.log("Will the service worker register?");
navigator.serviceWorker.register("{% url 'service-worker.js' %}") //note that I am using the url template here
.then(function(reg){
console.log("Yes, it did.");
}).catch(function(err) {
console.log("No it didn't. This happened:", err)
console.log("err.message:", err.message)
});
}
</script>