open1024.fr

Libérez vos octets !

Outils pour utilisateurs

Outils du site


Panneau latéral

developpement:django:django_webpack_setup_project

2020-05-10: Création
2020-05-11: Ajout SASS
2020-05-12: Ajout foundation-sites CSS Framework
2020-05-15: ReactJS 'App.js & class App render'
2020-05-16: Ajout de Compass SASS Framework
2020-05-17: Nouvelle structure de dossiers
2020-05-17: Utilisation
2021-10-25: Séparation avec_virtualenv et avec_pipenv
2021-10-25: Notes sur la configuration du fichier settings.py

Django setup avec Webpack ReactJS et SASS

Cette procédure est écrite à l'occasion de la refonte du projet sport reporting.
En plus d'utiliser pipenv j'y ajoute webpack afin d'intégrer sass et foundation zurb depuis yarnpkg sous debian 10 bullseye (testing).

Cette méthode est de plusieurs jours de recherche et de test de différentes sources:

Donc à utiliser pour les petites applications, ne nécessitant pas un gros fichier JS (>200ko) sinon, il faut utiliser le code spliting https://webpack.js.org/plugins/split-chunks-plugin/

Dossier projet

# création du dossier projet:
cd ~/dev/
mkdir sport
cd sport

Git init

git init
touch .gitignore

Dans le fichier .gitignore mettre les exclusions du standard de github:

git add -A
git commit -m "build(init): Main app directory"

Environnement Python

Avec Virtualenv

Le dossier venv est créé par PyCharm Pro lors de la définition du projet.
Cette commande permet d'activer cet environnement virtuel:

$ source venv/bin/activate

On peut contrôler le contenu de l'environnement virtuel:

$ pip list
Package    Version
---------- -------
asgiref    3.4.1
Django     3.2.8
pip        21.3.1
pytz       2021.3
setuptools 57.0.0
sqlparse   0.4.2
wheel      0.36.2

Avec Pipenv

Cette commande permet de créer l'environnement virtuel et de l'activer:

pipenv shell
 
Creating a virtualenv for this project…
Pipfile: /home/jc/dev/sport/Pipfile
Using /usr/bin/python3 (3.8.2) to create virtualenv…
⠴ Creating virtual environment...

Installation de django et modules aux. depuis pipenv:

pipenv install django Pillow django-crispy-forms crispy-forms-foundation
ls -lh
-rw-r--r-- 1 jc jc  220 mai   10 11:46 Pipfile
-rw-r--r-- 1 jc jc 4,3K mai   10 11:47 Pipfile.lock

Git:

git add -A
git commit -m "build(init): Python virtual environnement."

Création du projet sport

django-admin startproject . config_prj

Organisation des dossiers du projet:

tree
.
├── config_prj
│   ├── asgi.py
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
├── Pipfile
└── Pipfile.lock

Git:

git add -A
git commit -m "build(django): Django init project."

Création de l'application reporting

python manage.py startapp reporting

Activation de l'app

reporting/settings.py
ALLOWED_HOSTS = ["127.0.0.1", "192.168.1.2", "192.168.1.3", "192.168.1.4"]
INSTALLED_APPS = [
    [...]
#    'users.apps.UsersConfig',           #user management
    'reporting.apps.ReportingConfig',   #main app
    'crispy_forms',                     #addon for load django form
]
 
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, 'templates'),
        ],
        [...]
 
LANGUAGE_CODE = 'fr-fr'
L'ajout de crispy_forms est optionnel. Il est utilisé pour le rendu des formulaires.
Si le projet est créé avec PyCharm Pro, le paramétrage du dossier templates est déjà fait. Il faut juste créer ce dossier à la racine du projet Django.

Structure des dossiers :

.
├── config_prj
│   ├── asgi.py
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   ├── views.py
│   └── wsgi.py
├── manage.py
├── Pipfile
├── Pipfile.lock
└── reporting
    ├── admin.py
    ├── apps.py
    ├── __init__.py
    ├── migrations
    │   └── __init__.py
    ├── models.py
    ├── tests.py
    └── views.py

Git:

git add -A
git commit -m "build(django): Django reporting app."

Paramétrage Django pour Webpack

Dans le dossier racine du projet config_prj, créer le dossier template:

mkdir templates

Dans le dossier de configuration du projet, créer le fichier views.py:

views.py
from django.shortcuts import render
 
def index(request):
    return render(request, "index.html")

Dans le dossier template créer le fichier index.html

template/index.html
<html>
    <head>
        <title>Demo</title>
    </head>
    <body>
        <h1>Hello World!</h1>
    </body>
</html>

Dans le fichier urls.py, ajouter la vue:

config_prj/urls.py
from django.contrib import admin
from django.urls import path
from . import views
 
urlpatterns = [
    path('', views.index),
    path('admin/', admin.site.urls),
]

Dans le fichier settings.py ajouter l'accès au dossier templates:

config_prj/settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
[...]

A ce stade on peut contrôler l'enchaînement des urls.py/views.py/index.html:

python manage.py runserver

Structure :

.
├── config_prj
│   ├── asgi.py
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   ├── views.py
│   └── wsgi.py
├── db.sqlite3
├── manage.py
├── Pipfile
├── Pipfile.lock
├── reporting
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
└── templates
    └── index.html

Git:

git add -A
git commit -m "build(django): Django hello world."

Ajout static CSS sans SASS

A la racine du projet, créer les dossiers static/css avec main.css

static/css/main.css
h1 {
    color: red;
}

Dans template/index.html ajout en en-tête load static et le lien stylesheet:

template/index.html
{% load static %}
<html>
    <head>
        <title>Demo</title>
        <link rel="stylesheet" href="{% static 'css/main.css' %}"/>
    </head>
    <body>
        <h1>Hello World!</h1>
    </body>
</html>

En début du fichier settings.py ajouter :

import os

En fin du fichier settings.py ajouter :

config_prj/settings.py
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]

Visu du rendu avec le css sans le transpileur SASS:
Git:

git add -A
git commit -m "build(django): CSS without SASS transpiler."

Webpack setup

Webpack est utiliser pour générer ou transpiler du code dans un autre format de code:
Exemple, le langage SASS vers CSS et JSX en JS.

Init

Ce placer dans le dossier static du projet :

yarnpkg init
 
yarn init v1.22.4
question name (django_static): 
question version (1.0.0): 
question description ("django with webpack integration in static folder"): django with webpack integration in static folder
question entry point (index.js): 
question repository url: 
question author (JC_onLine): 
question license (MIT): 
question private: 
success Saved package.json

Installation

L'option -D de yarnpkg add -D […] permet de séparer les modules nécessaires au développement.
yarnpkg add -D webpack webpack-cli @babel/core @babel/preset-env @babel/preset-react babel-loader style-loader css-loader sass sass-loader node-sass
[...]
info Direct dependencies
├─ @babel/core@7.9.6
├─ @babel/preset-env@7.9.6
├─ @babel/preset-react@7.9.4
├─ babel-loader@8.1.0
├─ css-loader@3.5.3
├─ node-sass@4.14.1
├─ sass-loader@8.0.2
├─ sass@1.26.5
├─ style-loader@1.2.1
├─ webpack-cli@3.3.11
└─ webpack@4.43.0
[...]

Toujours dans le dossier static du projet, créer le fichier webpack.config.js:

static/webpack.config.js
const path = require('path')
 
module.exports = {
    entry: {
        app: './src/index.js'
    },
    watch: true,
    devtool: 'source-map',
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: ['babel-loader']
            }
        ]
    },
    resolve: {
        extensions: [
            '.js'
        ]
    }
}

Dans le dossier static, créer le dossier src avec index.js dedans:

static/src/js/index.js
const x = "this is a test alert";
alert(x);

et ajouter le lien js à index.html

template/index.html
{% load static %}
<html>
    <head>
        <title>Demo</title>
        <link rel="stylesheet" href="{% static 'css/main.css' %}"/>
    </head>
    <body>
        <h1>Hello World!</h1>
        <script src="{% static 'dist/app.bundle.js' %}"></script>
    </body>
</html>

Paramétrage du fichier package.json (en dessous de “main”: “index.js”):

static/package.json
[...]
  "main": "index.js",
  "scripts": {
      "test": "echo \"Error: no test specified\" && exit 1",
      "webpack": "webpack"
  },
[...]

1er test de webpack

Il faut 2 terminaux:

  • Un terminal dans le dossier static
yarnpkg run webpack
 
webpack is watching the files…
 
Hash: ed230c0c3f7b8e8a30d0
Version: webpack 4.43.0
Time: 1795ms
Built at: 10/05/2020 18:24:17
            Asset       Size  Chunks                   Chunk Names
    app.bundle.js  998 bytes       0  [emitted]        app
app.bundle.js.map   4.62 KiB       0  [emitted] [dev]  app
Entrypoint app = app.bundle.js app.bundle.js.map
[0] ./src/index.js 43 bytes {0} [built]
Pour lancer webpack en mode développement :
yarnpkg run webpack - -mode development
Car par défaut c'est le mode production qui minimifie le code.

  • Il est possible d'ajouter - -mode development dans package.json objet scrips:.
  • 2ème solution, dans webpack.config.js ajouter la clé mode: 'development',.

J'utilise cette 2ème méthode car je préfère concentrer les modifications dans le fichier webpack.config.js.

Webpack a créé le dossier dist avec les fichier app.bundle.js et app.bundle.js.map dedans.

  • Un terminal à la racine du projet
python manage.py runserver

Visu du résultat:

Git:

git add -A
git commit -m "build(webpack): yarnpkg run webpack make js bundle for django."

ReactJS

Installation

yarnpkg add react react-dom
info Direct dependencies
├─ react-dom@16.13.1
└─ react@16.13.1

Mise à jour de webpack.config.js:

const path = require('path')
 
module.exports = {
    entry: {
        app: './src/index.js'
    },
    watch: true,
    devtool: 'source-map',
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {
                            presets: ['@babel/react']
                      }
                    }
                ],
            }
        ]
    },
    resolve: {
        extensions: [
            '.js'
        ]
    }
}

Utilisation

Modifions le fichier src/index.js (suppression du test alert):

static/src/js/index.js
import React from 'react';
import ReactDOM from 'react-dom';
 
ReactDOM.render(
    <div>
        <h1>This is JSX !!</h1>
    </div>,
    document.getElementById("root")
)

Modifions le fichier index.html pour accueilir le JSX:

template/index.html
{% load static %}
<html>
    <head>
        <title>Demo</title>
        <link rel="stylesheet" href="{% static 'css/main.css' %}"/>
    </head>
    <body>
        <div id="root"></div>
        <script src="{% static 'dist/app.bundle.js' %}"></script>
    </body>
</html>

Visu:

Git:

git add -A
git commit -m "build(react): yarnpkg run webpack make react js bundle for jango."
Pour séparer le JS du SASS,
on déplace src/index.js vers src/js/index.js

Mise à jour de webpack.config.js

static/webpack.config.js
    entry: {
        app: './src/js/index.js'
    },

Git:

git add -A
git commit -m "build(webpack): Move src/index.js to src/js/index.js."
git commit -m "build(webpack): Fix webpack.config.js with src/js/index.js"

Installation de SASS

2020-05-11
Les modules SASS ont été installés au début de cette procédure. Pour rappel :

yarnpkg add -D style-loader css-loader sass sass-loader node-sass

Fichier CSS avec mini-css-extract-plugin

yarnpkg add -D mini-css-extract-plugin
└─ mini-css-extract-plugin@0.9.0

Dans le fichier webpack.config.js ajouter la constante MiniCssExtractPlugin: et remplacer 'style-loader':

static/webpack.config.js
const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
 
module.exports = {
    entry: {
        app: './src/js/index.js'
    },
    mode: 'development',
    watch: true,
    devtool: 'source-map',
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {
                            presets: ['@babel/react']
                      }
                    }
                ],
            },
            {
                test: /\.(scss)$/,
                exclude: /node_modules/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'sass-loader',
                ],
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '../css/index.css',
        })
    ],
    resolve: {
        extensions: [
            '.js'
        ]
    }
}

Créer le dossier src/scss avec index.scss dedans.

src/scss/index.scss
$h1-color: #3465A4;
h1 {
    color: $h1-color;
}

Dans le fichier index.js ajouter l'importation de index.scss:

static/src/js/index.js
import '../scss/index.scss';
import React from 'react';
import ReactDOM from 'react-dom';
 
ReactDOM.render(
    <div>
        <h1>This is JSX with SASS style file!!</h1>
    </div>,
    document.getElementById("root")
)

Dans le fichier index.html mettre à jour l'appel du static/css/index.css

template/index.html
{% load static %}
<html>
    <head>
        <title>Demo</title>
        <link rel="stylesheet" href="{% static 'css/index.css' %}"/>
    </head>
    <body>
        <div id="root"></div>
        <script src="{% static 'dist/app.bundle.js' %}"></script>
    </body>
</html>

Visu:

Git:

git add -A
git commit -m "build(webpack): Add SASS transpiler. Tested in Django with JSX and SASS."
git commit -m "test(react): This is ReactJS JSX with SASS style file powered by Django!!"

Foundation Zurb

2020-05-12
Dans le dossier static :

yarnpkg add foundation-sites
info Direct dependencies
└─ foundation-sites@6.6.3

Ajouter les @import et @include dans le fichier Sass:

static/src/scss/index.scss
@import 'foundation-sites/scss/foundation';
@include foundation-global-styles;
@include foundation-typography;
 
.content {
    margin-top: 10px;
    margin-left: 20px;
}
$h1-color: #3465A4;
.title {
    color: $h1-color;
}
.mybutton {
    @include button();
    margin-left: 50px;
    // exemple avec customisation des couleurs:
    // @include button(false, #3B3933, #2A2926, #EDEDED, "solid");
}

Détail du fichier JSX correspondant:

import '../scss/index.scss';
import React from 'react';
import ReactDOM from 'react-dom';
 
ReactDOM.render(
    <div className="content">
        <h3 className="title">
            This is ReactJS JSX with SASS file and Foundation Zurb style, </h3>
        <h2>powered by Django!!</h2>
        <a href="about.html" className="mybutton">Learn More</a>
 
    </div>,
    document.getElementById("root")
)

Rendu:

git:

git add -A
git commit -m "build(foundation): Add Foundation Zurb CSS framework. Test typo & button better presentation."

ReactJS & App.js class render component

2020-05-15
Affin d'être conforme à la méthode donnée dans l'outil officiel creat-react-app voici la mise en place:

App.js

C'éer le fichier App.js :

La majuscule de App.js est importante pour définir un composant React.
static/src/js/App.js
import React, { Component }  from 'react';
import ReactDOM from 'react-dom';
import '../scss/index.scss';
 
class App extends Component {
    render() {
        return (
            <div className="content">
                <h3 className="title">
                    This is ReactJS 'App.js & class App render' JSX with SASS file and Foundation Zurb style, </h3>
                <h2>powered by Django!!</h2>
                <a href="about.html" className="mybutton">Learn More</a>
            </div>
        );
    }
}
export default App;

index.js

Mettre à jour index.js permettant l'appel du component App.js:

import React from 'react';
import ReactDOM from 'react-dom';
import '../scss/index.scss';
import App from './App';
 
ReactDOM.render(<App />, document.getElementById("root"));

index.thml

La page HTML reste inchangée, voici le code pour rappel:

template/index.html
{% load static %}
<html>
    <head>
        <title>Demo</title>
        <link rel="stylesheet" href="{% static 'css/index.css' %}"/>
    </head>
    <body>
        <div id="root"></div>
        <script src="{% static 'dist/app.bundle.js' %}"></script>
    </body>
</html>

Rendu

C'est transparent pour l'utilisateur, seul le texte est changer pour montrer que ça fonctionne:

Git:

git add -A
git commit -m "build(reactJS): Use App.js with App render, main component."

Compass + mixin

J'utilise les librairies de Compass pour mettre en relief certains composant comme les cadres, boutons et ligne de tableau le survol d'éléments, en utilisant les dégradés.

yarnpkg add compass compass-mixins
info Direct dependencies
├─ compass-mixins@0.12.10
└─ compass@0.1.1

Exemple d'utilisation:

static/src/scss/index.scss
// #### Foundation Zurg ####
@import 'foundation-sites/scss/foundation';
@include foundation-global-styles;
@include foundation-typography;
// #### Compass framework lib ####
@import "../../node_modules/compass-mixins/lib/compass/css3";
 
// colors definition:
$body-color: #2F2F2F;
$title-color: #f0f0f0;
$power-color: #ff8000;
 
body {
    background: $body-color;
}
.content {
    background: #3B3B3B;
    margin-top: 10px;
    margin-left: 20px;
    margin-right: 20px;
    padding: 10px;
    @include box-shadow(
        rgba(255, 255, 255, 0.1) 0 1px 0,
        rgba(0, 0, 0, 0.8) 0 2px 5px 2px);
}
.sub-content {
    background: #262626;
    padding: 10px;
    margin-bottom: 5px;
    border-color: #1E1E1E;
    border-radius: 4px;
    border-style: solid;
    border-width: 1px;
    @include box-shadow(
        rgba(255, 255, 255, 0.1) 0 1px 0,
        rgba(0, 0, 0, 0.9) 0 1px 7px 1px inset);
}
.title {
    color: $title-color;
}
.django-power {
    color: $power-color;
}
.mybutton {
    // @include button(false, #3B3933, #2A2926, #EDEDED, "solid");
    @include button();
    margin-left: 50px;
    @include box-shadow($body-color 2px 2px 15px);
    @include box-shadow(
        rgba(255, 255, 255, 0.1) 0 1px 0,
        rgba(0, 0, 0, 0.8) 0 1px 5px 1px inset);
}
ul {
    color: #C6C6C6;
}
code {
    font-family: menlo, monaco, "andale mono", "courier new", fixed;
    border-radius: 4px;
    background-color: #212121;
    @include box-shadow(
        rgba(255, 255, 255, 0.1) 0 1px 0,
        rgba(0, 0, 0, 0.9) 0 1px 7px 1px inset);
    color: #DADBB1;
    border-color: black;
}
static/src/js/apps.js
import React, { Component }  from 'react';
import ReactDOM from 'react-dom';
import '../scss/index.scss';
 
class App extends Component {
    render() {
        return (
            <div className="content">
                <h3 className="title">Technical stuff inside this page:</h3>
                <div className="sub-content">
                    <ul>
                        <li>Babel JSX bundler</li>
                        <li>ReactJS 'App.js & class App render'</li>
                        <li>SASS bundler file</li>
                        <li>Foundation Zurb Framework style, ex: typo, button</li>
                        <li>Compass SASS Framework, ex:<code>box-shadow</code>
                            effects (frame & button)</li>
                    </ul>
                </div>
                <h2 className="django-power">Django powered!</h2>
                <a href="about.html" className="mybutton">Learn More</a>
            </div>
        );
    }
}
export default App;

Rendu:

Git:

git add -A
git commit -m "build(compass): Add compass 
libraries & mixins."
git add -A
git commit -m "fix(render): Fix page typo."
git add -A
git commit -m "fix(render): Fix page typo again."

Webpack en dehors du dossier static

2020-05-17
Pour optimiser le traitement de collectstatic de Django, il est préférable de sortie webpack et surtout le dossier node_modules du dossier static.

Nouvelle structure de dossiers

  • A la racine du projet, créer un dossier frontend:
  • Déplacer les fichiers et dossiers suivants dans ce dossier frontend:
    • dist
    • node_modules
    • package.json
    • src
    • webpack.config.js
    • yarn-error.log
    • yarn.lock
  • Dans src créer le dossier js et y déplacer app.bundle.js
  • Renommer le dossier dist en dist_not_used qui ne sera plus utilisé, car le JS va être directement bundle dans le dossier static. dist devient supprimable.

JavaScript

Modifier la sortie de webpack.config.js: output: {…} est actualiser le chemin '../static/js':

frontend/webpack.config.js
const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
 
module.exports = {
    entry: {
        app: './src/js/index.js'
    },
    mode: 'development',
    watch: true,
    devtool: 'source-map',
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, '../static/js')
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {
                            presets: ['@babel/react']
                      }
                    }
                ],
            },
            {
                test: /\.(scss)$/,
                exclude: /node_modules/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'sass-loader',
                ],
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '../css/index.css',
        })
    ],
    resolve: {
        extensions: [
            '.js'
        ]
    }
}

html

Mettre à jour index.html avec

<script src="{% static 'js/app.bundle.js' %}"></script>
template/index.html
{% load static %}
<html>
    <head>
        <title>Demo</title>
        <link rel="stylesheet" href="{% static 'css/index.css' %}"/>
    </head>
    <body>
        <div id="root"></div>
        <script src="{% static 'js/app.bundle.js' %}"></script>
    </body>
</html>

Récap dossiers

tree -L 2                                                 
.
├── config_prj
│   ├── asgi.py
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   ├── views.py
│   └── wsgi.py
├── db.sqlite3
├── frontend
│   ├── dist_not_used
│   ├── node_modules
│   ├── package.json
│   ├── src
│   ├── webpack.config.js
│   ├── yarn-error.log
│   └── yarn.lock
├── manage.py
├── Pipfile
├── Pipfile.lock
├── reporting
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── static
│   ├── css
│   └── js
└── templates
    └── index.html

Git:

git add -A
git commit -m "build(frontend): New static structure without Webpack inside."

Utilisation

  • Depuis le dossier racine: du projet
python manage.py runserver
  • Depuis le dossier frontend
yarnpkg run webpack
developpement/django/django_webpack_setup_project.txt · Dernière modification: 2021/10/27 10:59 de jc_online