Moving the API Root of Default Router in Django - 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.

Related

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.

How to limit catch all urls in django

I have a django project with specific urls, setup by a 'catchall' URL.
This is so I can go to mysite/living, and have it pass living as a parameter and pull up the appropriate details from my db.
My urls.py:
url(r'^$', views.index, name='index'),
url('about/', views.about_view, name='about_view'),
url('contact/', views.contact_view, name='contact_view'),
url('(?P<colcat>[\w\-]+)/collection/(?P<name>[\w\-]+)$', views.collection_detail, name='collection_detail'),
url('(?P<colcat>[\w\-]+)/$', views.collection_view, name='collection_view'),
I am running into the problem where, anything can be passed as a parameter. This is particularly notable with search engines, where mysite/index.html/index.html returns a valid page.
Is there a way to limit the urls that are 'allowed' to be matched?
It is very unlikely for a user to enter/modify URLs manually while browsing. Everyone just googles and clicks whatever link is shown by the search engine. So, You just need to restrict what the search engine indexes.
This can be done by adding a sitemap.xml file to the root of your website.
sitemap.xml specifies all the urls of your website along with some additional information inorder to make it easier for search engines to crawl. If you don't add a sitemap.xml, search engines try to crawl through every possible url. If added they wont.
There is already a sitemap generating framework provided by django: https://docs.djangoproject.com/en/2.1/ref/contrib/sitemaps/

Django - Request resolution

I would like to know how Django is resolving request urls urlpatterns in general.
My theory:
Django at some point turns all its urlpatterns into list of regexes, and then tries to match them against incoming Request's url.
Question:
Am I correct? If yes, can somebody point me out where in source code is this happening?
Looks like there is nothing about this mentioned in django docs, and I feel like I am doing a blind search in source code. Any insights appreciated.
The process is described here. In short:
Django determines the root URLconf module to use....
Django loads that Python module and looks for the variable urlpatterns. This should be a Python list of django.urls.path() and/or django.urls.re_path() instances.
Django runs through each URL pattern, in order, and stops at the first one that matches the requested URL.
Once one of the URL patterns matches, Django imports and calls the given view, which is a simple Python function (or a class-based view)....
If no URL pattern matches, or if an exception is raised during any point in this process, Django invokes an appropriate error-handling view....

how to prevent access to admin urls in Django?

Django gives admin url automatically, such as www.example.com/admin.
I do not want any outside visitors to access this url. This should be accessed only with in the host and allowed IP address.
If I try to access to https://instagram.com/admin/ (which is built using Django),it gives 404 page not Found error
How can I achieve the same behavior?
what is the preferred and right way to do it?
I host my webservice inwebfaction and allowing IP address of host means other webfaction account-holders might be able to access the admin URL which I dont want to. Looking for a neat and simple way
Thanks:
PS: I see a similar question posted here but that is with respect to PHP. I am wondering how can I acheive the same using Django?
One common method, which is advocated by Two Scoops of Django, is to change your admin url. Thus, rather than logging into your admin at www.example.com/admin/, you would log in at www.example.com/supers3cret4dm1n/ or something that you've set. This is likely what Instagram has done in your example.
Example code:
urlpatterns = patterns(''
...
url(r'^supers3cret4dm1n/', include(admin.site.urls)), # Change the pattern to whatever you want here
...
)
Note that this doesn't make it accessible from only one IP address, but it does effectively 'hide' your admin login page.
Another tip is to use the django-admin-honeypot package. This sets up a fake admin page at www.example.com/admin while having your real admin page at another site that you've set. Then, django-admin-honeypot will alert you if anyone tries to hack your admin at the fake admin site.
EDIT:
If you're dead-set on restricting by IP address, here's a SO question and answer showing how to do it with nginx. I imagine it'd be similar with others.
simply you can treat the admin path as a secret, so set it as an environment variable in your system and then retrieve it (good approach if your source code is public).
ADMIN_URL_PATH = os.getenv('DJANGO_ADMIN_PATH')
urlpatterns = [
...
path(ADMIN_URL_PATH, admin.site.urls)
...
]

How are Django page templates assigned to each page?

I couldn't find this info in the Django docs, but I'm sure it is there, I'm just very new and don't know what terms/etc to search on.
How are Django page templates assigned to each page?
I have a login to a Django site, and also SFTP access to the site. I don't think my Django login is a superuser/full-admin though because the interface seems pretty limited compared to other CMS systems. I can edit pages, posts and the media library, but I don't see anything that says how each page is assigned a template.
For example, I have this file /mysite/templates/pages/index.html
I know that template is being used for the home page because it has all of the content that is specific to the home page on it, and changes I make show up on the home page.
I tried copying that file to test.html, but when I browse to test.html in my browser, I get a 404 error (I also get that error if I go to index.html). So there must be something else that maps a template to a page, but I'll be dambed if I can find it. Will I need more access to the admin area, or can I do something with SFTP? I also have SSH access but wasn't able to follow any of the steps online to create a new superuser account for me, for Django.
Edit: Thanks for both answers, after I work through this I'll accept whichever helped the most. I do not have a views.py file, but I think it might be using an extra module for this routing, I have this in my urls.py file:
urlpatterns = patterns("",
("^admin/", include(admin.site.urls)),
url("^$", "mezzanine.pages.views.page", {"slug": "/"}, name="home"),
("^", include("mezzanine.urls")),
)
Is this "mezzanine" something different which changes the answer (location of views.py or list of views)?
url.py is the file that maps the urls to methods that return rendered templates. In essence you define the url and a method and when someone goes to that url, that method gets called which returns a HTTP response with the rendered template. This map is called urlpatterns. In the following example when someone goes to yourwebsite/blog then in the blog apps, view.py, page method is called, which will use a template and render that with specific information.
urlpatterns = patterns('',
url(r'^blog/$', 'blog.views.page'),
url(r'^blog/page(?P<num>\d+)/$', 'blog.views.page'),
)
Have a look at this link.
https://docs.djangoproject.com/en/dev/topics/http/urls/
Django uses urls.py files to map paths to views. This match is resolved using regular expressions. When a match is found, Django executes the associated view (usually inside views.py). The view is in charge to render the template required for the path (by finding it on the server's hard disk and loading it).
All aforementioned means that there's no direct association between a url path (i.e www.example.com/path/to/page) and a file on the server's hard disk (i.e /server/path/to/page). It's all performed dynamically by Django's engine when a request comes in.
If you want to know which view is gonna be generated for a specific path, follow the regexs at urls.py until you find the path you're looking for. Then open the view for that url and see inside which template it is rendering.
Reading doc's URL Dispatcher is a good point to start learning about this.
Hope this helps!