sin tribu
sin tribu

Reputation: 1180

Build a react project in django

I am embedding a react project inside my django app. I am serving the index.html file created by npm run build from django as opposed to just using django as a third party api. I like this approach as it leverages django user authentication/ csrf tokens/ etc.

After npm run build I am extracting the head and the scripts on index.html and putting them in my base.html of django, this way, the react scripts will be available wherever I need them and I can use my django templates for my navbar, footer etc.

The problem I am having is I have to copy and paste the headers and scripts on every build, so my question is if there is a way to make npm run build build the files with the same name every time, or perhaps another solution, so when I rebuild the react project I don't have to recopy and paste to base.html?

Here is a code snippet sample

base.html

<html>
<head>
<!-- Copy and pasted header files from react build-->
</head>
<body>

<navbar></navbar>

{% block content %}
{% endblock %}

<script> //copy and pasted scripts from react build - different script names on every build </script> 
</body>
</html>

And then a file served by django

homepage.html

{% extend 'base.html' %}

<div class="other-django-stuff"></div>

{% block content %}
   <div id="root"></div> // where my react project is anchored
{% endblock %}

Upvotes: 1

Views: 642

Answers (2)

sin tribu
sin tribu

Reputation: 1180

Thanks to Emanuele's guidance Here is the following solution using python. This assumes the react static build folder is added to STATIC_DIRS in settings.py:

base.html

<html>
<head>

{% include 'react_head.html' %}

</head>
<body>

<navbar></navbar>

{% block content %}
{% endblock %}


{% include 'react_scripts.html' %}

<script src='django-scripts.js'></script>

</body>
</html>
build_base.py

from bs4 import BeautifulSoup 

index = open( "build/index.html" )
soup = BeautifulSoup( index, features="html.parser" )

links   = soup.findAll( "link" )
scripts = soup.findAll( "script" )

with open( "./../templates/react_head.html", "w" ) as html:
    
    html.write( r"{% load static %}" + "\n" )
        for link in links:
            url = link["href"]
            if "favicon" in url: continue 

            fname = "/".join( url.split("/static/")[1:] )
            link["href"] = '{% static ' + f"'{fname}' " + r"%}"
            html.write( link.prettify() )


with open( "./../templates/react_scripts.html", "w" ) as html:
    html.write( r"{% load static %}" + "\n" )
    for script in scripts:
        if 'src' in script:
            url = script["src"]
            fname = "/".join( url.split("/static/")[1:] )
            script["src"] = '{% static ' + f"'{fname}' " + r"%}"

        html.write( script.prettify() )
    
package.json 

"scripts": {
  ...
  "build": "react-scripts build && python ./build_base.py",
  ...
},

Upvotes: 1

Emanuele Scarabattoli
Emanuele Scarabattoli

Reputation: 4489

Well, so what is needed in this case is a post build script that allows you to edit the file called base.html to have the right names and scripts produced by the build. I will describe here some conceptual steps on how to achieve this result.

Step n. 1: Change the package.json file to allow the execution of a script after the build

In this case I am using a JavaScript script file that will be run using nodejs, but you can use also other languages or shell scripts

"scripts": {
  ...
  "build": "react-scripts build && node ./postbuild.js",
  ...
},

Note that, since the && operator is used, the postbuild.js file will executed only if the build will have success.

Step n. 2: Create the post build script that will write your base.html file

With the script you will be able to write the base.html file dynamically.

Note: Since you can create the script in different languages, the script itself it is not included in the answer.

Upvotes: 1

Related Questions