Using Browser Router in react-router-dom with Django backend - django

I'm building an application that uses React as front-end and Django Rest as back-end.
From front-end, I'm using react-router-dom for declarative routing and Django only serves API
So when user enters an URL like this https://myapp.com/something/something-else, it's actually handle by Django and it will return an error since Django doesn't know which page to lead to with this URL. It should be handled by React instead.
One of the workaround I have is to use a Hash Router, so the URL will look like this (with the hash symbol): https://myapp.com/#/something/something-else
But there have been cases where user will just key the URL without the hash sign (as they didn't know).
Is there away to handle this without using Hash Router?

I can't say exactly because you haven't provided code, or an explanation of your project structure.
How are you doing a hash-router? If you have Django serving a single HTML file, then you should be able to edit your urls.py to pass any URLs that don't match the api to the same page.
As an example, I have a Django website with a Preact frontend, and I have Preact files that are built into static .js files, which are then served by Apache. Django does all URL routing and serves HTML files, which then request the Preact JS files from Apache.

Related

Communication between Django and React

I'm trying to setup a project using Django for backend and React for frontend. The project has several screens, a lot of information in DB and images generated by the backend, and will include some authentication and user permissions for different screens.
According to what I found - the best way to do it is having Django render an html file:
def index(request):
return render(request, 'frontend/index.html')
which references a .js file:
<script src="{% static "frontend/main.js" %}"></script>
Which is created using Webpack.
This main.js retrieves the data it needs from Django using a REST api:
fetch("...some Django endpoint..").then(response => ... this.setState(...retrieved data...))
Unlike when just using Django for backend + Django templates for frontend where the backend can just send the context directly to the template:
def index(request):
context = {'information': .... retrieve info from DB}
return HttpResponse(loader.get_template('bla/index.html').render(context, request))
and the template can use this info directly, without referencing the backend again:
{% for bla in information %}
I'm wondering if it is a reasonable setup?
It seems excessive to have the frontend use REST for retrieving each piece of information it needs and the backend exposing another REST api for each part of data it needs to supply (Instead of just pushing all of the information to a single dict and sending it over along with the template),
Also, it requires at least 2 RTTs to render the full page (which I guess usually is okay)
According to what I found - the best way to do it is having Django render an html file:
I disagree with this line. I would say it would be best to keep the react app and Django app totally separate.
I believe, the Django application should solely provide APIs and adminsite(maybe, depending on your needs).
And the frontend should be a standalone app which can be served through NGINX/ExpressJs/Apache etc.
There are several advantages of this setup.
From Django application's perspective, the advantages are:
Django will not be burdened to serve the Frontend. Use gunicorn or uwsgi to serve the Django APIs.
As Django will provide data through API only, it will provide clarity on how the frontend application will communicate with the backend. I know that you can send data using context when Django serves the react app, but this might cause confusion because of API and context's co-existence.
You can use Token based authentication, JWT etc instead of Django's own session based authentication, which have a lot of other advantages.
Freeing your frontend application from backend is the best thing can happen for the frontend. Like for example:
if you had Django to serve the frontend, you were almost forced to use session based auth(its not like you can't use other auths, but whats the point of having multiple auth systems)
You couldn't have used server side rendering with Django rendering the frontend.
Lets say, you are have no idea about how Django works, but you will be forced to setup a Django application in your local machine, because it serves the frontend.
You couldn't have used ExpressJs to serve the frontend, or use the advantages of using NGINX to serve those contents.
Deployment would be complicated if you have docker setup. In this case, you would have had to use one Docker Container to serve everything, else you could have used multiple docker containers to serve backend/frontend.
Lets say, you want to serve the Django application in one server, frontend from other server, but with Django tightly coupled with Frontend, you can't do this setup.
You can easily connect external RESTful API services without bothering about Django. Even you can use any other frameworks like Tornado, Flask etc(but DRF+Django ORM is awesome) to develop APIs.
There are some more generic advantages of having backend and frontend separated.
There is a fantastic tutorial which you can read on medium about setting up separate Django + ReactJs app.
You can use GraphQL, it has several advantages over REST, f.e.:
only one endpoint for entire app;
ability to fetch data with relations between them;
easy data structure modifications on both sides;
advanced client with cache/normalization/subscriptions/optimistic updates (I prefer apollo to relay);
can be used as datasource for static site generation (SEO);
you can stich other services/APIs;
... many more.
Using react Server Side Rendering you can get pages without additional requests - 'prefilled'/rehydrated, ready for interactions - better Time to Interactive.
Tutorial/demo: django-graphql-apollo-react-demo

how to make Django play well with Angular URL

In Angular 2 app that's served by Django, how to make the Angular 2 URL work with Django when reloading the page?
Specifically, say a page served by Django has the url http://localhost:8000/home. The page contains 3 tabs and under one of which is an Angular app. Clicking on that tab shows the Angular app, and the url changes to http://localhost:8000/home/ng_app. Now if the browser is refreshed with this url, Django will show a 404, which makes sense because http://localhost:8000/home/ng_app is not present in the Django app.
So how to make the Angular URL work? (entering http://localhost:8000/home/ng_app leads to the Angular app instead of 404). Thanks!
Assuming that your entire app is served from /home, you can just use the following url pattern:
urlpattern('/home/.*$', views.my_angular_homepage)
If you want to also redirect to the route of everything after you can use any of the standard tricks to capture the trailing part of the URL, and then do a $location.path in you angularJS app.
You should consider using hash bangs in your angular app as URLs. Checkout this tutorial I found by googling which also uses has bangs for URLs.
In this way the angular app URLs wont conflict with your Django app and Angular would pick it up for routing purposes
Use url(r'^/home/(?!ng_root/).*$' where ng_root is the root url of the angular static files.
Checkout my blog post for more details https://4sw.in/blog/2016/django-angular2-tutorial/ .

Using Django Rest Framework for only certain apps inside Django Application

I'm not sure if this is possible since an extensive search gave me nothing. Or I might be searching the wrong terms.
Now I have a few apps inside my Django application as follows:
--AppOne
--AppTwo
--ExtendedAdmin
manage.py
Now, AppOne and AppTwo use Django Rest Framework and its related Token Auth Mechanism. For the extendedAdmin, I'd like to use native Django Auth or even SessionAuth of DRF since I'm trying to add quite an extensive admin panel to the application. I've not been able to find a satisfactory way of customizing Django Rest Framework to work it's auth mechanisms only for certain applications. Is there a way to do this? If not, what should I be doing different?
So far, I know you cannot. Because django rest framework intercepts the url and then performs its own logic of token validation. But there are solutions that you can use to keep both Session and Token Authentication.
I am using this for all my projects -
I keep Session Authentication for all urls that will be accessed for normal browsing
and I use api urls with django rest framework to be prefixed with /api for my api urls
For example -
The Session Based login is at http://<host>/account/login
and TokenBased login is at http://<host>/api/account/login
The easiest way to make prefixed url for django rest framework is by using Routers - http://www.django-rest-framework.org/api-guide/routers/#defaultrouter
Example -
class UserViewSet(ModelViewSet): # just a sample view set
...
router = routers.SimpleRouter()
router.register(r'api/users', UserViewSet)

Django URL conf and Backbone.js Router

I have a backbone.js single-page app that is all set up with the router (well, actually a Backbone.Marionette app with a Backbone.Marionette AppRouter, but nevertheless). However, the backend is based in Django, where I do not have the URL conf directing to views for all URLs that are already in the backbone.js routes.
Based on the existing URLs in the Django URL conf, Backbone.js will serve the backbone routes regardless of what is listed in the Django conf - it seems something, anything just needs to be there.
Do I need to have proper Django views in order to offer a fallback for older browsers/SEO?
What are the best practices to coordinate the Django URL conf and the Backbone.js Router?
I've found a post that addresses this issue quite well:
http://duganchen.ca/single-page-web-app-architecture-done-right/
Briefly, my reasoning for including a fallback is for non-javascript browsers and SEO reasons. At the time of this post, non-javascript browsers account for ~1.4% (less than 2% from everything I've read) of users, making SEO The major consideration. Again, SEO may not be relevant for everyone reading this post, in which case, this can be skipped.
I found Thomas Davis' tutorial using phantom.js quite helpful. http://backbonetutorials.com/seo-for-single-page-apps/
However, another issue that I needed to account for was the history API, which has been neglected by all but the latest IE browsers. Given my client's users, about 15% of which are using IE <= 9, this was also a problem.
In the end, I also needed to use history.js. All in all, this was a lot of work to update an otherwise very simple website. However, I learned a lot from this ordeal.
In my opinion if your backbone app is truly a single page then you don't need any django views whatsoever. You can serve your index.html as a static file (in production, not even by django) and then let backbone's router take care of your url configuration, as you're doing already. You can use backbone's history and navigate to fake urls, add urls parameters etc, for resources in your app.

Protecting static files from non logged in users in Django

I have an existing site with a number of documents being served staticly. Client wants to add login protection to the site - not a problem using django_auth. However, the files being served from apache are still downloadable?
Is there a way to restrict access?
Ideally, this would require the path to these docs not changing on the site.
Was thinking of removing the alias from the apache config and having that route to a view that has the #login_required decorator on it, and then forwarding on.
See Having Django serve downloadable files on how to set up Django to work with Apache X-Sendfile. You can wrap the X-Sendfile header sending with some authentication checks and you should be good to go.