Differences between RESTful API and urlpatterns router in Django - django

Am new to web developpement and wondering the differences between:
Django Restful API
and
standard Django URL routers
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^$', views.index, name='Index'),
url(r'^getvalue/$', views.get_points, name='Get Points'),
url(r'^putvalue/$', views.put_points, name='Put Points'),
]
What are the benefits of setting Django restful API when interacting with Javascript components since both are JSON sending URL ?

Before understanding this you have know that,
REST API concept.
HTTP verbs(REQUEST METHOD)
REST API
REST API is nothing but very special. Just remember one thing this is a concept where we can use proper use of HTTP VERBS. Like, GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS etc....
HTTP VERBS
I already told you the names of HTTP VERBS. Think what we do normally?? Basically I do, I use POST for updating db row, I use POST for DELETE a row. But in REST API concept we can't do like this kinds of nasty things. When we are going to delete something we need to use DELETE
Links
You may read this, https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

It was quite some time since the question had been asked, but I thought I'd provide an answer as it bothered me as well.
The main reason why you want to use Django's routers is convenience: once you declared them once, you don't have to go through the same cumbersomeness as persistent declaration of urls in urls_patterns.
However, the url_patterns can still be used if for any reason you want to have a specific hard-coded url.

Related

How to exclude drf router path from drf_spectacular SERVERS setting

I would like to verify some best practices when using drf_spectacular with a django project.
I have an API in a django project where all the endpoints use the api/ prefix, i.e. in urls.py, I'm using.
path('api/', include('myapp_api.urls')),
The myapp_api project'ss urls.py uses the default router, so something like
router = routers.DefaultRouter()
router.register(r'assets', views.AssetsViewSet)
By default, this means that the swagger docs will present all the endpoints as something like...
/api/assets/{}/
instead of
/assets{}/
At this point, the swagger UI's test calls will work just fine because it will correctly call https://example.com/api/assets{}/. It just looks a bit messy in SwaggerUI so it's not ideal. I'm getting the impression that the /api is superfluous and should be essentially "handled" by the "servers" value.
A problem arises when I set the OpenAPI server object, which for drf_spectacular is the SERVERS setting, e.g.
SPECTACULAR_SETTINGS = {
'SERVERS': [{'url': 'https://example.com/api'}],
}
This will result in failures with SwaggerUI test calls because they try to sent requests to the following (not the /api/api)
https://example.com/api/api/assets/{}/
A secondary problem is that the /api/ prefix will still appear on all the SwaggerUI listed endpoints.
I seem to have two options:
I could go to my urls.py and use path('', include('myapp_api.urls')) instead of path('api/', include('myapp_api.urls')) but that's not desirable because the prefixing seems to make sense in this context.
I could use a drf_spectacular preprocessing hook and tweak all the endpoints it's generating.
e.g.
SPECTACULAR_SETTINGS = {
'SERVERS': [{'url': 'https://example.com/api'}],
'PREPROCESSING_HOOKS': ['my_preprocessing_hooks.strip_the_api_prefix']
}
def strip_the_api_prefix(endpoints, **kwargs):
for i in range(0, len(endpoints)):
temp = list(endpoints[i])
if temp[0].startswith('/api'):
temp[0] = temp[0][4:]
endpoints[i] = tuple(temp)
return endpoints
My question is, this all seems like a brittle hack and I'm wondering if I'm missing something. I would like to know if I'm following best practices etc.
I'm pretty sure I need to be setting at least one OpenAPI server value, so today it's https://example.com/api which could always be the 'bleeding edge', but later there could be a https://api.example.com/v1 if there's multiple major versions in the future that I want to maintain. My understanding is that the OpenAPI server value is important for client applications so that there's less need to guess the non-path part of the URL when interacting with an API.
I'm presuming that it's best practices to not have prefixes for all the endpoints that are documented in the SwaggerUI, but it seems that drf_spectacular is pulling them automatically from the urls.py files, which is why the /api keeps getting added.
So in short, is there a better way to handle this situation that using a preprocessing hook?
I've not used drf-spectacular before but I had a quick look at the docs and may be able to give you some ideas.
For your DRF urls - path('api/', include('myapp_api.urls')),. For different API version you would have different url files, even different views files. For example, non versioned files being your latest "bleeding edge":
myapp_api/urls/urls.py # latest and greatest
myapp_api/urls/v1_urls.py # older supported urls
myapp_api/views/views.py # latest and greatest
myapp_api/urls/v1_views.py # older supported urls
Then you would import the different versions in your main url file:
path('api/', include('myapp_api.urls.urls')),
path('api/v1/', include('myapp_api.urls.v1_urls')),
For your drf-spectacular, You may not need to implement SERVERS but if you do, it may be possible to do the below. I haven't used this package before though. Here's the documentation I've looked at for this settings.
SPECTACULAR_SETTINGS = {
'SERVERS': [
{'url': 'https://example.com/api'},
{'url': 'https://example.com/api/v1'},
],
}
Having the URLs and views setup with versioning forces versioning as the default. Although when you create a new "bleeding edge" API, it will involve renaming files and imports. If you don't want this, an alternative setup could be:
You keep track of the API by versioning them in the file names, where v1 is the oldest, and you increment for each iteration. So if you had v1, v2 and v3, v3 would be the latest. This would be urls/v1_urls.py, urls/v2_urls.py and so on. You would then only update the main urls file to point to the latest version. For example:
# Older supported APIs
path('api/v1/', include('myapp_api.urls.v1_urls')),
path('api/v2/', include('myapp_api.urls.v2_urls')),
# Latest API
path('api/', include('myapp_api.urls.v3_urls')),
If v4 became the latest, you would move v4 into the latest and move v3 into the oldest and change the URL to be api/v3/.
This combined with versioned named files would make it easier to delete retired versions and also add new version without having to rename imports etc.

Issue using i18n_patterns with API

I have an app which includes a website and an API. I have translated the website to a second language. The main language is currently English and the second one Farsi. I have used i18n_patterns in urlpatterns to redirect between the two languages.
The issue is when using the API, it seems like Django redirects my request and since the data is sent using POST, it drops all data and gives me an empty result using GET. I tried taking the API url out of the urlpattern and then appending it which solves this issue, but that doesn't work since I need to have Unicode support for Farsi and since there is no i18n_patterns support this way, I get an unintelligible response.
This is the Urlpattern:
urlpatterns = i18n_patterns(
path('admin/', admin.site.urls),
path('mainapp/', include('mainapp.urls')),
path('', include('mainapp.urls')),
prefix_default_language=True
) + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns.append(path('api/', include('mainapp.api.urls')))
Is there any way I can solve this issue with the least possible change to the app? Any help, suggestion, or guidance would be very much appreciated.
EDIT: Thank you. I changed the code to reproduce the issue. I call http://localhost:8000/api/scan/. If I add the path('api/', include('mainapp.api.urls')) to the original urlpattern with i18n_patterns, and send the request to API, the request.POST value is <QueryDict: {}> while otherwise it should send a key:value to the backend. I looked around and it seems when you add i18n_patterns to urlpatterns, a redirect happens and since HTTP doesn't allow POST data redirects, I get an empty response.
The goal of i18n_patterns is to translate the url itself, so it's a bit weird that you'd want to that for an API. The idea is that users who look at the address bar in their browser read the urls and it might help to translate them so it's easier for them to understand where they are on your site. So for example if you have a url "/shop/product/" in English and you want it to be "/magazin/produit/" in French.
Since an API doesn't display URLs to end-users, it just doesn't make sense to have the API urls fall under i18n_patterns. That will solve the redirect issue.
Your original problem is that your view returns an "unintelligible response" when you call it using an URL which doesn't specify the language. That's probably because the view is detecting the wrong language. This is how Django detects the language preference. So if the url doesn't have the information, it will:
Look for the language cookie.
Look at the Accept-Language HTTP header, in this order.
So you can either ensure your API requests set the correct header (and don't send the language cookie), or you can just set the active language in each view called by the API.
Not sure I understand your problem fully, but you can prevent the redirects by setting redirect_on_fallback to False in your CMS_LANGUAGES settings.

Django API with different levels

Currently, the project I'm working on has an API like the following:
api/
visitors/ # Endpoint returning list of visitors
visitor-rates/ # Endpoint returning visitors per time
gift-shop-orders/ # Endpoint returning purchases at the gift shop
order-rates/ # Endpoint returning purchases per time
What I'd like to do is group the visitor and gift shop endpoints under their own sub-heading. For example:
api/
visits/
visitors/
rates/
gift-shop/
orders/
rates/
Note that visits and gift-shop only have the job of listing out the URLs that are available under the respective subheading. So, a GET request to /api/visits should return:
{
"visitors": "/api/visits/visitors",
"rates": "/api/visits/rates"
}
In other words, I'd like /api/visits/ and /api/gift-shop/ to have the same behavior as the default router view used by Django for displaying the endpoints available in the root of the API (/api/).
I've tried simply nesting just the URLs together. So, suppose I've defined routers for the visitor endpoints and shop endpoints. I've tried:
api_patterns = [
url(r'^visits/', include(visitor_router.urls)),
url(r'^gift-shop/', include(shop_router.urls)),
]
urlpatterns = [
url(r'^api/', include(api_patterns)),
]
This makes it so that requests to /api/visits/ and /api/gift-shop/ respond correctly. However, if I go to /api/, no links to /api/visits or /api/gift-shop are given.
This leads me to believe that I need to nest the routers themselves, not just the URLs. I haven't seen any documentation on doing this, though, and my attempts at coming up with a custom solution have only led to other issues. Does anyone know if there is a simple, standard way to do this that I am missing?
tl;dr: How do I nest Django routers?
The short answer is you don't. If you're using ViewSets as a way to simplify your views, you need to stick to the rules of ViewSets, and if you want to have nested routes, you can do it with the #detail_route and #list_route decorators, but they don't express your resources as the rest of the framework does, and it's a manual labor.
The alternative, is using a third-party package called drf-nested-routers, which is really well done and can definitely do what you're expecting.

Moving the API Root of Default Router in Django

I'm using Django 1.8 and Django REST Framework. I want the API Root functionality of using a Default Router, but I want to move it to another url, like /apiroot/ or something like that.
I found this post, but it doesn't really answer my question. I don't want to rewrite the API, I basically just want the functionality of the ^$ route to be a standard template page (home page) for the site, and have site.com/apiroot to be the new API root url.
In the process of writing up this question, I looked at the DRF source code, and found my answer. Instead of deleting the question, I figured I'd go ahead and post it, since someone else may be wondering the same thing, and a search hasn't turned up any answers to this already.
The solution to this was to add the following lines to my urls.py:
url(r'^$', media_views.index, name='index'),
url(r'^apiroot/', router.get_api_root_view()),
The first line will replace the ^$ url entry with a pointer to my index method in media_views. The second will mimic the DefaultRouter functionality from / to /apiroot/ and show the API root page there instead.

What is the best practice method of using Angular JS routing with a Django backend?

I have created a REST API in Django to pull data from my database. I have a front end application built with Angular that makes calls to that same API. The API has a few URL's, and I have one other URL to serve up index.html and handle the routing. Angular injects the "#/" into the URL. Ideally I would not have that, but when I use the HTML5 mode and location provider, Django picks up the URL and does not see the specified URL in its list and therefore throws an error.
I have seen some resources online, but they are not very clear to me.
Basically, what are accepted best practices with regard to creating angular applications with a Django backend.
I appreciate your help! Thank you in advance.
If you want to serve the index.html for every url and then do the routing in angular you can do somethings like this in your <project_folder>.urls.py
from <your_app> import views
urlpatterns = patterns('',
url(r'^.*$', views.index),
)