3

I am attempting to serve a django app with a vue frontend and need some help configuring the static files.

The Question TLDR:

How do I get Django to recognize this built path as an attempt to reach a static file, or alternatively, how do I modify the post-build injection path on the Vue side to match the current Django static-file settings?

Django serves the built index.html from vue, however it can not find the static files (scripts/css) that are autoinjected from the build process due to the path being "absolute". I have modified the vue.config.js to not autoinject the scripts (since they will need {% static %} during the build, and the template index.html to add them in appropriately.

My directory structure is as follows:

- Root
  - app
  - migrations
  - templates
    - app (outputDir)
      - index.html (Built from vue)
      - base.html
      - templateFolder1
      - templateFolder2
  - various .py files
  - assets (assetsDir)
    - css
      - static files
    - js
      - static files
  - frontend
    - public
      - index.html (Template for build for vue)
    - src
    - assets
    - components
    - App.vue
    - main.js

The build is run from the frontend directory, with --no-clean to not delete my django templates folder on build.

Here is my workaround for adding {% static %} tags to the built index.html. I realize I am breaking the convention vue has that assetsDir is a subdirectory of outputDir, and I am not opposed to adding another staticfile dir to the settings.py to match the convention (although my issue is still the same).

vue.config.js

publicPath: isProd ? '/app/' : '/',
outputDir: __dirname + '/../app/templates/app',
assetsDir: "../../../assets",
indexPath: './index.html',
configureWebpack: {
        ...
        plugins: [
            new HtmlWebpackPlugin({
                template: __dirname + '/public/index.html',
                favicon: __dirname + '/../assets/img/favicon/favicon.ico',
                inject: false,
                minify: false
            })
        ],
    },

public/index.html

{% load static %}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>MAPP Remote</title>
      <link rel="shortcut icon" href="{% static '<%= htmlWebpackPlugin.files.favicon %>' %}">
      <% for (key in htmlWebpackPlugin.files.css) { %> <link rel="stylesheet" href="{% static '<%= htmlWebpackPlugin.files.css[key] %>' %}"> <% } %>
  </head>
    <body>
    ...
    <div id="app"></div>

    <!-- built files will be auto injected -->
  <% for (key in htmlWebpackPlugin.files.js) { %> <script type="text/javascript" src="{% static '<%= htmlWebpackPlugin.files.js[key] %>' %}"></script> <% } %>
  </body>
</html>

The built index.html:

app/templates/app/index.html

{% load static %}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>MAPP Remote</title>
      <link rel="shortcut icon" href="{% static '/app/favicon.ico' %}">
       <link rel="stylesheet" href="{% static '/app/../../../assets/css/chunk-vendors.0ba3e87d.css' %}">  <link rel="stylesheet" href="{% static '/app/../../../assets/css/app.fb012fc8.css' %}"> 
  </head>
  <body>
    ...
    <div id="app"></div>

    <!-- built files will be auto injected -->
   <script type="text/javascript" src="{% static '/app/../../../assets/js/chunk-vendors.6a3b11f1.js' %}"></script>  <script type="text/javascript" src="{% static '/app/../../../assets/js/app.45295baa.js' %}"></script> 
  </body>
</html>

My django settings.py configuration for static files:

Settings.py

...
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
STATIC_URL = "/static/"
STATICFILES_DIRS = [os.path.join(BASE_DIR, "assets")]
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
...

The way my static file finders are configured via Django requires modification to the built script/css paths inside of app/templates/app/index.html.

In stead of <script src="{% static '/app/../../../assets/js/chunk-vendors.6a3b11f1.js' %}">

The path currently needs to be <script src="{% static 'js/chunk-vendors.6a3b11f1.js' %}">

Changing the assetsDir path in vue.config.js to match the Vue convention of having assets be a subdirectory of outputDir results in a similar issue in which the path is 'app/assets/js/...' rather than 'js/...'

1 Answer 1

3

I decided to adjust the path that is loaded into the template during the Vue build by modifying public/index.html file as well as the vue.config.js options. I declared a const asset_dir = '/asset/dir in vue.config.js and then added this as an extra option to the HtmlWebpackPlugin to pull it into the template. Lastly, I substring the path for the static files by the length of the unnecessary portion of the path.

vue.config.js

const asset_dir = "../../../assets"

module.exports = {
    publicPath: isProd ? '/app/' : '/',
    outputDir: __dirname + '/../app/templates/app',
    assetsDir: asset_dir,
    indexPath: './index.html',
    configureWebpack: {
        ...
        plugins: [
            new HtmlWebpackPlugin({
                template: __dirname + '/public/index.html',
                inject: false,
                minify: false,
                assetsDir: asset_dir
            })
        ],
    }
}

public/index.html

{% load static %}
<% if (htmlWebpackPlugin.options['assetsDir'] !== undefined) { %> <% var assetDirLength = htmlWebpackPlugin.options['assetsDir'].length + htmlWebpackPlugin.files.publicPath.length + "/".length%> <% } %>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>MAPP Remote</title>
      <link rel="shortcut icon" href="{% static 'img/favicon/favicon.ico' %}">
      <% for (key in htmlWebpackPlugin.files.css) { %> <link rel="stylesheet" href="{% static '<%= htmlWebpackPlugin.files.css[key].substr(assetDirLength) %>' %}"> <% } %>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    <!-- Pulled from htmlWebpackPlugin Docs -->
    <!-- https://github.com/jaketrent/html-webpack-template/blob/86f285d5c790a6c15263f5cc50fd666d51f974fd/index.html -->
    <% for (var chunk in htmlWebpackPlugin.files.chunks) { %>
      <script src="{% static '<%= htmlWebpackPlugin.files.chunks[chunk].entry.substr(assetDirLength) %>' %}"></script>
    <% } %>

  </body>
</html>

Sign up to request clarification or add additional context in comments.

2 Comments

This is currently broken with vue3, partially due to the move to vite. I'm in the process of figuring out a fix for this, as vite will not process files that are not strictly HTML (IE {% static %} makes it fail).
have you been able to achieve it?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.