Handling single page application url and django ur

2020-05-14 12:46发布

I have a single page application created in Vue.js that utilizes the HTML5 History Mode for routing, and the html file is served with Django.

The urls.py of django is like so:

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

And views.home:

def home(request):
    return render(request, 'index.html')

Consider the following scenario:

  1. User visits the home page (i.e., /)

Since, the home page responds with required index.html for the Single page Vuejs app, it works like its supposed to.

  1. From there the user navigates to the about page (i.e., /username/12).

Its still working fine, as its navigating with the Vue router.

  1. Now, the user refreshes the page.

Since there's no /username/12 in the urls.py patterns, it will show Page not found (404).

Now, I could provide another pattern in urls.py to catch all pattern in the last order as this:

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?

6条回答
一纸荒年 Trace。
2楼-- · 2020-05-14 12:54

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.

查看更多
劳资没心,怎么记你
3楼-- · 2020-05-14 12:55

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) 
    ]
    
查看更多
Summer. ? 凉城
4楼-- · 2020-05-14 13:02

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))
查看更多
叼着烟拽天下
5楼-- · 2020-05-14 13:05

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

查看更多
时光不老,我们不散
6楼-- · 2020-05-14 13:19

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/
查看更多
女痞
7楼-- · 2020-05-14 13:20

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>
查看更多
登录 后发表回答