Обработка URL-адреса приложения одной страницы и URL-адреса django

У меня есть одностраничное приложение, созданное в Vue.js, которое использует режим истории HTML5 для маршрутизации, а файл html - с Django.

urls.py django выглядит так:

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

И views.home:

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

Рассмотрим следующий сценарий:

  • Пользователь посещает домашнюю страницу (т.е. /)

Так как на домашней странице отвечает требуемый index.html для приложения Vuejs с одной страницей, он работает так, как предполагалось.

  1. Оттуда пользователь переходит к странице about (т.е. /username/12).

Он все еще работает нормально, поскольку он перемещается с маршрутизатором Vue.

  1. Теперь пользователь обновляет страницу.

Так как там нет /username/12 в шаблонах urls.py, он покажет Страница не найдена (404).

Теперь я мог бы предоставить еще один шаблон в urls.py, чтобы поймать весь шаблон в последнем порядке, как это:

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

Но другие URL-адреса, такие как медиа или статические URL-адреса, будут также указывать на одно и то же правило репликации всех шаблонов catch. Как я могу решить эту проблему?

Ответ 1

У меня похожая проблема.

Как я могу использовать vue-router и django rest framework одновременно?

Это мое решение этой проблемы. Надеюсь, это поможет вам.

Ожидаемые результаты:

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

и я пробую это, и это работает!

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")),
]

Порядок важен, url(r'^.*$', TemplateView.as_view(template_name="index.html")), последний

а это мой вью роутер

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

Мой проект на GitHub

Ответ 2

Поскольку вы упомянули "одну страницу":

  • Предполагается, что сервер будет обслуживать только одну страницу index.html (или все, что вы хотели бы назвать).

  • Сервер и веб-приложение (интерфейсный) код будут связываться через вызовы api, так что сервер предоставляет ресурсы, и веб-приложение заботится об использовании этого ресурса.

  • В случае отсутствия ресурса сервер по-прежнему не должен отвечать отдельной страницей, он все равно должен отвечать сообщением, которое может использовать веб-приложение.

У меня есть одностраничное приложение, созданное в Vue.js, которое использует режим истории HTML5 для маршрутизации

Я считаю, что вы используете vue-router, который имитирует одностраничное приложение как полнофункциональное многостраничное приложение.


Вы можете взглянуть на this и this, но приведенное выше верно для одностраничного приложения.


Вы поделились своими URL-адресами:

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

Но другие URL-адреса, такие как медиа или статические URL-адреса, будут также указывать на одно и то же правило репликации всех шаблонов catch. Как я могу решить эту проблему?

  • Способом, которым вы можете управлять, будет либо с помощью службы, отличной от '/', как указано выше для /app.

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

    и в файле router.js:

    new Router({
      mode: 'History',
      base: '/app'
      routes: [
        {
          path: '/',
          name: 'name',
          component: ComponentName
        }
      ]
    })
    
  • Или префикс цели, обслуживаемой такими URL-адресами, как

    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) 
    ]
    

Ответ 3

Поскольку index.html является шаблоном (динамическая страница), потому что он служил из ваших представлений вместо статического, это становится немного странным.

Для производства определенно используйте nginx или apache для обслуживания статического контента аналогичным образом.

Для развития, это то, что я сделал бы:

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))

Ответ 4

Ответ, предоставленный выше @billi, работает отлично, до определенного момента. Моя проблема в том, что для моего SPA я хочу (нужно?) Использовать систему Vue <router-view> и <router-link>. Но router.js отображает пути к компонентам Vue, и некоторые из моих путей - к представлениям Django через Django urls.py - например, это приведет к пустой странице, если перейти к /api/place из nav маршрутизатора Vue, но будет корректно преобразован в Django, если обновится.

Идеи?

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>

Ответ 5

Я решил проблему с помощью отрицательного регулярного выражения, поэтому все, что находится внутри (?!ignore1|ignore2) будет игнорироваться.

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

Примечание: я попытался не включать admin и api-token-auth в отрицательный views.home, и у меня это не сработало, он все равно будет views.home путь, который начинается с admin на views.home, не уверен почему, как в соответствии с моим Понимая, Джанго должен соответствовать первым.

Ответ 6

Я использую режим истории с включенным маршрутизатором VueJS в Django 2.x следующим образом:

Из приложения urls.py вам нужно repath URL-адрес вашего внешнего интерфейса примерно так:

# 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")),
]

Теперь режим истории работает плавно!

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

Стать:

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