質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

ウェブアプリケーション

ウェブアプリケーションとは、ウェブ上でアクセスされるアプリケーションのことを呼びます。この場合におけるウェブとは、インターネットやイントラネット上を意味します。

ウェブブラウザ

ウェブブラウザ(インターネットブラウザ)とは、www上に公開されている情報リソースをユーザーに視覚的提供・操作させる機能を持ったソフトウェアプログラムです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

意見交換

クローズ

4回答

740閲覧

django "{% for %}" VS "for ()" 表示速度上の疑問

akiyama3284pga

総合スコア186

Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

ウェブアプリケーション

ウェブアプリケーションとは、ウェブ上でアクセスされるアプリケーションのことを呼びます。この場合におけるウェブとは、インターネットやイントラネット上を意味します。

ウェブブラウザ

ウェブブラウザ(インターネットブラウザ)とは、www上に公開されている情報リソースをユーザーに視覚的提供・操作させる機能を持ったソフトウェアプログラムです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2023/06/06 05:25

0

0

テーマ、知りたいこと

djnago のタグはサーバ側で処理され、通常のHTMLに変換され、利用者のウェブブラウザに通常のレスポンスが送出されるかと思います。
そこで疑問があります。

例えばかなり重いクエリセットオブジェクトを渡す場合、HTML内で回す場合には
当然{% for %}を使わなくてはいけませんが、
JS内で処理する場合、{% for %}を使うのか、for(;;)を使うのかの二択になるかと思います。

前者

{% for obj in queryset %} console.log("{{ obj.id }}") {% endfor %}

後者

for (var i = 0; i < "{{ queryset.length }}"; i++){ console.log("{{ queryset.forloop.counter0.id }}") }

上2つでは、速度上の違いは発生するものでしょうか?
結局後者でも各ループにおいてサーバ側で処理しなくてはいけないコンテキストが出現するため、
レスポンスがウェブブラウザに渡るまでの速度に違いは出ないと考えております。

現在システムが肥大化しており、少しでも速度に関連するもの、またはとりあえずレスポンスが少しでも早く渡ることができれば、と考えています。

やはりとりあえずの速度を上げるにはデータの受け渡しの部分を非同期にするしかないでしょうか...

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

回答4

#1

maisumakun

総合スコア145183

投稿2023/06/06 14:32

編集2023/06/07 00:10

上2つでは、速度上の違いは発生するものでしょうか?

速度問題に関しては、いちばん確実な鉄則があります。

推測より計測です。

実際に表示を行って、各種検証ツールで動作を確認しましょう。


速度とは別件の問題として、後者ではループをJavaScriptサイドで行う以上、{{ queryset.forloop.counter0.id }}の展開は1度しか行われず、同じ値を何度もconsole.logし続けることになるかと思うのですが、それは意図通りの動作でしょうか?

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

#2

akiyama3284pga

総合スコア186

投稿2023/06/07 01:35

#1
お世話になっております。
書き方がお粗末で申し訳ありません。ニュアンスだけ伝わればとあいまいな書き方になってしまいましたが、
おっしゃる通りこのまま実行する場合は同じ値が出力されることになります。
ただ、速度の検証観点からは問題無いかと思いました。

以下のようなテストを行ってみましたところ、はっきりと判明いたしました。

view

context['aaa_range'] = range(10000000000) # {% for %} いてレート用 context['aaa'] = 10000000000 # 通常 for (;;) 用

template

$(function){ var aa = 0 // 1 var co = 10000000000 for (var i = 0; i < co; i++){ aa += 1 } // 2 for (var i = 0; i < "{{ aaa }}"; i++){ aa += 1 } // 3 {% for obj in aaa_range %} aa += 1 {% endfor %} }

1と2は通常通り、レスポンスがブラウザに渡ってから、ブラウザのjsで処理されるため、htmlが表示表示されてから、長い待ち時間となります。

3は、リクエストを投げてレスポンスを受けるまでに長い待ち時間がかかります。つまり遷移以前に長い待ち時間

大事なことを忘れておりましたが、検証以前にコードを見ていれば、なんとなく想像できていたように思います。
1や2は、
for (var i = 0; i < "1000000"; i++){
console.log("aaa")
}

のようになりますが、

3は、
console.log("aaa");
console.log("aaa");
console.log("aaa");
console.log("aaa");


のように展開されたものが(つまりサーバ側でループしている。)来ています。
これはつまりサーバ側に負荷を担わせていることになると思います...

しかし、よく考えてみますと、まとまった行データを渡す処理にはどこかでループが必要になるので、
これは避けて通れないようにも思いました。

これはdjangoが悪いというより、むしろどんなフレームワークだろうと、使わなかろうと、必要なステップであり、
これを端折るにはクエリの最適化や非同期等の別の観点からみていく必要があるように思いました。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

#3

katsuko

総合スコア3469

投稿2023/06/07 14:28

{% for obj in queryset %} console.log("{{ obj.id }}") {% endfor %}

テンプレートは便利な反面、汎用的な処理なので無駄も多いです。
もっとも数回程度では気にすることはありませんが、数十万回も処理するのであれば、それなりに影響は出ます。

djangoは知らないので、flaskで試してみます。
(djangoのテンプレートエンジンがflaskと同じjinja2かどうかはわかりませんが、考え方は一緒だと思います)

html

1<html> 2<body> 3<script> 4function hoge { 5 {% for i in range(count) %} 6 console.log({{ i }}); 7 {% endfor %} 8} 9</script> 10</body> 11</html>

python

1@app.route('/1/<int:count>') 2def test1(count): 3 s = time.time() 4 res = render_template('test1.html', count=count) 5 print(f'test1: {time.time() - s}s.') 6 return res

というように処理した場合、printで出力した結果は、

test1: 0.07741498947143555s.

となります。

これを、以下のように変えてみます。

html

1<html> 2<body> 3<script> 4function hoge { 5 const data = {{ data }} 6 for (let i = 0; i < data.length; ++i) { 7 console.log(i); 8 } 9} 10</script> 11</body> 12</html>

python

1@app.route('/2/<int:count>') 2def test2(count): 3 s = time.time() 4 data = list(range(count)) 5 res = render_template('test2.html', data=data) 6 print(f'test2: {time.time() - s}s.') 7 return res

これを実行すると、

test2: 0.008939266204833984s.

というようになります。
まぁこれは、repr(list(data))とJavaScriptの配列の初期化が同じ構文で書けるから使えるわけでして、ちょっと冗長に

html

1<html> 2<body> 3<script> 4function hoge { 5} 6</script> 7 {{ script }} 8</body> 9</html>

python

1@app.route('/3/<int:count>') 2def test3(count): 3 s = time.time() 4 script = '' 5 for i in range(count): 6 script += 'console.log(' + str(i) + ');\n' 7 res = render_template('test3.html', script=script) 8 print(f'test3: {time.time() - s}s.') 9 return res

というように、処理がかかる箇所だけテンプレートを使わずにテキスト化するだけでも、

test3: 0.025707244873046875s.

と、それなりに効果があります。


まぁただ、ご自身でも少し書かれているようですが、こんな小手先のテクニックも大事ですが、処理するデータ量を減らす方向で考えたほうがもっと効果的だと思います。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

#4

akiyama3284pga

総合スコア186

投稿2023/06/08 00:15

#3
フラスコ環境での実テスト、そして情報のご提供ありがとうございました。
とても参考になるとともに、仰います通り、木を見て森を見ずなものに近いように思いました。
本当にマイクロ秒単位での高速化が求められる最終局面ではないため、まずはもっと大きく削ることができることに注力したいと思います。
それにテンプレートタグは便利でかなりコードが見やすくなるというメリットもありますので...状況を天秤にかけつつ最適化を行って行きたいと思います。           

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

最新の回答から1ヶ月経過したため この意見交換はクローズされました

意見をやりとりしたい話題がある場合は質問してみましょう!

質問する

関連した質問