代码之家  ›  专栏  ›  技术社区  ›  kramer65

为什么django不能正确地提供SPA静态文件?

  •  0
  • kramer65  · 技术社区  · 5 年前

    我正在用一个 Django backend Vuejs frontend .在开发中,我分别用 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运行一切。为了做到这一点,我正在努力遵循 this description .我有以下几点 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')),
    ]
    

    并在“我的设置”中设置以下设置。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']
    

    然后我跑了一圈。py静态:

    $ 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服务器 ,启动Django dev服务器并打开浏览器。终端向我显示:

    $ 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中正确地提供这些文件吗?

    enter image description here

    enter image description here

    [编辑]

    我刚发现如果我改变

    STATIC_URL = '/static/'
    

    STATIC_URL = '/'
    

    当我去看电影时,它能正常工作 http://127.0.0.1:8000/index.html 但是现在 http://127.0.0.1:8000/ 给了我这个错误:

    enter image description here

    [编辑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']
    

    在档案里 设置/URL。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/ .

    不幸的是,我仍然得到同样的结果。索引。html加载,但js和css文件仍然不加载。还有其他想法吗?

    0 回复  |  直到 5 年前
        1
  •  3
  •   Siddharth Bhatia    5 年前

    错误原因

    第一个病例

    STATIC_URL = '/static/'
    

    Django试图在数据库中查找静态文件 backend/static/ 文件夹仅适用于带有url的任何内容 /static/ i、 例如:。 /static/css/* /static/js/* 在你的 index.html ,但事实并非如此 指数html 有像这样的文件引用 /css/* /js/* ,因此没有找到它们。

    这个案例在博客示例中起作用的原因与此相同,即他们的模板文件保存在 '../frontend/build 目录和静态文件位于“/前端/构建/静态“因此 指数html 我会寻找 static/js/* 而不是 /js/* 因此 /静止的/ 在Django中访问url位置,然后Django会在 backend/static 正确地

    这就是为什么在代码中,将其设置为第二种情况,即。

    STATIC_URL = '/'
    

    获取静态文件的URL以正确访问 /css/* /js/* 甚至你的 /index.html 可以被认为是一个静态文件,即所有这些URL都被认为是静态的,并在 后端/静态/ 文件夹,从而在浏览器中正确显示。

    但是现在URL被弄乱了,也就是说:

    re_path('', TemplateView.as_view(template_name='index.html')),
    

    可以被视为Django在寻找这个位置: / ,但它已被保留用于搜索静态文件,并且在搜索后不包含任何文件名 / 意味着你没有在找任何文件。

    可能的解决方案

    Django允许自定义url模式,即您可以在设置中创建两个新变量。py即。

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

    然后配置你的URL。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)
    
        2
  •  2
  •   shahab-qazavi    5 年前

    我很久以前就遇到过这个问题,我用一个简单的方法解决了它。 太简单了。 正如我所看到的,你的静态文件夹不在你的应用程序根目录中,它位于错误的位置。 把它放到你的应用程序根目录中。。。因为Django正在寻找应用程序主根目录中的静态文件夹,在那里可以查看。派伊在里面。 也许你有5个或更多的应用程序。 Django不关心你拥有的应用程序的数量,Django只会在你的应用程序根目录中查找你的静态文件夹。 但是模板文件夹可以位于项目根目录中。 所以,把你的静态文件夹放到你的应用根目录中。 在这种情况下,我指的是驾驶舱内的根。

    然后你必须在设置中添加这些更改。py

    PROJECT_NAME = '---Your projects name---'
    STATIC_URL = '/static/'
    STATIC_ROOT = os.path.join(BASE_DIR, PROJECT_NAME, 'static/')
    
    EXTERNAL_APPS = [
    
         'django.contrib.staticfiles',
      ]
    

    祝你好运

        3
  •  1
  •   Sasja Vandendriessche    5 年前

    使用 STATIC_URL

    首先 静态URL 将是静态文件所在的url will be served .所以如果你 STATIC_URL='/' 然后导航到 http://127.0.0.1:8000/index.html 它基本上是为公众服务的 index.html 从Django应用程序的静态文件夹中。如果你去 http://127.0.0.1:8000/ 您位于“静态文件夹根目录”,如您所见,目录索引是被禁止的。所以不要使用 静态URL='/'

    文件的位置

    您遵循的教程将SPA的build文件夹添加到Django中的模板中 settings.py 文件

    因此,你应该尝试添加:

    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(os.path.dirname(BASE_DIR), 'frontend', 'dist')],  # <== ADDED THIS
            'APP_DIRS': True,
            'OPTIONS': {
                # removed to keep this example small
            },
    ]
    

    或者,如果要将静态文件指向模板目录:

    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'static')], 
            'APP_DIRS': True,
            'OPTIONS': {
                # removed to keep this example small
            },
    ]
    

    然而,我不认为这是最佳实践,因为静态文件(图像、JS、CSS)在 Django .

    关于 os.path

    还有,为了你的 STATICFILES_DIRS 不是这样的 os.path.join() works 。第一个参数应该是要与一个或多个路径组件连接的路径,然后将这些组件作为下一个参数传递。此外 BASE_DIR 不在正确的目录级别,正如您在打印 基地迪尔 路径你需要孩子的父母 基地迪尔 目录去找到那条路 parent directory ,你能行 os.path.dirname(BASE_DIR) 因为 基地迪尔 这是一条路。应用这个我们得到:

    STATICFILES_DIRS = [
        os.path.join(os.path.dirname(BASE_DIR), frontend, dist),
    ]
    

    最后一点

    我确实觉得,让你的水疗中心由Django提供服务,而不是完全独立于Django,这可能是一种反模式 基地迪尔 (因此命名为基本目录)。但我不知道它的来源,也不知道它究竟是如何最佳实践的。

        4
  •  0
  •   scriptmonster    5 年前

    第一件事!如果你想把Django的用户界面作为生产的静态文件,这是个坏主意!您需要考虑使用nginx或类似的web服务器为您提供静态文件。

    这个 TemplateView 在你的 urls.py 这有点问题。因为你的静态文件是在 STATIC_URL = '/static/' 在第一个场景中,js和css文件的URL不匹配。你可以编辑这些文件,然后 static 模板标签在里面 index.hml 但这也不是个好主意。在vuejs端进行更改时,需要直接构建和替换包,然后更新站点。所以这一行需要永远删除,索引。html必须被视为静态文件:

        re_path('', TemplateView.as_view(template_name='index.html')),
    

    当你改变的时候 STATIC_URL = '/' 您可以访问所有静态文件。如果你让django进来 debug = True 它自动提供静态文件。否则,需要向文件中添加静态url conf。(别忘了,这不利于生产!)

    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")),
    ]
    
    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
    

    使用此设置时,您需要小心URL和静态文件命名。它们可能会相互超越。

    附言:谢谢你提出这么详细的问题。

        5
  •  0
  •   Chih Sean Hsu    5 年前

    有一种愚蠢的方法可以解决这个问题。

    这个问题是关于VueJS构建的html将从如下路径获取js和css /js/app.12312sd.js /css/app.dsfeewj.css

    html built by vueJS

    静态文件根路径的默认设置为 /static/ ,所以您只需更改每个 /js/... /css/... 在里面 index.html 手动或使用 sed 或者用别的东西来代替它们 /static/js/.../ /static/css/.../ .

    你无法取代 STATIC_URL / 因为它是你网站的根url。

    在你编辑它之后,它看起来是这样的,然后你的django服务器就会工作得很好。

    enter image description here

        6
  •  0
  •   Nikhil Khandelwal    5 年前

    在构建前端之前,将css/和js/放在名为“static”的目录中,留下索引。html就是这样。然后当你跑的时候 yarn build 你的目录结构如下-

    cockpit
    ├── backend/
    │   ├── cockpit/
    │   │   ├── views.py
    │   │   ├── css/
    │   │   └── etc..
    │   ├── settings/
    │   │   └── settings.py
    │   └── manage.py
    └── frontend/
        └── dist/
            ├── static/
            │       ├── css/
            │       └── js/
            └── index.html
    

    设置基地和前端指向“驾驶舱/前端/距离”,以便以后快速使用。

    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    FRONTEND_DIR = os.path.join(os.path.dirname(BASE_DIR),'frontend','dist')
    

    现在,或者像之前一样将前端静态文件复制到后端目录-

    STATIC_URL = '/static/'
    STATICFILES_DIRS = [
        os.path.join(FRONTEND_DIR,'static/'),
    ]
    STATIC_ROOT = os.path.join(BASE_DIR, "static/")
    

    或者,您可以直接将后端指向构建的前端目录,而不是再次复制它。(我总是这样做)

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

    在模板目录中包含构建的前端目录,以便django能够提供服务 index.html

    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [FRONTEND_DIR],
            'APP_DIRS': True,
            'OPTIONS': {
                # removed to keep this example small
            },
        },
    ]
    

    现在运行服务器

    $ python manage.py collectstatic
    $ python manage.py runserver
    

    如果以后决定在线部署,可以通过设置Nginx为静态目录中的静态文件提供服务,轻松地使用此配置。 希望这有帮助:)

    清除[编辑]中的困惑

    STATIC_ROOT指向系统上的目录。STATIC_URL指的是在使用网站时访问静态_根目录中的静态文件的URL。

    假设你有一个文件脚本。如图所示,在您的目录中

    cockpit
    ├── backend/
    │   ├── cockpit/
    │   │   ├── views.py
    │   │   ├── css/
    │   │   └── etc..
    │   ├── settings/
    │   │   └── settings.py
    │   └── manage.py
    └── frontend/
        └── dist/
            ├── static/
            │       ├── css/
            │       └── js/
            │          └── script.js
            └── index.html
    

    您已经将STATIC_ROOT设置为指向“campock/frontend/dist/STATIC/”。

    现在,如果您将STATIC_URL设置为“STATIC/”,您就可以在 http://127.0.0.1:8000/static/js/script.js

    或者,如果您将静态_URL设置为“xcsdf/”,则可以在以下位置访问js文件: http://127.0.0.1:8000/xcsdf/js/script.js