如何从 Heroku 上的 Django 项目正确提供我的 Angular 应用程序静态文件?

How to properly serve my Angular application static files from a Django project on Heroku?

我正在尝试在 Heroku 上部署我使用 Django 2、DRF 和 Angular 6 构建的应用程序,并努力获取我的 Angular 应用程序的静态文件。我在开发环境和 Heroku 上都使用 WhiteNoise。 项目结构如下:

my_project/ 
   |-frontend/
   |    |- dist/
   |    |- ...
   |-myproject/
   |    |-settings/
   |    |  |- __init.py
   |    |  |- base.py
   |    |  |- prod.py
   |    |  |- dev.py
   |    |- __init__.py
   |    |- urls.py
   |    |- views.py
   |    |- wsgi.py
   |-other_app1/
   |    |- ...
   |-other_app2/
   |    |- ...
   |-staticfiles/
   |    |-....
   |-manage.py
   |-Procfile
   |-requirements.txt

我以这样的方式配置了 Heroku,每次部署我的应用程序时,都会构建我的 angular 应用程序,因此在 frontend/dist/ 中生成静态文件,然后 collectstatic 是 运行 并将它们复制到项目根目录中的静态文件夹中。我的设置是:

ANGULAR_APP_DIR = os.path.join(BASE_DIR, 'frontend/dist')

STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATIC_URL = '/static/'

STATICFILES_DIRS = [
    ('frontend', os.path.join(ANGULAR_APP_DIR)),
]

这很好用,并收集了我的 angular 应用程序的所有文件到 staticfiles/ 和一个名为 frontend/

的文件夹

我的问题是,现在我正在尝试提供位于 staticfiles/frontend 中的 index.html 文件,但我无法使其工作:

urls.py

urlpatterns = [      
    path('', HomePageView.as_view()), #first one
    # .... Other urls
]

views.py

from django.views import static

# Serves the index page
class HomePageView(TemplateView):
  def get(self, request, **kwargs):
      return static.serve(request,'index.html', 'staticfiles')

这会找到并提供 index.html 文件,但无法成功将我的应用程序的其余必要 js 文件提供给 运行(polyfills、main、样式...),因为'/static/' 不会自动附加到请求它们的 url。 有人知道我如何告诉 Django 或 Heroku 在 staticfiles/frontend 文件夹下找到它们吗?

生成的 index.html 如下所示:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>My Angular Project</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="styles.578ada0b0e3742b4b4bb.css"></head>
<body class="with-top-navbar">
  <app-root></app-root>
<script type="text/javascript" src="runtime.92d800a8cdfd75f49eae.js"></script>
  <script type="text/javascript" src="polyfills.7ee4ee2275a8c5d5ea6f.js"></script>
  <script type="text/javascript" src="main.4aeccc46b72bbab58fd0.js"></script></body>
</html>

我的控制台的输出是:

[29/Jun/2018 13:31:30] "GET / HTTP/1.1" 200 638
Not Found: /styles.578ada0b0e3742b4b4bb.css
Not Found: /runtime.92d800a8cdfd75f49eae.js
[29/Jun/2018 13:31:30] "GET /styles.578ada0b0e3742b4b4bb.css HTTP/1.1" 404 2353
Not Found: /polyfills.7ee4ee2275a8c5d5ea6f.js
Not Found: /main.4aeccc46b72bbab58fd0.js
[29/Jun/2018 13:31:30] "GET /polyfills.7ee4ee2275a8c5d5ea6f.js HTTP/1.1" 404 2359
[29/Jun/2018 13:31:30] "GET /runtime.92d800a8cdfd75f49eae.js HTTP/1.1" 404 2353
[29/Jun/2018 13:31:30] "GET /main.4aeccc46b72bbab58fd0.js HTTP/1.1" 404 2344
Not Found: /styles.578ada0b0e3742b4b4bb.css
[29/Jun/2018 13:31:32] "GET /styles.578ada0b0e3742b4b4bb.css HTTP/1.1" 404 2353
Not Found: /runtime.92d800a8cdfd75f49eae.js
[29/Jun/2018 13:31:32] "GET /runtime.92d800a8cdfd75f49eae.js HTTP/1.1" 404 2353
Not Found: /polyfills.7ee4ee2275a8c5d5ea6f.js
[29/Jun/2018 13:31:32] "GET /polyfills.7ee4ee2275a8c5d5ea6f.js HTTP/1.1" 404 2359
Not Found: /main.4aeccc46b72bbab58fd0.js
[29/Jun/2018 13:31:32] "GET /main.4aeccc46b72bbab58fd0.js HTTP/1.1" 404 2344

此外,请注意我不想手动编辑生成的 index.html 也不想将其放在其他地方,因为这会改变我的部署工作流程。

此外,我知道 django.views.statics.serve 不适合生产,所以如果您知道如何以其他方式提供此文件,也欢迎提供提示

谢谢!

您可以使用render

from django.shortcuts import render
return render(request, 'index.html', {})

对于静态文件,需要添加/static/ like

{% load static %}
<script type="text/javascript" src="{% static "polyfills.7ee4ee2275a8c5d5ea6f.js" %}"></script>

最后我可以通过安装这个不错的包来自己解决它:django-spa 它构建在 whitenoise 之上,直接从静态文件根文件夹中为 SPA 提供索引和文件。

包的使用示例here

此外,我不得不从收集静态文件的目录中删除 "frontend" 文件夹:

STATICFILES_DIRS = [
os.path.join(ANGULAR_APP_DIR),
]

并删除我用于在“/”上提供索引的视图:

urlpatterns = [
  # The following SPA settings are handled in Django-SPA
  # - everything not matched in Django's urlpatterns goes to /
  # - index.html served on /
  # - all /static/... files served on /...
  # other views....
  path('admin/', admin.site.urls),
]

感谢@metakermit的打包!

如果您不喜欢使用软件包,可以尝试以下方法


settings.py

ANGULAR_APP_DIR = os.path.join(BASE_DIR, 'path/to/your/angular/app/dist')

STATICFILES_DIRS = [
    os.path.join(ANGULAR_APP_DIR),
]

STATICFILES_STORAGE = os.environ.get('STATICFILES_STORAGE', "whitenoise.storage.CompressedManifestStaticFilesStorage")

WHITENOISE_ROOT = STATIC_ROOT
WHITENOISE_INDEX_FILE = True

步骤:

  1. ng build
  2. python manage.py collectstatic --no-input --clear
  3. python manage.py runserver

Reference