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

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

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

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

Python 3.x

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

Q&A

1回答

5769閲覧

django modelでForeignKeyの設定にForm経由でデータを保存する方法

Meteor

総合スコア18

Django

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

Python 3.x

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

0グッド

0クリップ

投稿2019/01/04 09:12

APIで飛んできたデータを二つのモデルに分けて(Head,Detail)DBへ保存しています。
この二つのモデルにリレーションをかけると保存が上手く進まず躓きました。

【目標】
二つのデータをリレーションをかけて保存すること。

APIで一度に飛んでくるデータの構造は、

json

1transaction_send&params={ "data":[{ 2 "table_name":"TransactionHead", 3  "proc_detail_name":"register_transaction", 4  "rows":[{ 5       "transactionHeadId":"113", 6       "transactionDateTime":"2013-01-20 10:01:34",}] 7  },{ 8  "table_name":"TransactionDetail", 9  "proc_detail_name":"register_transaction", 10  "rows":[{ 11       "transactionHeadId":"113", 12       "transactionDateTime":"2013-01-20 10:01:34", 13       "transactionDetailId":"0",]} 14  } 15]}

このデータを保存する為のモデルは、

Django

1class TransactionHead(models.Model): 2 class Meta: 3 db_table = 'transaction_head' 4 transactionHeadId = models.BigIntegerField(verbose_name="取引ID", null=True) 5 transactionDateTime = models.DateTimeField(verbose_name="取引日時", null=True) 6 7class TransactionDetail(models.Model): 8 class Meta: 9 db_table = 'transaction_detail' 10 transactionHead = models.ForeignKey(TransactionHead, on_delete=models.CASCADE, default=1) 11 transactionHeadId = models.BigIntegerField(verbose_name="取引ID", null=True) 12 transactionDateTime = models.DateTimeField(verbose_name="取引日時", null=True) 13 transactionDetailId = models.IntegerField(verbose_name="取引明細ID", null=True) 14

データをDBに保存する為のコードが下記です。

django

1 2#jsonデータをモデルに保存していくコード 3     # TransactionHead 4 for hd in head_data: 5 head_form = HeadForm(hd) 6 if head_form.is_valid(): 7 head_form.save() 8 9 # TransactionDetail 10 for dd in detail_data: 11 detail_form = DetailForm(dd) 12 if detail_form.is_valid(): 13 detail_form.save() 14 detail_flag += 1 15

そして問題となっている箇所がFormなのですが、

class DetailForm(forms.ModelForm): class Meta: model = TransactionDetail fields = '__all__'   # transactionHeadId = forms.IntegerField(label="取引ID", required=False) transactionDateTime = forms.DateTimeField(label="取引日時", required=False)

いくつか試したことは、
①上記のfrom(ForeignKeyの部分を無くす)場合、headデータは保存されましたが、detailデータは保存されませんでした。
②上記の#を入れた箇所にtransactionHead = forms.ModelChoiceField(models.TransactionHead.objects)を入れるとエラーになりました。
③上記の#にforms.IntegerField()を入れましたが、同じくエラーになりました。

リレーションを掛けなかった場合、それぞれにデータが格納されたのは確認しているので、Formのこの箇所に問題があるのは理解できたのですが、解決策が全く見当がつきません。

お知恵をお借りできると幸いです。

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

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

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

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

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

guest

回答1

0

具体的などういうエラーでお困りなのかわからないので、想像でしかないですが、transactionHeadtransactionHeadIdがわかればモデルオブジェクトを取得できるので、あえて ModelFormの項目からは除外して(=validationの対象外になる)、最終的にsaveするタイミングで、自動的に transactionHeadをセットしてからsaveするのが良さそうに思いました。

class DetailForm(forms.ModelForm): class Meta: model = TransactionDetail # transactionHead は除外 fields = ("transactionHeadId", "transactionDateTime", "transactionDetailId") def save(self, commit=True): detail = super().save(commit=False) # 意図的にモデルをcommitしない detail.transactionHead = TransactionHead.objects.get( transactionHeadId=detail.transactionHeadId ) detail.save() return detail

save(commit=False) にすると DBには保存せずに、モデルオブジェクトだけを作ってreturnしてくれます。

参考: https://docs.djangoproject.com/ja/2.1/topics/forms/modelforms/#the-save-method

投稿2019/01/05 16:21

tell_k

総合スコア2120

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

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

Meteor

2019/01/05 16:34

ご丁寧にありがとうございます。 実は、この方法も考えたのですが、モデルの項目はかなり絞って3項目にしていますが、実際は50項目くらりありまして。。全部書くのも大変かなーと思い試してはいませんでした。 別のシステムからAPIでデータを飛ばして、それを保存する処理が最後まで行けなければ「失敗」で終わると言う事して確認できておらず、どのようなエラーとなったか確認できて(方法の知識がなく)いませんでした。送信が「成功」となった時にDBを確認するとデータが格納されていないと言う確認方法で進めてきました。 モデルフォームを利用して、データを通すフィールドが無ければデータがセーブされないと言う仕様になっているのでしょうか?
tell_k

2019/01/05 18:36

はい。そうです。fields に 指定しなければ、そのフィールドはFormに存在しないことになります。Formに存在しない以上、モデルの該当フィールド(カラム)にも何もセットされません
Meteor

2019/01/06 02:41

説明不足で申し訳ありません。 例えば、保存したいデータが10項目あったとします。 しかし、formで指定したフィールドが__all__としているのにフィールド数が9項目だったとします。 今回と同じ現象ではあるのですが、一つでもフィールドが足りなかったら、その足りないフィールドだけの値が保存されないだけではなく、全てのデータが保存されないという認識で大丈夫でしょうか?
tell_k

2019/01/06 10:32

> formで指定したフィールドが__all__としているのにフィールド数が9項目だったとします。 fields = "__all__" と書いたら、フィールド数は10項目になるんじゃないでしょうか? 9項目になるという話がよくわかりませんでした。ModelForm は、フォームフィールドの定義を自分でもしなくても、裏で勝手にモデルに応じたフィールドを生成してくれます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問