Djangoでモダンなコーディング環境を作りたい
最近、
Djangoを書く案件があり、pythonも楽しいしview, model等の操作性がいいので毎日楽しかったのですが、フロント開発の際にいつも通りpugやらscssやらモダンな言語使ってガンガン書こうとしたら壁に当たりました。てかdjango-htmlってpug使えるのか?そもそもgulpどうやって導入するんだ?
え?素のhtmlとcssでコーディングするの...?
その日は絶望して寝ました。
翌日、
gulpをなんとかDjangoに導入してscssをコンパイルできるようになりました。
しかし、静的サイトでは問題なく動くgulpのbrowser-sync
が、Djangoでは動きませんでした。
Djangoのプロジェクト起動コマンドpython3 manage.py runserver
を打ち、gulpを監視状態にしてscssを書き、保存したらブラウザを手動リロード。
これのどこがモダンやねん
その日は絶望して寝ました。
翌日、
いろいろあり悟りを開きました。その結果、ターミナルで
gulp
と打つと
python3 manage.py runserver
が実行されてDjangoプロジェクトが起動する。
さらに、gulpが動いてbrowser-syncでlocalhost
にプロジェクトを表示。
同時に、scssを監視し始める。scssに変更があれば直ちにコンパイルして localhost
をリロード。
するようになりました。
ちなみにpug導入は成功しませんでした。うまくいったら続編を書きます。
正直なところ、かなり荒削りに書いているので詳細な仕様などわからないことが多いですが、私と同じ思いをしている人間が数多いることを信じて、完成したgulpfile.jsをここに遺します。
この記事を理解するための前提条件目安
Django触ったことある
npm触ったことある
gulp触ったことある
くらいです。
環境
MacBookPro15,4
Quad-Core Intel Core i7
Python 3.8.9
pip 22.1.2
Django 2.2.27
ディレクトリ構成
+
がフォルダを表しています。
+ mysite/
+ app/
+ static/
+ app/
+ css/
+ style.css
+ template/
+ app/
+ index.html
+ mysite/
+ settings.pyなど
manage.py
上記は、Djangoのプロジェクト名をmysite
、アプリ名をapp
とした場合の構成です。
Djangoにてstatic
とtemplate
の設定が済んでいる前提です。
この記事を読んでいる人はほとんど済んでいるはずです...よね?
ちんぷんかんぷんという人はこの記事を読めば理解できるかも
npm導入
プロジェクトルートディレクトリ(mysite/)にてnpmを導入します
また、必要モジュールをインストールします。
npm init y
npm i -D browser-sync gulp gulp-plumber gulp-sass sass
以下のようにpackage.jsonに追記されたと思います。
"devDependencies": {
"browser-sync": "^2.27.10",
"gulp": "^4.0.2",
"gulp-plumber": "^1.2.1",
"gulp-sass": "^5.1.0",
"sass": "^1.53.0",
},
次に、
mysite/app/src/app/css/style.scss
となるようにフォルダを作成して空のscssファイルを用意します。
また、プロジェクトルートディレクトリにgulpfile.js
を追加します。空で大丈夫です。
+ mysite/
+ app/
+ src/
+ app/
+ scss/
+ style.scss // 追加した
+ static/
+ app/
+ css/
+ style.css
+ template/
+ app/
+ index.html
+ mysite/
+ settings.pyなど
+ node_modules/
.gulpfile.js // 追加した
manage.py
package-locl.json // 増えた
package.json // 増えた
gulpfile.js書いていく
先ほどプロジェクトルートディレクトリに追加したgulpfile.js
を書いていきます。
gulp4に対応したモダンな書き方になっているので見慣れたgulp記法と違う箇所があるかもしれません。
まず、必要モジュールを読み込みます。
const { src, dest, watch, parallel, series } = require('gulp')
const sass = require('gulp-sass')(require('sass'))
const plumber = require('gulp-plumber')
const browserSync = require('browser-sync').create()
const child = require('child_process')
続いて、django起動とホットリロードを有効にする関数を定義します。
この部分についてはほとんどこの記事を参考にしました。マジ感謝。
軽く解説すると、先ほど読み込んだchild_process
から、spawn
を使用してdjangoのrunserverを実現しています。spawn
についてはこの記事が参考になります。少し古いですが。
また、browserSync
にてproxy
、port
オプションを使ってDjangoと紐づけています。ここに関しては正直よくわかっていません。たくさん試したらこれで成功したという程度です。むしろ正しい方法教えてください。
ついでに、reload関数も定義します。こちらはbrowserSync
をリロードするだけの機能を持ちます。
のちほどwatch関数で呼び出すことで力を発揮します。
// runserver
var server = null
const runserver = (cb) => {
if (server) {
server.kill()
console.log("kill task done!")
}
server = child.spawn('python3', ['manage.py', 'runserver'], {
stdio: 'inherit'
})
server.on('close', (code) => {
if (code !== 0) {
cb(code)
console.error('Django runserver exited with error code: ' + code)
} else {
cb()
console.log('Django runserver exited normally.')
}
})
}
// browser
const browser = (cb) => {
browserSync.init({
ui: false,
notify: false,
proxy: {
target: "http://127.0.0.1:8000",
},
port: 8000
})
}
// reload
const reload = (cb) => {
browserSync.reload()
cb()
}
python manage.py runserver
でdjango起動する人はserver = child.spawn('python' ~
になります。
筆者はpython3 manage.py runserver
でdjango起動するのでserver = child.spawn('python3' ~
です。
browser-syncのオプションであるtarget
やport
も環境によって変わってくるかと思います。
さらに、scssをコンパイルする関数を定義します。
plumper
はエラーが出た時に後述のwatch関数を止めないためにあります。
また、browserSync.stream()
を最後に渡すことでファイル保存時、browser-syncに変更差分のみ綺麗に伝えてくれます。
const compileScss = () => {
return src('./app/src/app/scss/**/*.scss')
.pipe(plumber())
.pipe(sass())
.pipe(dest('./app/static/app/css/'))
.pipe(browserSync.stream())
}
watch関数を定義します。
watch関数にてscssを監視し、先ほど定義したコンパイル関数とリロード関数を渡します。
こうすると、監視対象に変更があった際に指定した関数を実行してくれます。
const watchSass = () => {
watch('./app/src/app/scss/**/*.scss', series(compileScss, reload))
}
最後に、関数を外部から呼び出せるようにして一つにまとめます。
gulp3から4への変更ではここが一番大きな変更かもしれませんね。
exports.default
とすることでgulpのデフォルトコマンドに任意の関数を指定できます。
また、parallel
は関数を並列に処理するためのものです。
exports.runserver = runserver
exports.browser = browser
exports.watchSass = watchSass
exports.default = parallel(
runserver,
browser,
watchSass,
)
実行
長かったですね。すいません。
ターミナルにてプロジェクトルートディレクトリに移動し、gulpを実行します。
gulp
しばらく待つと、localhost
にてプロジェクトが表示されるはずです。
その状態で、mysite/app/src/app/css/style.scss
を編集して保存すれば、コンパイルされてmysite/statoc/app/css/
に書き出されると思います。また、画面もリロードされるはずです。
完全版 gulpfile.js
これまで解説したgulpfile.jsをまとめたものを掲載します。
gulpfile.js
const { src, dest, watch, parallel, series } = require('gulp')
const sass = require('gulp-sass')(require('sass'))
const plumber = require('gulp-plumber')
const browserSync = require('browser-sync').create()
const child = require('child_process')
/* -------------------------------------------
RUNSERVER, BROWSER
------------------------------------------- */
// runserver
var server = null
const runserver = (cb) => {
if (server) {
server.kill()
console.log("kill task done!")
}
server = child.spawn('python3', ['manage.py', 'runserver'], {
stdio: 'inherit'
})
server.on('close', (code) => {
if (code !== 0) {
cb(code)
console.error('Django runserver exited with error code: ' + code)
} else {
cb()
console.log('Django runserver exited normally.')
}
})
}
// browser
const browser = (cb) => {
browserSync.init({
ui: false,
notify: false,
proxy: {
target: "http://127.0.0.1:8000",
},
port: 8000
})
}
// reload
const reload = (cb) => {
browserSync.reload()
cb()
}
/* -------------------------------------------
SCSS
------------------------------------------- */
const compileScss = () => {
return src('./app/src/app/scss/**/*.scss')
.pipe(plumber())
.pipe(sass({outputStyle: 'expanded'}))
.pipe(dest('./app/static/app/css/'))
.pipe(browserSync.stream())
}
/* -------------------------------------------
WATCH
------------------------------------------- */
const watchSass = () => {
watch(paths.scss + '**/*.scss', series(compileScss, reload))
}
/* -------------------------------------------
EXPORTS
------------------------------------------- */
exports.runserver = runserver
exports.browser = browser
exports.watchSass = watchSass
exports.default = parallel(
runserver,
browser,
watchSass,
)
所感
自分の環境だと、実行後にしばらくエラーが出てから綺麗に表示されるまでのラグがあります。
そんな不安定なコードを紹介すんなよという感じですが、これを構築するだけで2日かかってますので、自分と同じ思いをしている人のためにもとりあえず書きました。もっといい方法あるよという方がいたら是非教えていただきたいです。
また、今回はscssをコンパイルするコードのみですが、django-htmlもモダンに記述できる方法がないか模索中です。ちなみに、pugを導入しようとして一度挫折しています。こちらについてもいい情報ありましたら是非教えてください。
以上です。