如何用Django做后端搭配React.js前端

目录


Django后端搭配React.js前端


现在都流行前后端分离。基本上就是用javascript的fetch()或者Axios.get()/Axios.post() 在前端里调用后端的API,比如Django的Django Rest Framework或者是Node.js的Express.js框架。下面说的是用Django Rest Framework。以后计划会说Express.js。


一般人用的Template


我发现网上通常都要设置好几步,比如在views.py 里加一个function base view,

技巧

views.py里的代码如下

1
2
def index(request):
   return render(request, "build/index.html")

urls.py

1
2
3
4
5
from django.urls import path
from demo.views import index
urlpatterns = [
    path("", index, name="index")
]

安装django-cors-headers来解决CORS问题,然后在setting.py, 在TEMPLATES,static_folder和STATICFILES_DIRS变量添加前端目录。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
MIDDLEWARE = [
   'corsheaders.middleware.CorsMiddleware',  # added to solve CORS
   'django.middleware.common.CommonMiddleware',  # added to solve CORS
   'django.middleware.security.SecurityMiddleware',
   'django.contrib.sessions.middleware.SessionMiddleware',
   'django.middleware.common.CommonMiddleware',
   'django.middleware.csrf.CsrfViewMiddleware',
   'django.contrib.auth.middleware.AuthenticationMiddleware',
   'django.contrib.messages.middleware.MessageMiddleware',
   'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

TEMPLATES = [
   {
       'BACKEND': 'django.template.backends.django.DjangoTemplates',
       'DIRS': [os.path.join(BASE_DIR, 'frontend')]  # added the root folder of frontend here
       ,
       'APP_DIRS': True,
       'OPTIONS': {
           'context_processors': [
               'django.template.context_processors.debug',
               'django.template.context_processors.request',
               'django.contrib.auth.context_processors.auth',
               'django.contrib.messages.context_processors.messages',
           ],
       },
   },
]
STATIC_URL = '/static/'
STATICFILES_DIRS = (
   os.path.join(BASE_DIR, 'frontend', "build", "static"),  # update the STATICFILES_DIRS
)

我觉得这么做太麻烦了,还会有可能有图片加载不了的问题。


我是这么用serve函数解决的

我发现一个更简单有效的办法。就是在urls.py里重写serve()。我在django.conf.urls.static里找到serve 函数,然后像下面那样重写(不这么做的话会有打开不了根目录路径的问题)


In urls.py


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from django.conf import settings
from django.conf.urls.static import static, serve
from django.contrib import admin
from django.urls import path, re_path, include
from rest_framework.routers import DefaultRouter
from django.http import (
    FileResponse, Http404, HttpResponse, HttpResponseNotModified,
)
import posixpath
from pathlib import Path
from django.utils._os import safe_join
import mimetypes
from django.utils.http import http_date
from django.utils.translation import gettext as _
import demo.views
def my_serve(request, path, document_root=None, show_indexes=False):
    path = posixpath.normpath(path).lstrip('/')
    fullpath = Path(safe_join(document_root, path))
    if fullpath.is_dir():
        fullpath = Path(safe_join(document_root, 'index.html'))
      
    if not fullpath.exists():
        raise Http404(_('"%(path)s" does not exist') % {'path': fullpath})
    # Respect the If-Modified-Since header.
    statobj = fullpath.stat()
 
    content_type, encoding = mimetypes.guess_type(str(fullpath))
    content_type = content_type or 'application/octet-stream'
    response = FileResponse(fullpath.open('rb'), content_type=content_type)
    response["Last-Modified"] = http_date(statobj.st_mtime)
    if encoding:
        response["Content-Encoding"] = encoding
    return response

router = DefaultRouter()
router.register(r'testApi', demo.views.TestViewSet)

urlpatterns = [
    re_path(r'^api/v1/', include(router.urls)),
    path('admin/', admin.site.urls),
    re_path(r'^(?P<path>.*)$', my_serve, { 'document_root': settings.FRONTEND_ROOT }),
]  

in setting.py


1
2
3
4
5

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

FRONTEND_ROOT = os.path.abspath(os.path.join(BASE_DIR, '..', 'frontend', 'build'))


这种做法只需改setting.pyFRONTEND_ROOT一个地方。Django就会把所有的routing交给React router了。