python - 为什么 django 不能正确地为我的 SPA 静态文件提供服务?
问题描述
我正在使用Django 后端和Vuejs 前端构建一个网站。python manage.py runserver
在开发中,我分别用和启动了后端和前端yarn serve
。这很好用,我现在想部署该网站。为此,我跑了yarn build
,它dist/
在我的前端文件夹中创建了一个文件夹。所以我的结构是这样的:
cockpit
├── backend/
│ ├── cockpit/
│ │ ├── views.py
│ │ ├── css/
│ │ └── etc..
│ ├── settings/
│ │ └── settings.py
│ └── manage.py
└── frontend/
└── dist/
├── index.html
├── css/
└── js/
我现在想frontend/dist/
从我的 django 项目中提供源代码,以便我可以使用 uwsgi 运行所有内容。为此,我正在尝试遵循此描述。我有以下settings/urls.py
from django.contrib import admin
from django.urls import include, path, re_path
from django.views.generic import TemplateView
urlpatterns = [
path('admin/', admin.site.urls),
path('cockpit/', include("cockpit.urls")),
re_path('', TemplateView.as_view(template_name='index.html')),
]
并在我的 settings.py 中设置以下设置:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ['static'], # <== ADDED THIS
'APP_DIRS': True,
'OPTIONS': {
# removed to keep this example small
},
},
]
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, '../frontend/dist'),
]
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
print("BASE_DIR:", BASE_DIR)
print("STATIC_ROOT:", STATIC_ROOT)
print("STATICFILES_DIRS:", STATICFILES_DIRS)
印刷品向我展示了这一点:
BASE_DIR: /home/kramer65/repos/cockpit/backend
STATIC_ROOT: /home/kramer65/repos/cockpit/backend/static/
STATICFILES_DIRS: ['/home/kramer65/repos/cockpit/backend/../frontend/dist']
然后我运行了`python manage.py collectstatic:
$ python manage.py collectstatic
150 static files copied to '/home/kramer65/repos/cockpit/backend/static'.
所以它现在看起来像这样:
cockpit
├── backend/
│ ├── cockpit/
│ │ ├── views.py
│ │ ├── css/
│ │ └── etc..
│ ├── settings/
│ │ └── settings.py
│ └── manage.py
│ └── static/
│ ├── index.html
│ ├── css/
│ └── js/
└── frontend/
└── dist/
├── index.html
├── css/
└── js/
http-server
我通过从backend/static/
文件夹中运行(节点)来测试它。在浏览器中,网站加载并运行完美。下面是命令行的输出:
$ http-server
Starting up http-server, serving ./
Available on:
http://127.0.0.1:8080
http://192.168.0.104:8080
Hit CTRL-C to stop the server
[2020-05-18T13:50:58.487Z] "GET /" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:76.0) Gecko/20100101 Firefox/76.0"
(node:5928) [DEP0066] DeprecationWarning: OutgoingMessage.prototype._headers is deprecated
[2020-05-18T13:50:58.671Z] "GET /css/chunk-vendors.2c7f3eba.css" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:76.0) Gecko/20100101 Firefox/76.0"
[2020-05-18T13:50:58.679Z] "GET /css/app.e15f06d0.css" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:76.0) Gecko/20100101 Firefox/76.0"
[2020-05-18T13:50:58.681Z] "GET /js/chunk-vendors.9c409057.js" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:76.0) Gecko/20100101 Firefox/76.0"
[2020-05-18T13:50:58.687Z] "GET /js/app.c930fce5.js" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:76.0) Gecko/20100101 Firefox/76.0"
我停止了这个http-server
,启动了 Django 开发服务器并打开了浏览器。终端向我展示了这个:
$ python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
May 18, 2020 - 17:57:00
Django version 3.0.6, using settings 'settings.settings'
Starting ASGI/Channels version 2.4.0 development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
HTTP GET / 200 [0.22, 127.0.0.1:33224]
HTTP GET /static/debug_toolbar/css/print.css 200 [0.04, 127.0.0.1:33232]
HTTP GET /static/debug_toolbar/css/toolbar.css 200 [0.05, 127.0.0.1:33234]
HTTP GET /static/debug_toolbar/js/toolbar.js 200 [0.02, 127.0.0.1:33232]
HTTP GET /static/debug_toolbar/js/toolbar.timer.js 200 [0.04, 127.0.0.1:33234]
HTTP GET /js/chunk-vendors.9c409057.js 200 [0.80, 127.0.0.1:33228]
HTTP GET /css/chunk-vendors.2c7f3eba.css 200 [0.94, 127.0.0.1:33224]
HTTP GET /js/app.c930fce5.js 200 [0.98, 127.0.0.1:33230]
HTTP GET /css/app.e15f06d0.css 200 [0.99, 127.0.0.1:33226]
HTTP GET /favicon.ico 200 [0.09, 127.0.0.1:33226]
在浏览器控制台中,我看到源似乎已加载,但有些似乎是空的(0 字节)并且屏幕没有显示任何内容。下面是结果的屏幕截图和 Django 调试栏中的静态文件选项卡的屏幕截图。
有人知道为什么它不能在 Django 中正确地提供这些文件吗?
[编辑]
我刚刚发现如果我改变
STATIC_URL = '/static/'
至
STATIC_URL = '/'
当我去时它工作正常http://127.0.0.1:8000/index.html
,但现在http://127.0.0.1:8000/
给我这个错误:
[编辑 2]
好的,所以按照@geek_life 的建议,我将设置文件中的值更改为:
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PROJECT_NAME = 'cockpit'
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, PROJECT_NAME, "static/")
STATICFILES_DIRS = [os.path.join(BASE_DIR, '../frontend/dist')]
print("## BASE_DIR:", BASE_DIR)
print("## STATIC_ROOT:", STATIC_ROOT)
print("## STATICFILES_DIRS:", STATICFILES_DIRS)
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ['cockpit/static/'], # <= THIS IS WHAT I CHANGED
'APP_DIRS': True,
'OPTIONS': {} # And here some options
},
]
哪个打印出来
## BASE_DIR: /home/kramer65/repos/cockpit/backend
## STATIC_ROOT: /home/kramer65/repos/cockpit/backend/cockpit/static/
## STATICFILES_DIRS: ['/home/kramer65/repos/cockpit/backend/../frontend/dist']
在文件中settings/urls.py
我(仍然)得到了这个:
urlpatterns = [
path('admin/', admin.site.urls),
path('cockpit/', include("cockpit.urls")),
re_path('', TemplateView.as_view(template_name='index.html')),
]
static/
然后我将带有构建的 vuejs-ap 的文件夹从复制cockpit/backend/
到cockpit/backend/cockpit/
.
不幸的是,我仍然得到相同的结果。index.html 加载,但 js 和 css 文件仍然没有。还有其他想法吗?
解决方案
错误原因
对于第一种情况
STATIC_URL = '/static/'
Django 尝试在backend/static/
文件夹中查找静态文件,仅在 url 带有/static/
ie for eg/static/css/*
或/static/js/*
在您的 中提到index.html
的情况下,但这里不是这种情况,index.html
有文件引用,例如/css/*
and /js/*
,因此找不到它们。
这种情况在博客示例中起作用的原因是由于相同的原因,即它们的模板文件保存在一个'../frontend/build
目录下,而静态文件位于“../frontend/build/static”中,因此index.html
将寻找static/js/*
而不是/js/*
因此的/static/
url 位置在 Django 中访问,然后backend/static
正确查找文件
这就是为什么在您的代码中将其设置为第二种情况,即
STATIC_URL = '/'
正确获取静态文件的 url,/css/*
甚至/js/*
您/index.html
可以将其视为静态文件,即所有这些 url 都被认为是静态的,并在backend/static/
文件夹中进行搜索,因此在浏览器中正确显示。
但现在 URL 搞砸了,即:
re_path('', TemplateView.as_view(template_name='index.html')),
可以被视为 Django 正在寻找这个位置:/
,但它已经被保留用于搜索静态文件并且不包括任何文件名之后/
意味着您没有在寻找任何文件。
可能的解决方案
Django 允许自定义 url 模式,即您可以在 settings.py 中创建 2 个新变量,即
STATIC_URL = "/static/"
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
STATIC_JS_URL = "/js/"
STATIC_JS_ROOT = os.path.join(STATIC_ROOT, "js/")
STATIC_CSS_URL = "/css/"
STATIC_CSS_ROOT = os.path.join(STATIC_ROOT, "css/")
然后配置你的 urls.py
from django.contrib import admin
from django.urls import include, path, re_path
from django.views.generic import TemplateView
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('cockpit/', include("cockpit.urls")),
re_path('', TemplateView.as_view(template_name='index.html')),
]
urlpatterns += static(settings.STATIC_JS_URL, document_root=settings.STATIC_JS_ROOT)
urlpatterns += static(settings.STATIC_CSS_URL, document_root=settings.STATIC_CSS_ROOT)
推荐阅读
- ios - 我想在我的应用程序中购买电子书。我可以通过 webview 付款而不使用应用内购买吗.. iTunes 商店是否批准了我的应用
- javascript - 非法调用方法分配
- c# - 在 ASP.NET Core 的外部 OAuth 身份验证提供程序中使用 Bearer 令牌
- vb.net - 如何使用 vb.net 在子目录结构中查找并打开最新的 txt 文件
- android - Android将PCM字节数组写入wav文件
- docker - 在使用 Paket 从项目构建的 Docker 映像中找不到 Microsoft.AspNetCore.App 2.2.7
- c - 如何将更改保存在 c 上的动态树上?
- javascript - 带有 CSS 变量冒泡的 JavaScript 鼠标位置剪辑路径效果
- docker - 具有撰写文件单节点和本地映像的 docker 服务
- python - 计算列中工资范围的平均值