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

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

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

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

jQuery UI

jQuery UI はjQuery公式のインターフェースライブラリであり、対話型のウェブアプリケーションを作る際に役立ちます。

Python 3.x

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

Ajax

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

Q&A

解決済

1回答

3735閲覧

django、Ajaxを使って、順番を並び替えたい

hiranohirano

総合スコア33

Django

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

jQuery UI

jQuery UI はjQuery公式のインターフェースライブラリであり、対話型のウェブアプリケーションを作る際に役立ちます。

Python 3.x

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

Ajax

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

0グッド

0クリップ

投稿2019/03/02 03:50

前提・実現したいこと

管理画面上で、店舗オーナーが店舗スタッフの順番を
ドラッグ&ドロップで簡単に並び替えられるようにしたいです。

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

ブラウザ上で並び替え自体はできるのですが、スタッフの順番データ(order)が更新されません。

該当のソースコード

urls.py

python

1path('ajax/order_change/', ajax_order_change, name='ajax_order_change'), 2 3path('owner<int:owner_id>/store<int:store_id>/staff/', StoreStaffView.as_view(), name='owner_store_staff'), 4

models.py

python

1class Store(models.Model): 2 name = models.CharField(verbose_name='店舗名', max_length=100) 3 4class Staff(models.Model): 5 name = models.CharField(verbose_name='名前', max_length=100) 6 store_id = models.ForeignKey( 7 Store, verbose_name='店舗ID', on_delete=models.CASCADE,) 8 order = models.IntegerField(verbose_name='順序', blank=True, null=True) 9

views.py

python

1def ajax_order_change(request, store_id): 2 order = request.POST.get('order') 3 staff_list = [{ 'pk':staff.pk, 'order': staff.order } for staff in Staff.objects.filter(store_id=store_id)] 4 d = { 5 'staff_list':staff_list 6 } 7 return JsonResponse(d) 8 9class StoreStaffViewView(ListView): 10 model = Staff 11 template_name = 'myapp/owner_store_staff.html' 12 pk_url_kwarg = 'store_id' 13 14 def get_context_data(self, **kwargs): 15 context = super().get_context_data(**kwargs) 16 context['store'] = Store.objects.get(pk=self.kwargs['store_id']) 17 return context

owner_store_staff.html

html

1 2{% block content %} 3 <div id="sortable"> 4 {% for staff in store.staff_set.all %} 5 <div> 6 <p class="order">{{ staff.order }}</p> 7 <p>{{ staff.name }}</p> 8 </div> 9 {% endfor %} 10 </div> 11{% endblock %} 12 13{% block extrajs %} 14<script> 15 function getCookie(name) { 16 var cookieValue = null; 17 if (document.cookie && document.cookie !== '') { 18 var cookies = document.cookie.split(';'); 19 for (var i = 0; i < cookies.length; i++) { 20 var cookie = jQuery.trim(cookies[i]); 21 // Does this cookie string begin with the name we want? 22 if (cookie.substring(0, name.length + 1) === (name + '=')) { 23 cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); 24 break; 25 } 26 } 27 } 28 return cookieValue; 29 } 30 31 var csrftoken = getCookie('csrftoken'); 32 33 function csrfSafeMethod(method) { 34 // these HTTP methods do not require CSRF protection 35 return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); 36 } 37 38 $.ajaxSetup({ 39 beforeSend: function (xhr, settings) { 40 if (!csrfSafeMethod(settings.type) && !this.crossDomain) { 41 xhr.setRequestHeader("X-CSRFToken", csrftoken); 42 } 43 } 44 }); 45 46 //order並び替え------------------------------ 47 $('#sortable').sortable(); 48 $('#sortable').bind('sortstop', function (e, ui) { 49 // ソートが完了したら実行される。 50 var rows = $('#sortable .order'); 51 for (var i = 0, rowTotal = rows.length; i < rowTotal; i += 1) { 52 $($('.order')[i]).text(i + 1); 53 } 54 $.ajax({ 55 'url': '{% url "myapp:ajax_order_change" %}', 56 'type': 'POST', 57 'data': { 58 'order': $('.order').val(), 59 }, 60 'dataType': 'json' 61 }); 62 }); 63</script> 64 65{% endblock %}

お分かりになる方、何卒宜しくお願い致します。

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

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

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

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

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

guest

回答1

0

ベストアンサー

ざっと拝見したところでは、フロント側では #sortable 要素の sortstop イベントが起こったときに $('.order').val() の戻り値をポストしていて、バックエンド側では ajax_order_change() で更新処理を行う、というイメージとお見受けしました。

この理解が正しければ、

  • フロント側では、 $('.order').val() の戻り値はお望みのものではないのではないかと思います。一度中身を確認してみてください。
  • バックエンド側も、 ajax_order_change() には更新処理は書かれていないように見えます。

ですので、更新されないのはある意味当然かなという気がします。

「代わりにコードを書いて欲しい」ではなく、基本的に自力でやるつもりでサポートを求めていらっしゃるのであれば、まず、 Ajax でのデータ更新は一般にどのように書くものなのか、そして、今回の場合だとどのような処理をすればよいのか(フロントとバックエンドの間でどういうデータをやりとりすればいいのか、バックエンド側ではどういう処理をすればよいのか)をまず学習・整理して、擬似コードを書くところからチャレンジされてみてはいかがでしょうか。

jQuery UI Sortable を使われているのであれば次のページ等が参考になるものと思います。

追記 2019/03/14

フロント側からのデータを取得できない理由を教えていただけますと幸いです。

CSRF 設定( @csrf_exempt 等)は適切にされていて ajax_order_change() が実行されているのに print(staff_list) が空になる、ということですかね。

公式ドキュメントを読むと

If you need to access raw or non-form data posted in the request, access this through the HttpRequest.body attribute instead.

とのことで、このケースでは request.POST は使えないようですね。次のような感じで取得する必要があるのだと思います。

python

1import json 2staff_list = json.loads(request.body).get('staff_list', [])

これ ↑ はあくまでも説明のための単純化したサンプルです。実際にはどのようなデータが来るかわからないという前提で、例外処理等は適切に行ってください。

以下参考です。

Stack Overflow の質問:

公式ドキュメント:

request.POST will no longer include data posted via HTTP requests with non form-specific content-types in the header. In prior versions, data posted with content-types other than multipart/form-data or application/x-www-form-urlencoded would still end up represented in the request.POST attribute. Developers wishing to access the raw POST data for these cases, should use the request.body attribute instead.

コード:

追記 2019/03/15

staff_list = json.loads(request.body).get('staff_list', []) にすると、staff_listの中身が空ではなくなったのですが、JSONDecodeErrorになってしまいます。

...

度々恐縮ですが、アドバイスいただけますと幸いです。。

コメントでいただいた追加のご質問に回答します。

こちら検索してみたところ、 jQuery.ajax()data パラメータが文字列ではない場合に自動的に query string に変換する挙動をするようです。そして、今回の場合のように、 data の値が object で、その要素に array または object が含まれる場合には注意が必要とのことです。

以下 jQuery 公式ドキュメントからの引用です(英語に抵抗があれば Google 翻訳等を使ってください)。

data

Type: PlainObject or String or Array

Data to be sent to the server. It is converted to a query string, if not already a string. It's appended to the url for GET-requests. See processData option to prevent this automatic processing. Object must be Key/Value pairs. If value is an Array, jQuery serializes multiple values with same key based on the value of the traditional setting (described below).

Note: Because some frameworks have limited ability to parse serialized arrays, developers should exercise caution when passing an obj argument that contains objects or arrays nested within another array.

Note: Because there is no universally agreed-upon specification for param strings, it is not possible to encode complex data structures using this method in a manner that works ideally across all languages supporting such input. Use JSON format as an alternative for encoding complex data instead.

というわけで、フロント側とバックエンド側の両方で少し工夫が必要なようですね。すみません、私は jQuery で試していなかったので気が付きませんでした。

というわけで、具体的には、例えば JS 側を次のようにして

js

1$.ajax({ 2 'url': '{% url "myapp:ajax_order_change" %}', 3 'type': 'POST', 4 'data': { 5 'staff_list': JSON.stringify(staff_list), 6 }, 7 'dataType': 'json' 8 }); 9});

バックエンド側を次のようにすると値が取得できるのではないかと思います。

python

1import json 2from django.http import QueryDict 3 4def ajax_order_change(request, store_id): 5 # print() はデバッグが終われば削除してください 6 print(request.body) 7 params = QueryDict(request.body) 8 print(params) 9 staff_list = json.loads(params['order']) 10 print(staff_list) 11 // ...

request.body が query string になって渡されるので QueryDict に渡して使うとやりやすいと思います。

あくまでもサンプルなので、コピペせずアイデアのみ参考にしてください。また、繰り返しになりますが、どのようなデータが来るかわからないという前提で、例外処理等は適切に行ってくださいね :D

この点についてはこれで解決するのではないかと思います。

もし他にも問題が出てきましたら、一対一でサポートし続けるのは難しいので、別の質問を立ててみてくださいー

投稿2019/03/02 09:17

編集2019/03/15 11:55
gh640

総合スコア1407

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

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

hiranohirano

2019/03/03 14:09

ご回答いただきありがとうございます。色々調べてみて、スタッフID=1のみの順番(order)を更新できるようにはなりましたが、 フロント側で、辞書型のリストを渡そうとするところで少しつまづいています。 フロント側では、#sortable要素の sortstopイベントが起こったときに、staffIDと変更された順番(order)を辞書型リストとして残し、バックエンド側に渡したいと考えています。 htmlの <p class="order">{{ staff.order }}</p> だった部分にidをつけて以下のようにしました。 <p class="order" id="{{ staff.pk }}">{{ staff.order }}</p> 以下の部分で、idと順番を残したいと思っています。 $('#sortable').sortable(); $('#sortable').bind('sortstop', function (e, ui) { // ソートが完了したら実行される。 var rows = $('#sortable .order'); var staff_list = {};   //空の辞書型リストを用意 for (var i = 0, rowTotal = rows.length; i < rowTotal; i += 1) { $($('.order')[i]).text(i + 1); staff_id = $('#sortable .order').id;  //スタッフidを取得 order = i+1;  //orderの値を取得 staff_list.staff_id = order;  //id:orderの形で辞書型リストに残す } 期待しているのは、{'スタッフID': '順番', 'スタッフID': '順番', 'スタッフID': '順番', }のような辞書型リストですが、返ってくるのは、{staff_id: 5} というものになってしまいます。 フロント側でやろうとしていることの方向性があっているかと、合っている場合、期待通りにならない理由を教えていただけますと幸いです。
gh640

2019/03/04 10:53

大きな方向性はよいのでないでしょうか(これはあくまで私の意見なので「合っているかどうか」での回答は避けておきます)。 お考えのとおり、ドラッグ & ドロップで場所が変わった要素だけでなく、影響を受けるすべての要素の order をバックエンドに投げる必要があると思います。 ご期待通りにならない理由は、オブジェクト `staff_list` の要素にアクセスするときのキーが `staff_id` になってしまっているからではないでしょうか。変数 `staff_id` に格納された値をキーとしたい場合は次のように変更すべきかなと思います。 - staff_list.staff_id = order;  //id:orderの形で辞書型リストに残す + staff_list[staff_id] = order;  //id:orderの形で辞書型リストに残す ご質問への回答になっているでしょうか。がんばってください :D
hiranohirano

2019/03/11 08:32

ご回答いただき、ありがとうございます。 すいません、バック側でもつまづいてしまいましたので、再度ご質問させてください。 フロント側では期待通りに動いていると思います。 console.log(staff_list)と書くと、 {5: 2, 11: 3, 18: 4, 19: 1} のようになり、{スタッフID:順序}の辞書型リストができました。 $('#sortable').sortable(); $('#sortable').bind('sortstop', function (e, ui) { // ソートが完了したら実行される。 var rows = $('#sortable .order'); staff_list = {}; for (var i = 0, rowTotal = rows.length; i < rowTotal; i += 1) { $($('.order')[i]).text(i + 1); staff_id = rows[i].id; staff_id = Number(staff_id);   order = i+1; staff_list[staff_id] = order; } console.log(staff_list); $.ajax({ 'url': '{% url "myapp:ajax_order_change" %}', 'type': 'POST', 'data': { 'staff_list': staff_list, }, 'dataType': 'json' }); }); バック側では、フロント側からのstaff_listを取得して、データを更新させる処理を書いたつもりです。 しかし、print('staff_list')とやると、中身が空なので、おそらくここが間違っています。 フロント側では、staff_listの中に思った通りのものが入っていますが、バック側で取得できていません。 def ajax_order_change(request): #フロント側からデータを取得する staff_list = request.POST.getlist('staff_list') print(staff_list)  #これを確認すると、中身が空になってしまう。 #スタッフidを探して、orderを書き換える for key, value in staff_list: staff = Staff.objects.get(pk=key) staff.order = value staff.save() d = { 'staff_list': staff_list } return JsonResponse(d) フロント側からのデータを取得できない理由を教えていただけますと幸いです。 宜しくお願い致します。
gh640

2019/03/14 13:52

「確かに送られているデータが Django の request.POST に入っていないのはなぜか?」というご質問への回答を解答欄に追記しました。ご覧になってみてください :D
hiranohirano

2019/03/15 09:31

ご返信ありがとうございます。 csrf設定はowner_store_staff.htmlのfunction csrfSafeMethodのところでおそらく出来ているかと思います。 staff_list = json.loads(request.body).get('staff_list', []) にすると、staff_listの中身が空ではなくなったのですが、JSONDecodeErrorになってしまいます。 json_txt = str(json_txt).replace("'", '"').replace('True', 'true').replace('False', 'false') のようなことや body_unicode = request.body.decode('utf-8') staff_list = json.loads(body_unicode).get('staff_list',[]) などなど、色々と試してみましたが、解決策がわからず... 度々恐縮ですが、アドバイスいただけますと幸いです。。
hiranohirano

2019/03/15 09:35

body_unicode = request.body.decode('utf-8') staff_list = json.loads(body_unicode).get('staff_list',[]) のところで、 print(body_unicode)とすると、 おそらくデコードされたであるデータが返ってきます。 しかし、print(staff_list)としても、何も返ってきません。
gh640

2019/03/15 12:02

`JSONDecodeError` が出てしまう件に関するご質問への回答を回答欄に追記しました。(リンク先も合わせて)ご覧になりお試しになってみてくださいー。
hiranohirano

2019/03/17 03:13

ご返信ありがとうございます。 またつまづいてしまったのですが、頂いたアドバイスをもとに自力で頑張ってみます。 色々と丁寧に教えていただき、ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問