フルスタックチャンネル
サインアップサインアップ
ログインログイン
利用規約プライバシーポリシーお問い合わせ
Copyright © All rights reserved | FullStackChannel
解決済
POSTした内容からdictに変換する方法
Django
atnek
2024/02/06 17:28

お世話になっております。
DjangoにてPOSTした内容からDictに変換する方法をさがしているのですが、いい方法がありましたらご教示頂けますと幸いです。


実現したい内容は、
下記のようなフォームからPOSTを行い、

<tr>
	<td>
		<input name="key" value="名称1">
	</td>
	<td>
		<input name="Status1" value="値1-1">
	<td>
		<input name="Status2" value="値1-2">
	</td>
</tr>

<tr>
	<td>
		<input name="key" value="名称2">
	</td>
	<td>
		<input name="Status1" value="値2-1">
	<td>
		<input name="Status2" value="値2-2">
	</td>
</tr>

<tr>
	<td>
		<input name="key" value="名称3">
	</td>
	<td>
		<input name="Status1" value="値3-1">
	<td>
		<input name="Status2" value="値3-2">
	</td>
</tr>
~ jqueryで 行は増やせるようにしています ~

POSTされた内容を下記のようなdictで保存を行いたい感じです。

field = {
	'名称1' : {'status1' : '値1-1', 'status1' : '値1-2', },
	'名称2' : {'status1' : '値2-1', 'status1' : '値2-2', },
	'名称3' : {'status1' : '値3-1', 'status1' : '値3-2', }

}

一応、現時点で下記のようにコードは書けて、実現はできております。、

field = {}
parameters = request.POST
keys = parameters.getlist('key')
value1 = parameters.getlist('value1')
vakue2 = parameters.getlist('value2')
for i in range(len(keys)):
	field[keys[i]] = {
		'value1' : value1[i],
		'value2' : value2[i],
	}

が、このコードだと値がズレて保存されてしまう可能性があり、何かベストな記述が出来ないか模索中です。

何かヒント等を頂けましたらとても助かります。

回答 2件
login
回答するにはログインが必要です
はる@講師
1年以上前

DjangoでPOSTされたフォームの内容を辞書に変換する際、各行の複数の値をキーに対応させるためには、フォームの構造を少し変更して、各行ごとに一意の識別子を持たせることが効果的です。
jQueryを使用して行を動的に増やしているとのことですので、各行にインデックスや一意のIDを付与し、そのIDをキーとしてフォームのname属性に組み込むことで、サーバー側での処理を容易にします。

例えば、以下のようにフォームを修正します。

<tr>
	<td>
		<input name="items[0][key]" value="名称1">
	</td>
	<td>
		<input name="items[0][Status1]" value="値1-1">
	</td>
	<td>
		<input name="items[0][Status2]" value="値1-2">
	</td>
</tr>
<tr>
	<td>
		<input name="items[1][key]" value="名称2">
	</td>
	<td>
		<input name="items[1][Status1]" value="値2-1">
	</td>
	<td>
		<input name="items[1][Status2]" value="値2-2">
	</td>
</tr>
<tr>
	<td>
		<input name="items[2][key]" value="名称3">
	</td>
	<td>
		<input name="items[2][Status1]" value="値3-1">
	</td>
	<td>
		<input name="items[2][Status2]" value="値3-2">
	</td>
</tr>

そして、Django側でこの構造に合わせてデータを処理します。以下はその一例です:

from django.http import HttpRequest

def your_view_function(request: HttpRequest):
    # まず、itemsというキーでPOSTされたデータを取得
    items = request.POST.getlist('items')
    field = {}
    for item in items:
        # 各アイテム(行)のデータを取得
        key = item.get('key')
        if key:  # キーが存在する場合のみ処理
            # 辞書に格納
            field[key] = {
                'status1': item.get('Status1', ''),
                'status2': item.get('Status2', '')
            }
    return field

ただし、このコードは request.POST.getlist('items') で期待通りのデータを取得できる前提で書かれていますが、実際には request.POST からこのようなネストされたデータ構造を直接取得することはできません。
request.POST は QueryDict オブジェクトで、このようなネストされたデータ構造を扱うためには、カスタムのパース処理を実装する必要があります。

正確には、以下のようにPOSTデータの処理を行います:

def your_view_function(request):
    field = {}
    for key in request.POST.keys():
        if key.startswith('items['):
            # フィールド名からインデックスと実際のキーを抽出
            _, index, field_name = key.strip(']').split('[')
            index = int(index)  # インデックスを整数に変換
            if 'key' in field_name:
                # キーの場合は、新しいエントリを作成
                real_key = request.POST[key]
                field.setdefault(real_key, {})
            else:
                # Status1, Status2の場合は、既存のエントリを更新
                real_key = request.POST[f'items[{index}][key]']
                field[real_key][field_name.lower()] = request.POST[key]
    return field

このように、items[インデックス][フィールド名] 形式のフィールド名を解析して、必要なデータ構造を構築します。
ただし、この例では動的に増えるフォームフィールドに対応するための一般的なアプローチを示していますが、実際のプロジェクトの詳細に応じて適宜調整が必要になります。

試してみてください。

atnek
1年以上前

はるさん

ありがとうございます。
ご教示いただきました方法(フィールド名をパースしてからの検索)にてうまくいきました!

dictにkeyが存在しない場合のエラーを想定 + 重複する記述を省くために下記のように変更して書いてみました。

# inputのnameは row[(int)][(str)]にて設定
# key.strip(']')で吐き出す結果が'row[(int)][(bill)'となってしまったので、正規表現にて抽出

fields = {}
parameters = request.POST
for key in parameters.keys():
	if key.startswith('row['):
		index, field = re.findall('\[(\d+)\]\[(.+)\]', key)[0]
		index = int(index)
		if 'key' in field:
			continue
		real_key = parameters[f'row[{index}][key]']
		if not real_key in fields :
			fields.setdefault(real_key, {})
		fields[real_key][field.lower()] = int(parameters[key])

何度も助けていただきありがとうございます m(_ _)m