今回、DjangoでFormViewからSQLiteにデータを保存してみたので投稿します。
まずは完成版から。最低限必要なことを記載したいのでアプリの作成やプロジェクトでの設定は後ほど記載します。
フォームを表示するテンプレートを作成します。postが呼ばれたときの動作を定義します。csrf_tokenはセキュリティ上必要なもので、無いとエラーになります。form.as_pでforms.pyで定義している項目を全てp要素として表示します。クリックするとpostを実行するボタンを作成します。
#アプリ名/forms.html
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">送信</button>
{/form}
データベースに保存されたデータを表示するテンプレートを作成します。object_listはDjangoで準備されている変数なのでそのまま使用します。todo_listはこちらで定義した任意の変数でobject_listに格納された配列をtodo_listに渡してforループで使用します。
#アプリ名/list.html
<h1>List</h1>
<table>
<thead>
<tr><th>todo</th><th>date</th><th>detail</th></tr>
</thead>
<tbody>
{% for todo_list
in object_list %}
<tr>
<td>
{{ todo_list
.todo }}
</td>
<td>
**{{ todo_list
.date }}**
</td>
<td>
{{ todo_list
.detail }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
form.htmlでpostが成功した時に表示するテンプレートを作成します。
#アプリ名/reverse.html
<h1>Reverse</h1>
データベースのテーブルと連携するためにモデルを編集します。
#アプリ名/models.py
from django.db import models
class ToDoModel
(models.Model):
todo
= models
.CharField(max_length=20)
detail
= models
.CharField(max_length=200)
date
= models
.DateField()
フォームを作成してhtmlに表示する項目を定義します。また、フォームに入力されたデータをデータベースに保存するためにsaveメソッドにモデルとの連携の設定をします。
#アプリ名/forms.py
from django import forms
from . import models
class ToDoForm
(forms.Form):
todo
= forms
.CharField()
detail
= forms
.CharField()
date
= forms
.DateField()
def save
(self):
data
= self.cleaned_data
models
.ToDoModel
(
todo
= data
['todo
'],
detail
= data
['detail
'],
date
= data
['date
'],
).save()
上で作成したテンプレートとモデル、フォームをビューに設定します。
#アプリ名/views.py
from django.views import generic
from . import forms
, models
from django.urls import reverse_lazy
class Reverse
(generic
.TemplateView):
template_name = "アプリ名/reverse.html"
class ToDoView
(generic
.edit.FormView):
template_name = 'アプリ名/forms.html'
form_class = forms
.ToDoForm
success_url = reverse_lazy('reverse
')
def form_valid(self, form):
form.save
()
return super().form_valid(form)
class ToDoList
(generic
.ListView):
template_name = 'アプリ名/list.html'
model = models
.ToDoModel
ルーティングを設定します。
#アプリ名/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('reverse/',views.Reverse
.as_view(), name="reverse
"),
path('list/',views.ToDoList
.as_view()),
path('',views.ToDoView
.as_view()),
]
それでは細かく見ていきたいと思います。
django-admin startprojectでプロジェクトを作成した状態から始めます。
ターミナルでpython manage.py runserverを実行して、ブラウザにlocalhost:8000で接続した状態と、ディレクトリの構成は以下の様になります。
ターミナルでpython manage.py startappでアプリを作成します。アプリの名前はアプリ名とします。プロジェクト名、アプリ名は日本語のままだと不都合が起きかねないので適切な名前で作成してください。公式チュートリアルではmysiteがプロジェクト名、pollsがアプリ名になります。
プロジェクト名フォルダ内のsettings.pyのINSTALLED APPに「'アプリ名'」を登録し、urls.pyにアプリ名/urls.pyへのルーティングを記載します。
#プロジェクト名/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'アプリ名',
]
#プロジェクト名/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('', include('アプリ名.urls')),
path('admin/', admin.site.urls),
]
プロジェクト名フォルダ内での作業は以上です。
メニューに戻るアプリ名フォルダー内にtemplates/アプリ名/forms.htmlを作成します。VSCodeでは新しいファイル作成ボタンを押し「templates/アプリ名/forms.html」とすると一気に作成することができます。forms.htmlにはFormとだけ記載しておきましょう。後ほど内容は追加します。
アプリ名フォルダー内のviews.pyを編集します。動作確認をしながら進めるので、TemplateViewから作成します。
#アプリ名/views.py
from django.views import generic
class ToDoView(generic.TemplateView):
template_name='アプリ名/forms.html'
メニューに戻るアプリ名フォルダー内にurls.pyを作成し以下の様に記述します。
#アプリ名/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('',views.ToDoView.as_view()),
]
メニューに戻るターミナルでpython manage.py runserverを実行しブラウザでlocalhost:8000に接続するとFormと表示されます。
models.pyを編集してデータベースに作成するテーブルを定義します。とりあえずtodoというフィールドを一つだけ作成して後ほど増やしていきます。
#アプリ名/models.py
from django.db import models
class ToDoModel(models.Model):
todo = models.CharField(max_length=20)
models.pyのCharFieldではmax_lengthの指定が必須なります。最大文字数を20文字で設定します。
ターミナルでpython manage.py makemigrationsを実行した後にpython manage.py migrateを実行するとdb.sqlite3にapp_todomodelというテーブルが作成されます。
app
_todomodel
はapp
というアプリのmodels.pyにclassで定義されているToDoModel
という意味になります。
VSCodeにSQLite Viewerがインストールされていれば以下のような画面で確認することができます。
アプリ名フォルダー内にforms.pyを作成し以下のように記述します。
#アプリ名/forms.py
from django import forms
from . import models
class ToDoForm(forms.Form):
todo = forms.CharField()
forms.pyのCharFieldではmax_lengthの指定は必要ありません。
メニューに戻るアプリ名フォルダー内のviews.pyを編集します。
#アプリ名/views.py
from django.views import generic
from . import forms
class ToDoView(generic.edit.FormView
):
template_name='app/forms.html'
form_class = forms.ToDoForm
from . import formsでviews.pyと同じフォルダー内にあるforms.pyを呼び出します。TemplateViewで定義していたテンプレートをFormsViewに変更します。form_class = forms.ToDoFormでforms.pyに定義されているclassを呼び出します。
メニューに戻るFormとだけ記載していたforms.pyからFormを消して以下のように編集します。
#アプリ名/templates/アプリ名/forms.html
<form method="post">
{% csrf_token %}
{{ form.as_p }}
</form>
formがpostで呼ばれた時の挙動を定義します。csrf_tokenはセキュリティ上必須となる項目で記載しなければエラーになります。
その下の行で、先ほどforms.pyで定義したフォームを呼び出しています。.as_pはpタグで呼び出すという意味です。
メニューに戻るターミナルでpython manage.py runserverを実行するとブラウザにToDoのCharactor Fieldが表示されます。
さらにforms.htmlにButton要素を追加します。
#アプリ名/templates/アプリ名/forms.html
<form method="post">
{% csrf_token %}
{{ form.as_p }}
</form>
<button type="submit">送信</button>
データベースに保存する設定を行います。
ボタンをクリックしてデータの保存が成功した時に表示するページを作成します。forms.htmlと同じフォルダー内にreverse.htmlを作成し<h1>Reverse</h1>と記述します。
reverse.htmlを表示するためにviews.pyとurls.pyを編集します。
#アプリ名/views.py
from django.views import generic
from . import forms
class Reverse(generic.TemplateView):
template_name = "app/reverse.html"
class ToDoView(generic.edit.FormView):
template_name='app/forms.html'
form_class = forms.ToDoForm
#アプリ名/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('reverse/',views.Reverse.as_view(), name="reverse"),
path('',views.ToDoView.as_view()),
]
これでreverse.htmlのルーティングができました。
ToDoViewにはname属性をつけていませんでしたが、今回はname="reverse"を付けています。
views.pyを編集してデータの保存が成功した時の挙動としてreverse.htmlを表示する設定をsuccess_urlに行います。success_urlでは先ほどurls.pyでつけたname属性のreverseを指定しています。 次にform_validメソッドでデータの保存を設定します。
#アプリ名/views.py
from django.views import generic
from . import forms
from django.urls import reverse_lazy
class Reverse(generic.TemplateView):
template_name = "app/reverse.html"
class ToDoView(generic.edit.FormView):
template_name='app/forms.html'
form_class = forms.ToDoForm
success_url = reverse_lazy('reverse')
def form_valid(self, form):
form.save()
return super().form_valid(form)
forms.pyを編集します。
#アプリ名/forms.py
from django import forms
from . import models
class ToDoForm(forms.Form):
todo
= forms.CharField()
def save(self):
data = self.cleaned_data
models.ToDoModel(todo
= data['todo
']).save()
この時、models.ToDoModel(todo = data['todo'])の一つ目のtodoはmodels.pyで定義したtodo、二つ目のdata['todo']のtodoがforms.pyのtodoを表します。したがって、models.pyのToDoModelクラスのtodoにforms.pyのtodoに入力された値を代入するという意味になります。
Charactor Fieldに何かを入力してボタンをクリックします。
SQLiteにデータが保存されていることが確認できます。
保存した内容をリストで確認できるようにしていきます。
templates/アプリ名フォルダー内にlist.htmlを作成し、<h1>List</h1>と記載します。
views.pyとurls.pyを以下のように編集します。
#アプリ名/views.py
from django.views import generic
from . import forms, models
from django.urls import reverse_lazy
class Reverse(generic.TemplateView):
template_name = "app/reverse.html"
class ToDoView(generic.edit.FormView):
template_name='app/forms.html'
form_class = forms.ToDoForm
success_url = reverse_lazy('reverse')
def form_valid(self, form):
form.save()
return super().form_valid(form)
class ToDoList(generic.ListView):
template_name = 'app/list.html'
model = models.ToDoModel
#アプリ名/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('reverse/',views.Reverse.as_view(), name="reverse"),
path('list/',views.ToDoList.as_view()),
path('',views.ToDoView.as_view()),
]
先ほど作成しておいたlist.htmlを以下の様に編集します。
#アプリ名/templates/アプリ名/list.html
<h1>List</h1>
{% for todo_list
in object_list
%}
{{ todo_list
.todo }}
{% endfor %}
ここでobject_list
はDjangoで定義されている変数です。models.pyからhtmlにデータを渡すために使用します。object_listに渡されたQuerySetをfor文でtodo_list
に代入して表示します。todo_listはこちらで定義した任意の変数になります。
ターミナルでpython manage.py runserverを実行して localhost:8000/list/ に接続すると、データベースに保存されたデータを表示すことが出来ました。
model.pyのToDoModelクラスにtodoというフィールドを一つだけ定義していましたので増やしていきたいと思います。models.pyとforms.pyを以下のように編集します。
#アプリ名/models.py
from django.db import models
class ToDoModel(models.Model):
todo = models.CharField(max_length=20)
detail = models.CharField(max_length=200)
date = models.DateField()
ターミナルでmakemigrationsを実行すると以下のようなメッセージが表示されます。
#ターミナル
It is impossible to add a non-nullable field 'date' to todomodel without specifying a default. This is because the database needs something to populate existing rows.
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit and manually define a default value in models.py.
Select an option:
ターミナルでmakemigrationsを実行すると以下のようなメッセージが表示されます。新たなフィールドを背ってした際に、既に存在するレコードのデータをどうするかを聞かれています。
#ターミナル
It is impossible to add a non-nullable field 'detail' to todomodel without specifying a default. This is because the database needs something to populate existing rows.
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit and manually define a default value in models.py.
Select an option:
Select an option:の後に 1 を入力して実行すると>>>と表示されます。今回は日付を聞かれていますので'2000-1-1'と入力して実行します。シングルクオーテーションマークは外さずにそのまま入力してください。するともう一度に下の様なメッセージが表示されて今度はdetailをどうするか聞かれています。同じくSelect an option:の後に 1 を入力して実行した後、''(シングルクオーテーションマーク2つ)と入力して実行します。
ターミナルに以下のようなメッセージが表示されれば成功です。Migrations for 'app':の後にもう一行表示されるのですが、表示が異なるかもしれないのであえて省いています。
#ターミナル
Migrations for 'app':
- Add field date to todomodel
- Add field detail to todomodel
次にターミナルでpython manage.py migrateを実行するとデータベース変更が適応されます。
先ほどターミナルから追加したdateの2000-01-01とdetailの''で指定した空データが確認できます。
forms.pyを編集します。
#アプリ名/forms.py
from django import forms
from . import models
class ToDoForm(forms.Form):
todo = forms.CharField()
detail = forms.CharField()
date = forms.DateField()
def save(self):
data = self.cleaned_data
models.ToDoModel(
todo = data['todo'],
detail = data['detail'],
date = data['date']
).save()
ブラウザでlocalhost:8000に接続すると以下のように入力欄が追加されています。
次に何かを入力してからボタンをクリックします。
日付の入力方法には注意してください。
入力されたデータを確認することが出来ます。
list.htmlを以下の様に編集します。
#アプリ名/list.html
<h1>List</h1>
{% for todo_list in object_list %}
{{ todo_list.todo }}
{{ todo_list.detail }}
{{ todo_list.date }}
{% endfor %}
ターミナルでpython manage.py runserverを実行してブラウザでlocalhost:8000/list/に接続するとデータベースに保存されたデータをListViewで確認できます。
これでは分かりにくいのでテーブル形式で表示してみたいと思います。
list.htmlを以下の様に編集します。
#アプリ名/list.html
<h1>List</h1>
<table>
<thead>
<tr><th>todo</th><th>date</th><th>detail</th></tr>
</thead>
<tbody>
{% for todo_list in object_list %}
<tr>
<td>
{{ todo_list.todo }}
</td>
<td>
{{ todo_list.date }}
</td>
<td>
**
{{ todo_list.detail }}**
</td>
</tr>
{% endfor %}
</tbody>
</table>
ブラウザでlocalhost:8000/listでアクセスすると以下の様に表示されます。
少しだけ見やすくなりましたかね?
メニューに戻る以上、FormViewからデータをデータベースに保存してみましたので投稿いたしました。
分かりにくい部分などありましたらお知らせ頂けると助かります。お知らせいただいた内容は公開されることはありません。どの記事から投稿されたかは分かるようにしています。