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

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

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

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

Q&A

解決済

2回答

5000閲覧

djangoのformsetでtemplateにform.fieldと書いた場合の保存方法

keraker

総合スコア46

Django

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

0グッド

0クリップ

投稿2020/08/10 03:01

編集2020/08/11 02:53

追加
返信ありがとうございます。
https://blog.narito.ninja/detail/30
にあるお手本を用いて実装しようとしています。
この中のpost_formset.htmlの部分を以下のように変えたいです。

追加 
ありがとうございます。
参考にしたかたはgithubに出していらっしゃるのでそれをそのまま使っています。
https://github.com/naritotakizawa/django-modelformset-sample

djangoのformsetを使っています。
そのままのtemplateだと入力して送信をクリックすると、formset.save()は有効でデータベースに保存されます。そしてページを開きなおしても消えません。お手本どおりに動きます。

post_formset.html

1{% extends 'app/base.html' %} 2 3{% block content %} 4<form action="" method="post"> 5 <div class="row"> 6 {% for form in formset %} 7 <div class="col-sm-4"> 8 {{ form.as_p }} 9 </div> 10 {% endfor %} 11 </div> 12 {{ formset.management_form }} 13 {% csrf_token %} 14 <button type="submit" class="btn btn-primary">送信</button> 15</form> 16{% endblock %}

しかし以下のように変更してみるとformset.save()が反映されていないようで、ページを開きなおすと消えてしまいます。

{% extends 'app/base.html' %} {% block content %} <form action="" method="post"> <div class="row"> {% for form in formset %} <div class="col-sm-4"> {{ form.title }} {{ form.text }} {{ form.date }} </div> {% endfor %} </div> {{ formset.management_form }} {% csrf_token %} <button type="submit" class="btn btn-primary">送信</button> </form> {% endblock %}

view

from django.shortcuts import render, redirect from .forms import PostCreateFormSet def add(request): formset = PostCreateFormSet(request.POST or None) if request.method == 'POST' and formset.is_valid(): formset.save() return redirect('app:index') context = { 'formset': formset } return render(request, 'app/post_formset.html', context)

form

from django import forms from .models import Post class PostCreateForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for field in self.fields.values(): field.widget.attrs['class'] = 'form-control' class Meta: model = Post fields = '__all__' # これがモデルフォームセット PostCreateFormSet = forms.modelformset_factory( Post, form=PostCreateForm, extra=3 )

model

from django.db import models from django.utils import timezone class Post(models.Model): title = models.CharField('タイトル', max_length=200) text = models.TextField('本文') date = models.DateTimeField('日付', default=timezone.now) def __str__(self): return self.title

form.fieldとした場合の保存方法をご存知の方はいらっしゃいませんか。
よろしくお願いします。

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2020/08/10 14:42

ちょっと情報が足りなすぎてよく分かってませんが、ModelFormSetなら、form.fieldもModelなのでは? Modelならsave()ってあった気がするけど、、、 質問するならもっと具体的に動くものをサンプルでも作って載せないと回答付かないと思います。 でなければ、聞きたい部分を絶対他の意味に取れないくらい詳細に説明して、部分ソースを載せるくらいかな。今回はソースの量も少ない上に説明がほぼないため、こういう問答が必要になることが予想されるので、敬遠されちゃったんだと思いますよ。
退会済みユーザー

退会済みユーザー

2020/08/10 20:57 編集

削除しました
keraker

2020/08/10 23:30

返信ありがとうございます。 修正しました。
退会済みユーザー

退会済みユーザー

2020/08/10 23:59

すみませんが、情報が全く足りていません。 「以下のようなtemplateだと」ではなくて、完全なソースコードを貼ってください。 また、有効かどうかの判断は具体的にどうやって判断していますか?(デバッガで何行目に止まったからとか、DBで具体的にこう調べたら具体的にこうだったとか、それ以外の方法でどうだったとか) さらに「保存される」「formset.save()が反映」というのもどうやって判断したのか分かりません(DBを見てそう判断したのか、画面上何らかの方法で判断したのか、デバッガで追ったのかなど)。 保存方法もどこでどう保存しているのか全く書かれていません。 ようは、既存のものを変えたのであれば、私が全く同じソースを書けるくらいまで情報がないと、あなたが何を間違えたのかの情報にどうやっても辿り着かないのです。私はそれを推測しながら、小出しにされる情報から何度も何度も手探りで探したくないわけです。 私は回答側にも書いたとおり、誰がやっても同じ結果になるコードを書いています。同じコードを動かせるので、どこを間違えたのか手探りする必要もありません。言ってることが伝わるといいのですが。。。
退会済みユーザー

退会済みユーザー

2020/08/11 00:23

ちなみに私の書いたコードだとindex.htmlが質問中最初のコードブロックに近く(ただしあなたのコードはそのままでは動きません)、index2.htmlが次のコードブロックに近いです(ただしあなたのコードはそのままでは動きません)。そして、ちゃんとどちらもviews.pyで保存されています。ご参考までに。 viewの書き方的には紹介頂いたサイトの書き方が一般的ですよ。ただ、initialを使った初期化コードはそのままでclean()が動くのかよく分かりません。あなたの情報だけだとそれ以外の未確定要因でいくらでも落ちれるので、見当も付きませんが。
退会済みユーザー

退会済みユーザー

2020/08/11 02:09

そのまま使ってるんなら変えた部分のソースだけ全部貼ればいいでしょ... 質問する人がそんなに適当なのに、なんで回答する人が全部考えて要求しないと何もしないのか不思議です。
guest

回答2

0

ベストアンサー

オブジェクトのidが送られていなかったからです。
今回のモデルにはprimary keyとなるfiledがなかったので、自動的にidというフィールドが追加されています。

https://docs.djangoproject.com/ja/3.1/topics/db/models/#automatic-primary-key-fields

以下のように最後に1行追加すると動くはずです。

app/templates/app/post_formset.html

html

1{% extends 'app/base.html' %} 2 3{% block content %} 4<form action="" method="post"> 5 <div class="row"> 6 {% for form in formset %} 7 <div class="col-sm-4"> 8 {{ form.title }} 9 {{ form.text }} 10 {{ form.date }} 11 {{ form.id }} <!-- これが必要 --> 12 </div> 13 {% endfor %} 14 </div> 15 {{ formset.management_form }} 16 {% csrf_token %} 17 <button type="submit" class="btn btn-primary">送信</button> 18</form> 19{% endblock %}

投稿2020/08/11 03:27

編集2020/08/11 03:44
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

keraker

2020/08/11 04:07

ありがとうございます。 拝見したところ、 modelのほうのfieldのどれかにprimary_key=Trueを追加することでも解決できそうですね。 お手数おかけしました。
keraker

2020/08/11 04:08

参考資料もありがとうございます。
退会済みユーザー

退会済みユーザー

2020/08/11 04:18

> 拝見したところ、 modelのほうのfieldのどれかにprimary_key=Trueを追加することでも解決できそうですね。 これは機械的に考えればそうなんだけど、primary keyはuniqueなので、唯一の値でないといけない。 例えばtitleをprimary keyにしたとすると、同じタイトルの投稿ができなくなってしまうので、現実的には無理で、他も何かと都合が悪かったりします。まあ仮定の話なので、論理的には主キーにしても問題は解決しますね。 なお、このレベルの問題は普通に生成HTMLを見比べるだけで見つかるので、自分で解決してほしいです。
guest

0

回答ではありませんが、インデントがないと書けないので、こちらに。
質問がよく分からなかったので、ドキュメントに書いてあるとおりのModelFormSetを使ったコードを書いてみました。コードそのものを載せるとdjangoが作った部分と混ざって嵩張るので、venv環境構築とパッチを行うシェルスクリプトになっています。基本的なコマンドが揃っているUnix系OSなら動くと思います。

スクリプトの使い方

create_env.shとpatch.diffを両方空のディレクトリに保存してください。
コンソールから、そのディレクトリに行き、

sh

1. create_env.sh

で以下の処理を行います。

  1. venv仮想環境を構築
  2. pipで仮想環境内にdjangoをインストール
  3. djangoでmysiteプロジェクト作成
  4. djangoでmyappアプリ作成
  5. patch.diffパッチを当てます
  6. djangoでmigrateを実施します
  7. djangoでrunserverを実施します

あとはブラウザから確認するだけです。

http://localhost:8000/myapp/ ← index.htmlテンプレートを使ったレンダリング
http://localhost:8000/myapp/index2.html ← index2.htmlテンプレートを使ったレンダリング

※スクリプト内でgitで適当にcommitしたり、wgetで.gitignoreを取ってきたりもしています。

使い方の分からない人やUnix系の環境がない方は、パッチを見て、パッチを当てたコードを想像してください。

create_env.sh

bash

1#!/bin/bash 2PYTHON=python3 3if ! command -v $PYTHON; then 4 PYTHON=python 5fi 6 7$PYTHON -m venv env 8. env/bin/activate 9 10pip install django==3.1 11django-admin startproject mysite 12cd mysite 13django-admin startapp myapp 14 15if command -v git; then 16 git init 17 wget -O .gitignore https://github.com/github/gitignore/raw/master/Python.gitignore 18 git add . 19 git commit -m 'initial commit' 20fi 21 22patch -p1 <../patch.diff 23 24if command -v git; then 25 git add . 26 git commit -m 'patched' 27fi 28 29python manage.py makemigrations myapp 30python manage.py migrate 31 32if command -v git; then 33 git add . 34 git commit -m 'migrated' 35fi 36 37python manage.py runserver

patch.diff

diff

1diff --git a/myapp/forms.py b/myapp/forms.py 2new file mode 100644 3index 0000000..70a517f 4--- /dev/null 5+++ b/myapp/forms.py 6@@ -0,0 +1,4 @@ 7+from django.forms import ModelForm, modelformset_factory, BaseModelFormSet 8+from .models import Model1 9+ 10+ModelFormSet1 = modelformset_factory(Model1, fields=('field1', 'field2')) 11diff --git a/myapp/models.py b/myapp/models.py 12index 71a8362..a2df697 100644 13--- a/myapp/models.py 14+++ b/myapp/models.py 15@@ -1,3 +1,6 @@ 16 from django.db import models 17 18-# Create your models here. 19+class Model1(models.Model): 20+ field1 = models.CharField(max_length=200) 21+ field2 = models.CharField(max_length=200) 22+ 23diff --git a/myapp/templates/index.html b/myapp/templates/index.html 24new file mode 100644 25index 0000000..b6dedea 26--- /dev/null 27+++ b/myapp/templates/index.html 28@@ -0,0 +1,8 @@ 29+<h1>formsetのテスト</h1> 30+<form method="post"> 31+ {% csrf_token %} 32+ <table> 33+ {{ formset }} 34+ </table> 35+ <button type="submit">submit</button> 36+</form> 37diff --git a/myapp/templates/index2.html b/myapp/templates/index2.html 38new file mode 100644 39index 0000000..1220bdc 40--- /dev/null 41+++ b/myapp/templates/index2.html 42@@ -0,0 +1,14 @@ 43+<h1>formsetのテスト</h1> 44+<form method="post"> 45+ {% csrf_token %} 46+ <table> 47+ {{ formset.management_form }} 48+ {% for form in formset %} 49+ {% for field in form %} 50+ {{ field.label_tag }} {{ field }} 51+ {% endfor %} 52+ {% endfor %} 53+ </table> 54+ <button type="submit">submit</button> 55+</form> 56+ 57diff --git a/myapp/urls.py b/myapp/urls.py 58new file mode 100644 59index 0000000..0d74f3d 60--- /dev/null 61+++ b/myapp/urls.py 62@@ -0,0 +1,8 @@ 63+from django.urls import path 64+ 65+from . import views 66+ 67+urlpatterns = [ 68+ path('', views.index, name='index'), 69+ path('index2.html', views.index, name='index2'), 70+] 71diff --git a/myapp/views.py b/myapp/views.py 72index 91ea44a..6f8b7d9 100644 73--- a/myapp/views.py 74+++ b/myapp/views.py 75@@ -1,3 +1,18 @@ 76 from django.shortcuts import render 77+from .forms import ModelFormSet1 78+ 79+def index(request): 80+ return index_common(request, 'index.html') 81+ 82+def index2(request): 83+ return index_common(request, 'index2.html') 84+ 85+def index_common(request, template): 86+ if request.method == 'POST': 87+ formset = ModelFormSet1(request.POST) 88+ if formset.is_valid(): 89+ formset.save() 90+ else: 91+ formset = ModelFormSet1() 92+ return render(request, template, {'formset': formset}) 93 94-# Create your views here. 95diff --git a/mysite/settings.py b/mysite/settings.py 96index 1242d49..3506986 100644 97--- a/mysite/settings.py 98+++ b/mysite/settings.py 99@@ -31,6 +31,7 @@ ALLOWED_HOSTS = [] 100 # Application definition 101 102 INSTALLED_APPS = [ 103+ 'myapp.apps.MyappConfig', 104 'django.contrib.admin', 105 'django.contrib.auth', 106 'django.contrib.contenttypes', 107diff --git a/mysite/urls.py b/mysite/urls.py 108index e7f9f35..b41a8b1 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 path, include 117 118 urlpatterns = [ 119+ path('myapp/', include('myapp.urls')), 120 path('admin/', admin.site.urls), 121 ]

イメージ説明

投稿2020/08/10 19:08

編集2020/08/10 22:43
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問