├── .gitignore
├── templates
└── index.html
├── webpack.config.js
├── app.py
├── src
└── my.js
├── package.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | env/*
2 | node_modules/*
3 | static/bundle.js
4 | *.pyc
5 |
--------------------------------------------------------------------------------
/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Jo
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | entry: './src/my.js',
5 | output: {
6 | filename: 'bundle.js',
7 | path: path.resolve(__dirname, 'static')
8 | }
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, render_template
2 |
3 | app = Flask(__name__)
4 |
5 | @app.route('/')
6 | def main():
7 | return render_template('index.html')
8 |
9 |
10 | if __name__=="__main__":
11 | app.run(debug=True)
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/my.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | function component() {
4 | var element = document.createElement('div')
5 | element.innerHTML = _.join(['Hello','world','!!'], ' ');
6 |
7 | return element;
8 | }
9 |
10 | document.body.appendChild(component());
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cleaner",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "build": "webpack",
9 | "watch": "webpack --watch"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "devDependencies": {
15 | "webpack": "^3.6.0"
16 | },
17 | "dependencies": {
18 | "lodash": "^4.17.4"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Flask webpack Tutorial
2 |
3 | How to easily develop flask-based python web applications that use (external) javascript
4 | libraries? Adding direct links to your templates is a natural option but in the
5 | long term, the maintenance is pretty overwhelming. Secondly, as in my case the
6 | application might not work properly if deployed without internet or behind
7 | a strict firewall. Lastly, javascript underwent a lots of changes in the recent
8 | years and using the newest language features across all browsers, require
9 | more complex toolchains (e.g. using of babel).
10 |
11 | In this tutorial I will show a solution to that problem which uses `npm` (node package
12 | manager) and `webpack` tool. The tutorial should give you basic understanding of the
13 | tools rather than full deep dive into the aforementioned technologies.
14 |
15 | Prerequisites:
16 | * understanding of `Flask`, `Python`, and `javascript`
17 | * availability of `npm`
18 |
19 | ## Project structure
20 | Let us start with a simple directory structure for our application. We will create
21 | separate directories for templates and static content as required by Flask. We also
22 | add a directory to write our javascript code.
23 | ```
24 | mkdir -p flaskpack/templates
25 | mkdir -p flaskpack/static
26 | mkdir -p flaskpack/src
27 | ```
28 |
29 | ## Simple Flask Application
30 | I suggest to use virtual environment for programming with flask but this is not
31 | strictly required. Anyways you can just cd in to the project directory and create
32 | application code:
33 | ```
34 | cd flaskpack
35 | vi app.py
36 | ```
37 |
38 | ```python
39 | from flask import Flask, render_template
40 |
41 | app = Flask(__name__)
42 |
43 | @app.route('/')
44 | def index():
45 | return render_template('index.html')
46 |
47 | if __name__ == "__main__":
48 | app.run()
49 | ```
50 |
51 | As you see the application has just one entry point i.e. it reacts only on requests
52 | on root url. In this case a template (`index.html`) is rendered and sent back to the user.
53 | Now we have to create the template. Although it is possible to define the location of
54 | the template directory we use the default flask option which seeks for templates in
55 | `/template/` directory.
56 |
57 | ```bash
58 | vim /template/index.html
59 | ```
60 |
61 | ```html
62 |
63 |
64 | Simple web app
65 |
66 |
67 |
68 |
69 | ```
70 |
71 | There is not much happening here now. We have a simple web page with a heading.
72 | We also include a javascript file `bundle.js` from static directory. This file should
73 | incorporate additional logic of our application that we would like to outsource
74 | to the client. The problem is that the file does not exist yet. You can start the
75 | Flask application and it should work and serve all the static (i.e. not javascript-based)
76 | to the user:
77 | ```
78 | python app.py
79 | ```
80 |
81 | and use browser to go to `localhost:5000` to see the result.
82 |
83 | ## Javascript part
84 | Now we would like to extend our simple application with some javascript. To this
85 | end we have to create and edit `src/my.js` file. Please note that we don't create
86 | `bundle.js` now. This file will be created automatically later. Our javascript
87 | will look like that.
88 |
89 | ```
90 | vim /src/my.js
91 | ```
92 |
93 | ```javascript
94 | import _ from 'lodash';
95 |
96 | function component() {
97 | var element = document.createElement('div')
98 | element.innerHTML = _.join(['Hello','world','!!'], ' ');
99 |
100 | return element;
101 | }
102 |
103 | document.body.appendChild(component());
104 | ```
105 |
106 | We use an external library (`lodash`) to dynamically generate additional content
107 | of our page.
108 |
109 | Let us assume that our coding ends here, and we would like to test and ship the
110 | functionality. Two remaining problems are how to get the dependencies (i.e. `lodash`)
111 | and generate `bundle.js` with our code required by the previously created template.
112 |
113 | ## Npm package manager
114 |
115 | For managing the dependencies we will use `npm` (node package manager). Firstly, we
116 | need to initialize the tool.
117 |
118 | ```shell
119 | npm init -y
120 | ```
121 |
122 | This will create a manifest file `package.json` and also a directory `node_modules`
123 | where external libraries will be installed. At this point you can review the
124 | `package.json` file and edit things like project name, version, or description.
125 |
126 | The package manager can also install javascript libraries in the directory. We
127 | will require `lodash` library. To install it we have to issue following command:
128 |
129 | ```shell
130 | npm install lodash --save
131 | ```
132 |
133 | The `--save` argument is required to add this dependency to the `package.json` file.
134 | You can check it now to see that a new line was added:
135 |
136 | ```javascript
137 | "dependencies": {
138 | "lodash": "^4.17.4"
139 | }
140 | ```
141 |
142 | Although it would be possible to make the node_modules accessible to flask and thus
143 | visible from its the templates, this is probably not the best solution. Instead
144 | we will use `webpack` tool to manage the resources.
145 |
146 | ## Webpack
147 |
148 | First we have to install `webpack`. It is important to notice that `webpack` is not
149 | really required for our application to work (unlike `lodash`). The `npm` differentiate
150 | between dependencies and devDependencies. The later are used to manage tools
151 | required for development and deployment of the application but not during the
152 | runtime of it. To install `webpack` use `npm` like:
153 |
154 | ```shell
155 | npm install --save-dev webpack
156 | ```
157 |
158 | Now you can run the `webpack` to finally create `bundle.js` file required by our
159 | application, one way of doing this is to call `webpack` directly:
160 |
161 | ```shell
162 | ./node_modules/.bin/webpack src/my.js static/bundle.js
163 | ```
164 |
165 | After this step a new file `bundle.js` should appear in the static directory of
166 | your application and after starting Flask app again you should be able to see a webpage
167 | with dynamically generated content.
168 |
169 | A much more elegant way of working with `webpack` is to create a file called `webpack.config.js`
170 | which will describe how the `bundle.js` is created. Let us do it now:
171 |
172 | ```shell
173 | vi webpack.config.js
174 | ```
175 |
176 | ```javascript
177 | const path = require('path');
178 |
179 | module.exports = {
180 | entry: './src/my.js',
181 | output: {
182 | filename: 'bundle.js',
183 | path: path.resolve(__dirname, 'static')
184 | }
185 | };
186 | ```
187 |
188 | We can also extend `packages.json` to make the work with `webpack` easier. The `npm`
189 | offers the possibility to define scripts, you have to add following line to your
190 | project file:
191 | ```shell
192 | vi packages.json
193 | ```
194 |
195 | ```javascript
196 | "scripts": {
197 | "build": "webpack"
198 | },
199 | ```
200 |
201 | Now we can generate new version of `bundle.js` by just issuing:
202 |
203 | ```shell
204 | npm run build
205 | ```
206 |
207 | For the development it is useful to use `webpack` function called watch. This
208 | creates a process which reacts to changes in the javascript files and automatically
209 | regenerates `bundle.js`. To use this feature add one more script to the `package.json` file
210 | ```shell
211 | vim packages.json
212 | ```
213 |
214 | ```javascript
215 | "scripts": {
216 | "build": "webpack",
217 | "watch": "webpack --watch"
218 | },
219 | ```
220 |
221 | you can start it now by:
222 | ```shell
223 | npm run watch
224 | ```
225 |
226 | ## Links
227 |
228 |
229 |
--------------------------------------------------------------------------------