[Django]-Handling single page application url and django url

28👍

I have a similar problem.

How can I use vue-router and django rest
framework the same time?

This is my solution to this problem. Hope it helps you.

Expected results:

http://127.0.0.1:8000/       <-- TeamplateView index.html using vue
http://127.0.0.1:8000/course <-- vue-router
http://127.0.0.1:8000/api    <-- rest framework
http://127.0.0.1:8000/admin  <-- django admin

and I try this and it works!

urls.py

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
    url(r'^api/', include(router.urls)),
    url(r'^.*$', TemplateView.as_view(template_name="index.html")),
]

The order is important,
url(r'^.*$', TemplateView.as_view(template_name="index.html")),
is the last one

and this is my vue router

const router = new VueRouter({
  mode: 'history',
  base: __dirname,
  routes: [{
      path: '/courses',
      component: CourseSet
    }, {
      path: '/',
      component: hello
    }]
})

My project on GitHub

10👍

Since you have mentioned “single page”:

  1. The server is supposed to serve just one page the index.html (or whatever else you would like to call it).

  2. The server and the web application (front-end) code would communicate via api calls, such that the server provides resources and the web-app takes care of using that resource.

  3. In case of a missing resource, the server still must not respond with a separate page, it should still respond with a message that the web-app can use.

I have a single page application created in Vue.js that utilizes the HTML5 History Mode for routing

I believe you are using vue-router, which simulates the single-page app to be a full-featured, multi-page application.


You may want to take a look at this and this, but the above holds true for a single page application.


You shared your urlpatterns:

urlpatterns = [
  url(r'^admin/', admin.site.urls),
  url(r'^api-token-auth/', obtain_jwt_token),
  url(r'^.*$', views.home),
]

But other urls like the media or static urls will also point to the same catch all pattern regex. How can I solve this problem?

  1. A way you can manage that would be either by, serving on a route other than '/' like mentioned above for /app.

    urlpatterns = [
      url(r'^admin/', admin.site.urls),
      url(r'^api-token-auth/', obtain_jwt_token),
      url(r'^.*$/app', views.home),
    ]
    

    and in your router.js file:

    new Router({
      mode: 'History',
      base: '/app'
      routes: [
        {
          path: '/',
          name: 'name',
          component: ComponentName
        }
      ]
    })
    
  2. Or prefixing the purpose served by the urls like

    urlpatterns = [
      url(r'^api/admin/', admin.site.urls),
      url(r'^api/api-token-auth/', obtain_jwt_token),
      url(r'^.*$', views.home),
      url(r'^.*$/assets', your-static-assets) 
    ]
    

7👍

I’m using VueJS router enabled history mode with Django 2.x by this way:

From app urls.py you need to repath your frontend url like this:

# urls.py

from django.urls import path, include
from django.urls import re_path

urlpatterns = [
    path('', TemplateView.as_view(template_name="application.html"), name="app", ),
    # ... rest of your urls
]

urlpatterns += [
    re_path('^.*$', TemplateView.as_view(template_name="application.html")),
]

Now history mode working smoothly!

http://sample.com/#/users/login/

Become:

http://sample.com/users/login/

3👍

I solved the issue by using the negative look-ahead regex, so basically anything inside (?!ignore1|ignore2) will be ignored.

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^api-token-auth/', obtain_jwt_token),
    url(r'^(?!admin|api-token-auth|static).*$', views.home),
]

Note: I tried not including admin and api-token-auth inside the negative look-ahead and it didn’t work for me, it will still route path that starts with admin to the views.home, not sure why as according to my understanding, django should match the earlier ones first.

0👍

Since index.html is templated (a dynamic page) because it’s served from your views instead of static, this becomes a bit weird.

For production, definitely use nginx or apache to serve the static content in a similar fashion.

For development, this is what I would do:

from django.contrib.staticfiles.views import serve

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^api-token-auth/', obtain_jwt_token),
]

# Serve your static media (regex matches *.*)
if settings.DEBUG:
    urlpatterns.append(url(r'(?P<path>.*\..*)$', serve))

# Serve your single page app in every other case
urlpatterns.append(url(r'^.*$', views.home))

0👍

The answer provided above by @billi works fine, up to a point. My problem is that for my SPA I want (need?) to use the Vue <router-view> and <router-link> system. But the router.js maps paths to Vue components, and some of my paths are to Django views via Django urls.py – for example, this will result in a blank page if navigating to /api/places from the Vue router nav, but will resolve correctly to a Django if refreshed.

Ideas?

urls.py

urlpatterns = [
    # Django
    path('admin', admin.site.urls),
    url(r'^api-auth/', include('rest_framework.urls')),
    url(r'^api/places$', views.PlaceList.as_view()),

    # catchall to Vue single page app
    url(r'^.*$', TemplateView.as_view(template_name='myapp/spa.html'), name='home'),
]

router.js

export default new Router({
  mode: 'history',
  routes: [
    {path: '/search', component: Search},
    {path: '/about', component: About},
    {path: '/', component: Home},
    {path: '/api/places', }
  ]
})

App.vue

<template>
  <div id="app">
    <div>
    <router-link to="/search">Search</router-link> ::
    <router-link to="/about">About</router-link>
    <router-link to="/">Home</router-link> ::
    <router-link to="/api/places">Place API</router-link> ::
  </div>
  <router-view></router-view>
  </div>
</template>

0👍

Don’t know if anyone still looking for the solution.
I build Django Rest API and Vue front totally in a stand alone way. Then when i tried to put the dist folder items in static files of Django and tried to server through gunicorn and NGINX I just could not get the spa working.
Finally did this and very surprisingly it works.

Basically,

forward error page to the index.html of Vue.

Here’s my NGINX config:

location /static/ {
    root /root/bdn/bdn/server/;
}

location /media/ {
    root /root/bdn/bdn/server/;
}

location ^~ /admin/ { # Define routes to be directed to backend as proxy
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass http://unix:/run/gunicorn.sock;
}

location ^~ /api/ { # Define routes to be directed to backend as proxy
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass http://unix:/run/gunicorn.sock;
}

location ^~ /api-auth/ {
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass http://unix:/run/gunicorn.sock;
}

location ^~ /{
    root /root/bdn/bdn/server/templates/;
    index index.html;
}

error_page 404 /;  # THIS IS WHAT IT CAME DOWN TO AFTER HOURS AND HOURS OF SEARCHING

Leave a comment