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

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

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

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

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

Ajax

Ajaxとは、Webブラウザ内で搭載されているJavaScriptのHTTP通信機能を使って非同期通信を利用し、インターフェイスの構築などを行う技術の総称です。XMLドキュメントを指定したURLから読み込み、画面描画やユーザの操作などと並行してサーバと非同期に通信するWebアプリケーションを実現することができます。

Q&A

解決済

3回答

1154閲覧

jqueryによって動的に生成されたinputタグの中身を取得する方法

ryorgyo

総合スコア2

Django

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

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

Ajax

Ajaxとは、Webブラウザ内で搭載されているJavaScriptのHTTP通信機能を使って非同期通信を利用し、インターフェイスの構築などを行う技術の総称です。XMLドキュメントを指定したURLから読み込み、画面描画やユーザの操作などと並行してサーバと非同期に通信するWebアプリケーションを実現することができます。

0グッド

0クリップ

投稿2023/03/09 07:24

実現したいこと

DjangoでAjaxを利用して動的にデータの編集をしたい。

前提

現在Djangoを利用してSNSのようなアプリケーションを作っています。
搭載する機能は投稿のCRUDといいね(Ajax)機能であり、編集機能以外は完成しました。
編集機能の実装は、「元はpタグで出力している投稿内容」を「inputタグに変更して」、「ajax通信を用いてviewに飛ばし」、「viewでDBの書き換えを行う」という処理を想定しています。

発生している問題・エラーメッセージ

viewへ値を渡す際に、空白で返ってくる。

該当のソースコード

DjangoHTML:index.html

1<form 2 action="" 3 method="POST" 4 name="f" 5 class="d-flex flex-column align-items-center" 6 > 7 <input type="hidden" name="send_id" id="send_id" /> 8 <input type="hidden" name="send_text" id="send_text" /> 9{% csrf_token %} {%for post in posts%} 10 <div class="card mb-3" style="width: 30rem" id="{{post.id}}"> 11 <div class="card-body"> 12 <p class="card-text py-3 mb-0" id="title{{post.id}}">{{post.title}}</p> 13 {%if edit%} 14 <button class="btn btn-outline-primary edit">編集する</button> 15 {%endif%} 16 </div> 17 </div> 18 {%endfor%} 19 </form> 20<script> 21 // HTMLが読み込まれた際に発火するイベント 22 $(document).ready(function () { 23 // 編集ボタンを押した時にbtnのテキストと入力欄が変更する処理 24 $(".edit").click(function (e) { 25 // btn本来のform actionに飛ばす機能を無効にする 26 e.preventDefault(); 27 // 該当投稿のidを取得する 28 let id = $(this).parents(".card").attr("id"); 29 // 投稿内容をinputタグに置き換える 30 $(`#title${id}`).replaceWith(function () { 31 $(this).replaceWith( 32 `<input type='text' name='edit_text' class="form-control my-2" id="title${id}" value="${$( 33 this 34 ).text()}" >` 35 ); 36 }); 37 // btnのテキストと機能の書き換え 38 // 編集ボタンと削除ボタンを取得する 39 let edit_btn = $(this); 40 let delete_btn = $(this).next(); 41 // 「編集する」ボタンのテキストを変更 42 edit_btn.html("完了する"); 43 delete_btn.html("変更を破棄する"); 44 // 完了ボタンを押した際に、編集する 45 $(edit_btn).click(function () { 46 // idをsend_idに格納する 47 let id = $(this).parents('.card').attr('id') 48 let text = $(`#title${id}`).val() 49 // inputのvalをsend_textに格納する 50 $('form[name="f"]').attr('action','{% url 'edit' %}'); 51 $('form[name="f"]').submit() 52 $.ajax({ 53 url: '{% url "edit" %}', 54 type: "POST", 55 data: { 56 // viewにrequestで押されたbuttonのid属性を渡す 57 'id': id, 58 'new_text':text, 59 }, 60 dataType: "json", 61 // 成功した時 62 }).done(function (response) { 63 // inputタグをpタグに戻す 64 $(`#title${id}`).replaceWith(function () { 65 $(this).replaceWith( 66 `<p class="card-text py-3 mb-0" id="title${id}">${response.text}</p>` 67 ); 68 }); 69 // 完了ボタンを編集ボタンに戻す 70 edit_btn.html("編集する"); 71 // 変更を破棄するボタンを削除ボタンに直す 72 delete_btn.html("削除する"); 73 }); 74 }); 75 }); 76 }); 77 </script>

Django:views.py

1# 投稿編集 2def edit(request): 3 # idを取得 4 post_id = request.POST.get('id') 5 print(post_id) 6 # 編集後のテキストを取得 7 edit_text = request.POST.getlist('new_text') 8 print(edit_text) 9 # 該当投稿取得 10 post = モデル名.objects.get(pk=post_id) 11 # 書き換え 12 post.title = edit_text 13 # 変更をセーブする 14 post.save() 15 return JsonResponse({'text':edit_text})

試したこと

inputの内容をinput type=hiddenのvalueに渡し、viewで受け取る→失敗(空で帰ってくる)
pタグでなくinput disabledにし、編集時にdisabel = Falseとする→成功

この結果から、javascriptで書き換えたinputタグは、requestで送られず、val()も返ってこないと解釈できますが、私が実装したいのが「pタグをinputタグに書き換えてviewに値を渡す」ことであるので、できればこの方法は使いたくありません(CSSを当てる必要があるため)。

また、同じような方法で、input type=hiddenを投稿内容のpタグの下に忍ばせておくことも考えており、良い方法がなければこのやり方で実装すると思うのですが、HTML内にタグを増やしたくないため、書き換えでのやり方があれば教えていただきたいです。

私がご質問させていただきたいのは、「javascriptで動的に生成したinputタグは、入力された値をjsもしくはviewによって値を取得できるのかどうか」です。よろしくお願いしたします。

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

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

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

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

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

int32_t

2023/03/09 07:46

変数 text には期待どおりの値は入ってたんでしょうか?
ryorgyo

2023/03/09 07:55

お世話になります。コメントありがとうございます。 javascript内の処理の変数textについて、値は入っておりませんでした。 漏れていましたが、上記と質問内にもある解釈により、javascriptで動的に生成したinputの入力値はとれないのでは?と思いご質問させていただいた次第です。
int32_t

2023/03/09 08:01

> javascriptで動的に生成したinputの入力値はとれないのでは? そんなことはないです。絶対に取れます。 replaceWith() の使い方がおかしいとか submit() と AJAX 両方あるのはおかしいとかイロイロ気になるところはありますが、とにかくどこまで期待どおり動いているか1つずつ確認する必要がありますね。
int32_t

2023/03/09 08:06

まずは、「let text = $(`#title${id}`).val()」を実行したときに $(`#title${id}`) が本当に <input> を指しているかどうかの確認ですかね。
ryorgyo

2023/03/09 14:11 編集

ご指摘ありがとうございます。replaceWithやその他に関しては後ほどリファクタリング行います! また、ajaxとsubmitのダブりも気づきませんでした。こちらはajaxでviewへ遷移するよう修正いたしました。 本題に入りますが、$('#title${id}')は問題なく、変化後のinputタグを取得いたしました。 そして、ブラウザ上のコンソール画面で、$('#title${id}')←こちらのidは手動で入力しました。を取得したところ問題なく、入力した値(編集後の値)を取得することができました。 なので、 #{id}の型が違く、取得できないのでは?と考え、完了ボタンを押した際に発火するイベント内にて、変数${id}での指定ではなく、直打ち(id値をあらかじめ押さえておき、そのtextを書き換える)で指定したところ、うまく行きませんでした。 次の施策で、値を常時取得する関数を「編集ボタンを押した際に発火するイベント」の外で記述してみようと思います。
guest

回答3

0

ベストアンサー

最初に $(".edit") ボタンを押すともちろん <p><input> に置き換える関数が走ります。
次に「完了する」と見た目が変わった $(".edit") ボタンを押すと、こんども同じ関数が走り <input> を別の <input> に置き換え、その後 $(`#title${id}`).val() を処理する関数が走ります。<input><input> で置き換えるときには $(this).text() は空なので値が失われます。

1つのボタンで複数の異なる役割をこなすのはトラブルの元です。別々のボタン要素にして、場面によって表示非表示を切り替えましょう。

投稿2023/03/09 21:49

int32_t

総合スコア20661

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

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

ryorgyo

2023/03/10 14:07

ありがとうございます。 私も色々な手法で試し、無事値を取得することができましたが、ボタンのイベントが消えずに悩んでいました。 このコメントを拝見して、$(.'edit')のイベントが削除されておらず、2度3度と走っていることに気づきました。 >>1つのボタンで複数の異なる役割をこなすのはトラブルの元です。 こちらが身に沁みてわかりました。たくさんアドバイスをくださり、ありがとうございます。ボタンは複数用いて実装することにします。
guest

0

回答ではありません

とりあえず、djagoで返したHTMLからjQueryのajaxを使ってREST APIっぽくアクセスし、その結果をコンソールに出力する簡易コード環境を作成するスクリプトを書いてみました。本来Django REST frameworkとか使うべきなんだろうけど、原始的なのが良さそうだったので、参考までにということです。

環境構築スクリプト

python

1import os 2import venv 3import subprocess 4ENV = 'env' 5if os.name == 'posix': 6 BIN = 'bin' 7else: 8 BIN = 'Scripts' 9builder = venv.EnvBuilder(with_pip=True) 10builder.create(ENV) 11cwd = os.getcwd() 12python = os.path.join(cwd, ENV, BIN, 'python') 13os.environ['VIRTUAL_ENV'] = os.path.join(cwd, ENV) 14os.environ['PATH'] = os.path.join(cwd, ENV, BIN) + os.pathsep + os.environ['PATH'] 15p = subprocess.run([python, '-m', 'pip', 'install', '--upgrade', 'pip', 'setuptools']) 16p = subprocess.run([python, '-m', 'pip', 'install', 'django==4.1.7', 'whatthepatch==1.0.4']) 17with open('generated.py', 'wt', encoding='utf8') as f: 18 f.write("""\ 19import subprocess 20import os 21p = subprocess.run('django-admin startproject mysite', shell=True) 22os.chdir('mysite') 23p = subprocess.run('python manage.py startapp myapp', shell=True) 24p = subprocess.run('python ../patch.py ../patch.diff', shell=True) 25""") 26with open('patch.diff', 'wt', encoding='utf8') as f: 27 f.write("""\ 28diff --git a/myapp/templates/myapp/index.html b/myapp/templates/myapp/index.html 29new file mode 100644 30index 0000000..e0a8b77 31--- /dev/null 32+++ b/myapp/templates/myapp/index.html 33@@ -0,0 +1,22 @@ 34+{% csrf_token %} 35+<button id="start_ajax">ここ</button> 36+<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script> 37+<script> 38+$('#start_ajax').on('click', (ev)=>{ 39+ $.ajax({ 40+ url: '{% url "api" %}', 41+ type: "POST", 42+ headers: { 43+ 'X-CSRFToken': $('input[name="csrfmiddlewaretoken"]').attr('value') 44+ }, 45+ data: { 46+ 'id': 'hoge', 47+ 'new_text': 'fuga', 48+ }, 49+ dataType: "json", 50+ }).done((data) => { 51+ console.log(data); 52+ }); 53+}); 54+</script> 55+ 56diff --git a/myapp/urls.py b/myapp/urls.py 57new file mode 100644 58index 0000000..70c2b6b 59--- /dev/null 60+++ b/myapp/urls.py 61@@ -0,0 +1,7 @@ 62+from django.urls import path 63+from . import views 64+ 65+urlpatterns = [ 66+ path('', views.index, name='index'), 67+ path('api/', views.api, name='api'), 68+] 69diff --git a/myapp/views.py b/myapp/views.py 70index 91ea44a..4ad8b95 100644 71--- a/myapp/views.py 72+++ b/myapp/views.py 73@@ -1,3 +1,8 @@ 74 from django.shortcuts import render 75+from django.http import JsonResponse 76 77-# Create your views here. 78+def index(request): 79+ return render(request, 'myapp/index.html', {}) 80+ 81+def api(request): 82+ return JsonResponse({'newid': request.POST['id'] + '1', 'text': request.POST['new_text'] + '2'}) 83diff --git a/mysite/settings.py b/mysite/settings.py 84index 5ebda72..4c6fdc7 100644 85--- a/mysite/settings.py 86+++ b/mysite/settings.py 87@@ -31,6 +31,7 @@ ALLOWED_HOSTS = [] 88 # Application definition 89 90 INSTALLED_APPS = [ 91+ 'myapp.apps.MyappConfig', 92 'django.contrib.admin', 93 'django.contrib.auth', 94 'django.contrib.contenttypes', 95@@ -103,9 +104,9 @@ AUTH_PASSWORD_VALIDATORS = [ 96 # Internationalization 97 # https://docs.djangoproject.com/en/4.1/topics/i18n/ 98 99-LANGUAGE_CODE = 'en-us' 100+LANGUAGE_CODE = 'ja' 101 102-TIME_ZONE = 'UTC' 103+TIME_ZONE = 'Asia/Tokyo' 104 105 USE_I18N = True 106 107diff --git a/mysite/urls.py b/mysite/urls.py 108index 8bc0cd4..d2fd81c 100644 109--- a/mysite/urls.py 110+++ b/mysite/urls.py 111@@ -14,8 +14,9 @@ Including another URLconf 112 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 113 \"\"\" 114 from django.contrib import admin 115-from django.urls import path 116+from django.urls import include, path 117 118 urlpatterns = [ 119+ path('myapp/', include('myapp.urls')), 120 path('admin/', admin.site.urls), 121 ] 122""") 123with open('patch.py', 'wt', encoding='utf8') as f: 124 f.write("""\ 125import whatthepatch, os, sys 126with open(sys.argv[1], 'rt', encoding='utf8') as fp: 127 for diff in whatthepatch.parse_patch(fp): 128 header = diff.header 129 if header.new_path == '/dev/null': 130 os.remove(header.old_path) 131 else: 132 if header.old_path == '/dev/null': 133 old = [] 134 else: 135 with open(header.old_path, 'rt', encoding='utf8') as fo: 136 old = [line.rstrip(os.linesep) for line in fo] 137 new = whatthepatch.apply_diff(diff, old) 138 try: 139 os.makedirs(os.path.dirname(header.new_path)) 140 except FileExistsError: 141 pass 142 with open(header.new_path, 'wt', encoding='utf8') as fn: 143 for line in new: 144 print(line, file=fn) 145""") 146p = subprocess.run([python, 'generated.py'])

環境構築スクリプトの使い方

  1. 上記ファイルをcreate_env.pyという名前で作成
  2. tmpディレクトリを作成
  3. tmpディレクトリに移動
  4. create_env.pyを実行

Windows(コマンドプロンプト)の場合

cmd

1C:\...\>cd create_env.pyを作ったフォルダ 2C:\...\>mkdir tmp 3C:\...\>cd tmp 4C:\...\>python ..\create_env.py

Unixライク系(bash)の場合

Ubuntuの場合は、venvを使えるようにpython3-venvパッケージのインストールが必要です(入れてない場合)。

bash

1$ cd create_env.pyを作ったフォルダ 2$ mkdir tmp 3$ cd tmp 4$ python ../create_env.py 5 6以上で環境構築完了です。 7 8## 構築環境への入り方 9上記スクリプトは他のpython環境に影響を与えないため、`venv`を使った仮想環境(といってもVMとかじゃない)を作っています。環境を使うためには、まずvenvでその環境に入る必要があります。 10 11### Windows(コマンドプロンプト)の場合 12```cmd 13C:\...\>cd さっき作ったtmpディレクトリ 14C:\...\>.\env\Scripts\activate 15(env)C:\...\> 16...(作業が終わったら)... 17(env)C:\...\>deactivate 18C:\...\>

Unixライク系(bash)の場合

bash

1$ cd さっき作ったtmpディレクトリ 2$ ./env/bin/activate 3(env)$ 4...(作業が終わったら)... 5(env)$ deactivate 6$

djangoの起動

これは説明するまでもないと思いますが、mysiteプロジェクトのディレクトリに移動後、python manage.py runserverでローカルの8000ポートに起動します。

ブラウザから接続

http://localhost:8000/myappを開き、Ctrl+Shift+Iとかで開発者ツールの画面を開きます。ネットワーク画面でhttpプロトコルの内容を確認しながら、「ここ」ボタンを押すと、ajaxのリクエストが出てレスポンスが返り、コンソールにjsonが表示されているのがわかります。

djangoのviewとtemplateの解説

tmp/mysite/myapp/views.py

python

1from django.shortcuts import render 2from django.http import JsonResponse 3 4def index(request): 5 return render(request, 'myapp/index.html', {}) 6 7def api(request): 8 return JsonResponse({'newid': request.POST['id'] + '1', 'text': request.POST['new_text'] + '2'})

index()http://localhost:8000/myapp/の受け口で、テンプレートを返しています。
api()http://localhost:8000/myapp/api/の受け口`で、ajaxでもらったデータに1と2を付けてjsonで返しています。この部分はDjango REST frameworkでいいかもというところです。

tmp/mysite/myapp/template/myapp/index.html

html

1{% csrf_token %} 2<button id="start_ajax">ここ</button> 3<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script> 4<script> 5$('#start_ajax').on('click', (ev)=>{ 6 $.ajax({ 7 url: '{% url "api" %}', 8 type: "POST", 9 headers: { 10 'X-CSRFToken': $('input[name="csrfmiddlewaretoken"]').attr('value') 11 }, 12 data: { 13 'id': 'hoge', 14 'new_text': 'fuga', 15 }, 16 dataType: "json", 17 }).done((data) => { 18 console.log(data); 19 }); 20}); 21</script>

ボタンを押されたらajaxを実行してるだけです。jsonが返ってきたらコンソールに吐いています。一点面倒なのはdjangoが生成してくれるCSRFトークンのajaxでの送信です。
https://docs.djangoproject.com/en/4.1/howto/csrf/
を参考に送ればOKです。

投稿2023/03/09 16:42

dameo

総合スコア943

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

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

ryorgyo

2023/03/10 14:04

原始的は笑いました笑。 僕としては初めて実装したので、めちゃくちゃ便利なものあるじゃん!って感じだったのですが、他の手法で且つ使い勝手の良いもの?があるのですね。それが知れただけ今回質問をこのサイトに投げて良かったです。 また、ソースコードと参考サイトありがとうございます!とても助かります。動かしてみて挙動を勉強してみます。
guest

0

イベント外で変数textを定義して、入力があるたびにtextの中身を切り替える、その変数をイベントの中から呼び出すことで無事入力値を取得することができました。

おそらく、editボタンが押された際の定義イベント内だと、「生成されたinputタグのvalを取る」動作が働き、動的に値を入手できなかったのだと思います。

DjangoHTML

1$(".edit").on("click", function (e) { 2 // btn本来のform actionに飛ばす機能を無効にする 3 e.preventDefault(); 4 // 該当投稿のidを取得する 5 let id = $(this).parents(".card").attr("id"); 6 let text = $(`#title${id}`).text(); 7 // 投稿内容をinputタグに置き換える 8 $(`#title${id}`).replaceWith(function () { 9 $(this).replaceWith( 10 `<input type='text' name='edit_text' class="form-control my-2" id="title${id}" value="${$( 11 this 12 ).text()}" >` 13 ); 14 }); 15 // inputタグに値が入力されるたびに発火するイベント 16 $('input[type="text"]').on("input", function () { 17 // 変数textの中にinputに入力された値を格納する 18 text = $(this).val(); 19 }); 20 // btnのテキストと機能の書き換え 21 // 編集ボタンと削除ボタンを取得する 22 let edit_btn = $(this); 23 let delete_btn = $(this).next(); 24 // 「編集する」ボタンのテキストを変更 25 edit_btn.html("完了する"); 26 delete_btn.html("変更を破棄する"); 27 // 完了ボタンを押した際に、編集するイベントを追加 28 $(edit_btn).on("click", function () { 29 // idをsend_idに格納する 30 let id = $(this).parents(".card").attr("id"); 31 // inputのvalをsend_textに格納する 32 $.ajax({ 33 url: '{% url "edit" %}', 34 type: "POST", 35 data: { 36 // viewにrequestで押されたbuttonのid属性を渡す 37 id: id, 38 new_text: text, 39 }, 40 dataType: "json", 41 // 成功した時 42 }).done(function (response) { 43 // inputタグをpタグに戻す 44 $(`#title${id}`).replaceWith(function () { 45 $(this).replaceWith( 46 `<p class="card-text py-3 mb-0" id="title${id}">${response.text}</p>` 47 ); 48 }); 49 // 完了ボタンを編集ボタンに戻す 50 edit_btn.html("編集する"); 51 // 変更を破棄するボタンを削除ボタンに直す 52 delete_btn.html("削除する"); 53 }); 54 }); 55 });

投稿2023/03/09 15:30

ryorgyo

総合スコア2

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問