Python | Django | モデルフォームの使い方
公開日:2021/7/12
Pythonには,DjangoというWebアプリケーションフレームワークがある.フレームワークのため,Djangoを利用するとWebアプリを通常よりも短時間で開発することが可能になる.
前記事にて,「フォームにおけるバリデーションの使い方」を記した.前記事での設定をそのまま引き継いだ上で,本記事では,「モデルフォームの使い方」を以下4つの構成にて記す.
- モデルフォームの基本実装
- DB(データベース)へのデータ追加処理
- ページのフォーマットを変更
(1) “fields"を利用
(2) “exclude"を利用
(3) テキストボックスからテキストエリアへの変更 - Saveメソッドの利用
(1) 入力内容を変更(大文字から小文字に変更)
(2) 様々なFormに共通利用できるログ出力の実装
◆実施環境
Python 3.8.8
Django 3.2.3
■モデルフォーム(ModelForm)の使い方
“form_app/models.py"に追記し,以下のように編集する.
from django.db import models
class Bird(models.Model): # 以下追記箇所
birdname = models.CharField(max_length=50)
title = models.CharField(max_length=100)
review = models.CharField(max_length=300)
author = models.CharField(max_length=50)
“form_app/forms.py"に追記し,以下のように編集する.
from django import forms
from django.core import validators
from .models import Bird # 追記箇所
class ChocolateInfo(forms.Form):
chocolate_name = forms.CharField(label='チョコレート名', max_length=50)
chocolate_maker = forms.CharField(label='チョコレートメーカー', max_length=50)
mail = forms.EmailField(label='メールアドレス')
confirmed_mail = forms.EmailField(label='メールアドレス再入力')
is_available_for_sale = forms.BooleanField(label='販売中')
birthday = forms.DateField(required=False, label='開発日')
price = forms.DecimalField(initial=300, max_value=1000, label='価格',validators=[validators.MinValueValidator(1, message='1以上にしてください')])
flavor = forms.ChoiceField(choices=(
(1, 'Original Chocolat'),
(2, 'Double Chocolat'),
(3, 'Original White'),
(4, 'Rich Matcha'),
), widget=forms.RadioSelect, label='味')
maker_factory = forms.MultipleChoiceField(choices=(
(1, 'Ibaraki Moriya-city'),
(2, 'Saitama Sakado-city'),
(3, 'Aichi Inazawa-city'),
(4, 'Osaka Takatsuki-city'),
), widget=forms.CheckboxSelectMultiple, label='メーカー工場')
homepage = forms.URLField(
label='ホームページ',
widget=forms.TextInput(attrs={'class':'url_class','placeholder':'https://www.shelokuma.com'})
)
def __init__(self,*args,**kwargs):
super(ChocolateInfo, self).__init__(*args, **kwargs)
self.fields['flavor'].widget.attrs['id'] = 'id_flavor'
self.fields['maker_factory'].widget.attrs['class'] = 'maker_factory_class'
def clean_chocolatename(self):
chocolate_name = self.cleaned_data['chocolate_name']
if not chocolate_name.startswith('F'):
raise forms.ValidationError('名前は「F」から始めてください')
def clean(self):
cleaned_data = super().clean()
mail = cleaned_data['mail']
confirmed_mail = cleaned_data['confirmed_mail']
if mail != confirmed_mail:
raise forms.ValidationError('メールアドレスが一致しません')
class BirdModelForm(forms.ModelForm): # 以下追記箇所
class Meta:
model = Bird
fields = '__all__'
“form_app/views.py"に追記し,以下のように編集する.
from django.shortcuts import render
from . import forms
def index(request):
return render(request, 'form_temp/index.html')
def form_page(request):
form = forms.ChocolateInfo()
if request.method == 'POST':
form = forms.ChocolateInfo(request.POST)
if form.is_valid():
print('submission is successful')
print(form.cleaned_data)
else:
print('submission is failed')
return render( request,'form_temp/form_page.html',
context={ 'form':form } )
def form_bird(request): #以下追記箇所
form = forms.BirdModelForm()
return render( request, 'form_temp/form_bird.html',
context={'form':form} )
“form_app/urls.py"に追記し,以下のように編集する.
from django. urls import path
from . import views
app_name =
'form_application'
urlpatterns = [
path('', views.index, name='index'),
path('form_page/', views.form_page, name='form_page'),
path('form_bird/', views.form_bird, name='form_bird'), # 追記箇所
]
“form_project/templates/form_temp"に以下のように"form_bird.html"を作成する.
“form_project/templates/form_temp/form_bird.html"を以下のように編集する.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Form</title>
{% load static %}
<link rel="stylesheet" href="{% static 'css/style.css' %}">
</head>
<body>
<form method="POST">
{% csrf_token %}
<table>
{{form.as_table}}
</table>
<input type="submit" value="submit">
</form>
</body>
</html>
ターミナルを開き,”conda activate 仮想環境名”を実行し,仮想環境に移行する(移行方法の詳細はこちら)."cd form_project"を実行することによって,”form_project”のディレクトリに移動する.その後,”python manage.py makemigrations form_app”を実行する.以下が出力される.
(djangoenv) C:\Users\shiro\Desktop\210517_python development\myformtest\form_project
>python manage.py makemigrations form_app
Migrations for 'form_app': # 以下出力箇所
form_app\migrations\0001_initial.py
- Create model Bird
引き続き,"python manage.py migrate"を実行する.以下が出力される.
(djangoenv) C:\Users\shiro\Desktop\210517_python development\myformtest\form_project
>python manage.py migrate
Operations to perform: # 以下出力箇所
Apply all migrations: admin, auth, contenttypes, form_app, sessions
Running migrations:
Applying form_app.0001_initial... OK
ターミナルで"python manage.py runserver"を実行する.以下が出力される.
(djangoenv) C:\Users\shiro\Desktop\210517_python development\myformtest\form_project
>python manage.py runserver
System check identified no issues (0 silenced). # 以下出力箇所
July 11, 2021 - 15:39:59
Django version 3.2.3, using settings 'form_project.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
上記の"http://127.0.0.1:8000″をクリックする.以下ページに遷移する.
ブラウザのURLに"http://127.0.0.1:8000/form_application/form_bird"を入力すると,以下ページに遷移する.
ページの項目に内容を記述し,"submit"をクリックする.
エラーがなく,ターミナルに以下が出力されると成功となる.
[11/Jul/2021 17:18:39]
"POST /form_application/form_bird/ HTTP/1.1" 200 925
“form_app/views.py"に追記し,以下のように編集する.
from django.shortcuts import render
from . import forms
def index(request):
return render(request,'form_temp/index.html')
def form_page(request):
form = forms.ChocolateInfo()
if request.method =='POST':
form = forms.ChocolateInfo(request.POST)
if form.is_valid():
print('submission is successful')
print(form.cleaned_data)
else:
print('submission is failed')
return render(
request,'form_temp/form_page.html',
context={'form':form})
def form_bird(request):
form = forms.BirdModelForm()
if request.method == 'POST': # 追記箇所(27~30行目)
form = forms.BirdModelForm(request.POST)
if form.is_valid():
form.save()
return render(
request, 'form_temp/form_bird.html', context={'form':form}
)
上記を保存し,ブラウザのURLに"http://127.0.0.1:8000/form_application/form_bird"を入力し,以下ページの項目に内容を記述し,"submit"をクリックする.
エラーがなく,ターミナルに以下が出力される.
[11/Jul/2021 17:36:14]
"POST /form_application/form_bird/ HTTP/1.1" 200 925
SQLiteをインストールし(詳細はこちら参照),"SQLITE EXPLORER"を開くと,"form_app_bird"を以下赤枠にて確認できる.右クリックをし,"Show Table"をクリックする.
以下のように入力データを確認できる.
“form_app/forms.py"を以下のように変更する.
from django import forms
from django.core import validators
from .models import Bird # 追記箇所
class ChocolateInfo(forms.Form):
chocolate_name = forms.CharField(label='チョコレート名', max_length=50)
chocolate_maker = forms.CharField(label='チョコレートメーカー', max_length=50)
mail = forms.EmailField(label='メールアドレス')
confirmed_mail = forms.EmailField(label='メールアドレス再入力')
is_available_for_sale = forms.BooleanField(label='販売中')
birthday = forms.DateField(required=False, label='開発日')
price = forms.DecimalField(initial=300, max_value=1000, label='価格',validators=[validators.MinValueValidator(1, message='1以上にしてください')])
flavor = forms.ChoiceField(choices=(
(1, 'Original Chocolat'),
(2, 'Double Chocolat'),
(3, 'Original White'),
(4, 'Rich Matcha'),
), widget=forms.RadioSelect, label='味')
maker_factory = forms.MultipleChoiceField(choices=(
(1, 'Ibaraki Moriya-city'),
(2, 'Saitama Sakado-city'),
(3, 'Aichi Inazawa-city'),
(4, 'Osaka Takatsuki-city'),
), widget=forms.CheckboxSelectMultiple, label='メーカー工場')
homepage = forms.URLField(
label='ホームページ',
widget=forms.TextInput(attrs={'class':'url_class','placeholder':'https://www.shelokuma.com'})
)
def __init__(self,*args,**kwargs):
super(ChocolateInfo, self).__init__(*args, **kwargs)
self.fields['flavor'].widget.attrs['id'] = 'id_flavor'
self.fields['maker_factory'].widget.attrs['class'] = 'maker_factory_class'
def clean_chocolatename(self):
chocolate_name = self.cleaned_data['chocolate_name']
if not chocolate_name.startswith('F'):
raise forms.ValidationError('名前は「F」から始めてください')
def clean(self):
cleaned_data = super().clean()
mail = cleaned_data['mail']
confirmed_mail = cleaned_data['confirmed_mail']
if mail != confirmed_mail:
raise forms.ValidationError('メールアドレスが一致しません')
class BirdModelForm(forms.ModelForm):
class Meta:
model = Bird
fields = ['birdname','title','review'] # 変更箇所
上記を保存し,ブラウザのURLに"http://127.0.0.1:8000/form_application/form_bird"を入力すると,以下ページに遷移する.項目は,4項目から3項目に変更した.
“form_app/forms.py"を以下のように変更する.
from django import forms
from django.core import validators
from .models import Bird # 追記箇所
class ChocolateInfo(forms.Form):
chocolate_name = forms.CharField(label='チョコレート名', max_length=50)
chocolate_maker = forms.CharField(label='チョコレートメーカー', max_length=50)
mail = forms.EmailField(label='メールアドレス')
confirmed_mail = forms.EmailField(label='メールアドレス再入力')
is_available_for_sale = forms.BooleanField(label='販売中')
birthday = forms.DateField(required=False, label='開発日')
price = forms.DecimalField(initial=300, max_value=1000, label='価格',validators=[validators.MinValueValidator(1, message='1以上にしてください')])
flavor = forms.ChoiceField(choices=(
(1, 'Original Chocolat'),
(2, 'Double Chocolat'),
(3, 'Original White'),
(4, 'Rich Matcha'),
), widget=forms.RadioSelect, label='味')
maker_factory = forms.MultipleChoiceField(choices=(
(1, 'Ibaraki Moriya-city'),
(2, 'Saitama Sakado-city'),
(3, 'Aichi Inazawa-city'),
(4, 'Osaka Takatsuki-city'),
), widget=forms.CheckboxSelectMultiple, label='メーカー工場')
homepage = forms.URLField(
label='ホームページ',
widget=forms.TextInput(attrs={'class':'url_class','placeholder':'https://www.shelokuma.com'})
)
def __init__(self,*args,**kwargs):
super(ChocolateInfo, self).__init__(*args, **kwargs)
self.fields['flavor'].widget.attrs['id'] = 'id_flavor'
self.fields['maker_factory'].widget.attrs['class'] = 'maker_factory_class'
def clean_chocolatename(self):
chocolate_name = self.cleaned_data['chocolate_name']
if not chocolate_name.startswith('F'):
raise forms.ValidationError('名前は「F」から始めてください')
def clean(self):
cleaned_data = super().clean()
mail = cleaned_data['mail']
confirmed_mail = cleaned_data['confirmed_mail']
if mail != confirmed_mail:
raise forms.ValidationError('メールアドレスが一致しません')
class BirdModelForm(forms.ModelForm):
class Meta:
model = Bird
exclude = ['birdname'] # 変更箇所
上記を保存し,ブラウザのURLに"http://127.0.0.1:8000/form_application/form_bird"を入力すると,以下ページに遷移する.項目は,4項目から3項目に変更した.
“form_app/forms.py"を以下のように変更する.
from django import forms
from django.core import validators
from .models import Bird # 追記箇所
class ChocolateInfo(forms.Form):
chocolate_name = forms.CharField(label='チョコレート名', max_length=50)
chocolate_maker = forms.CharField(label='チョコレートメーカー', max_length=50)
mail = forms.EmailField(label='メールアドレス')
confirmed_mail = forms.EmailField(label='メールアドレス再入力')
is_available_for_sale = forms.BooleanField(label='販売中')
birthday = forms.DateField(required=False, label='開発日')
price = forms.DecimalField(initial=300, max_value=1000, label='価格',validators=[validators.MinValueValidator(1, message='1以上にしてください')])
flavor = forms.ChoiceField(choices=(
(1, 'Original Chocolat'),
(2, 'Double Chocolat'),
(3, 'Original White'),
(4, 'Rich Matcha'),
), widget=forms.RadioSelect, label='味')
maker_factory = forms.MultipleChoiceField(choices=(
(1, 'Ibaraki Moriya-city'),
(2, 'Saitama Sakado-city'),
(3, 'Aichi Inazawa-city'),
(4, 'Osaka Takatsuki-city'),
), widget=forms.CheckboxSelectMultiple, label='メーカー工場')
homepage = forms.URLField(
label='ホームページ',
widget=forms.TextInput(attrs={'class':'url_class','placeholder':'https://www.shelokuma.com'})
)
def __init__(self,*args,**kwargs):
super(ChocolateInfo, self).__init__(*args, **kwargs)
self.fields['flavor'].widget.attrs['id'] = 'id_flavor'
self.fields['maker_factory'].widget.attrs['class'] = 'maker_factory_class'
def clean_chocolatename(self):
chocolate_name = self.cleaned_data['chocolate_name']
if not chocolate_name.startswith('F'):
raise forms.ValidationError('名前は「F」から始めてください')
def clean(self):
cleaned_data = super().clean()
mail = cleaned_data['mail']
confirmed_mail = cleaned_data['confirmed_mail']
if mail != confirmed_mail:
raise forms.ValidationError('メールアドレスが一致しません')
class BirdModelForm(forms.ModelForm):
review = forms.CharField( # 追記箇所(50~55行目)
widget=forms.Textarea(attrs={
'rows':15,
'cols':25,
})
)
class Meta:
model = Bird
fields = '__all__'
上記を保存し,ブラウザのURLに"http://127.0.0.1:8000/form_application/form_bird"を入力すると,以下ページに遷移する.項目数は4項目に戻り,項目"Review"の入力エリアは拡大した.
“form_app/forms.py"を以下のように変更する.
from django import forms
from django.core import validators
from .models import Bird
class ChocolateInfo(forms.Form):
chocolate_name = forms.CharField(label='チョコレート名', max_length=50)
chocolate_maker = forms.CharField(label='チョコレートメーカー', max_length=50)
mail = forms.EmailField(label='メールアドレス')
confirmed_mail = forms.EmailField(label='メールアドレス再入力')
is_available_for_sale = forms.BooleanField(label='販売中')
birthday = forms.DateField(required=False, label='開発日')
price = forms.DecimalField(initial=300, max_value=1000, label='価格',validators=[validators.MinValueValidator(1, message='1以上にしてください')])
flavor = forms.ChoiceField(choices=(
(1, 'Original Chocolat'),
(2, 'Double Chocolat'),
(3, 'Original White'),
(4, 'Rich Matcha'),
), widget=forms.RadioSelect, label='味')
maker_factory = forms.MultipleChoiceField(choices=(
(1, 'Ibaraki Moriya-city'),
(2, 'Saitama Sakado-city'),
(3, 'Aichi Inazawa-city'),
(4, 'Osaka Takatsuki-city'),
), widget=forms.CheckboxSelectMultiple, label='メーカー工場')
homepage = forms.URLField(
label='ホームページ',
widget=forms.TextInput(attrs={'class':'url_class','placeholder':'https://www.shelokuma.com'})
)
def __init__(self,*args,**kwargs):
super(ChocolateInfo, self).__init__(*args, **kwargs)
self.fields['flavor'].widget.attrs['id'] = 'id_flavor'
self.fields['maker_factory'].widget.attrs['class'] = 'maker_factory_class'
def clean_chocolatename(self):
chocolate_name = self.cleaned_data['chocolate_name']
if not chocolate_name.startswith('F'):
raise forms.ValidationError('名前は「F」から始めてください')
def clean(self):
cleaned_data = super().clean()
mail = cleaned_data['mail']
confirmed_mail = cleaned_data['confirmed_mail']
if mail != confirmed_mail:
raise forms.ValidationError('メールアドレスが一致しません')
class BirdModelForm(forms.ModelForm):
review = forms.CharField(
widget=forms.Textarea(attrs={
'rows':15,
'cols':25,
})
)
class Meta:
model = Bird
fields = '__all__'
def save(self, *args, **kwargs): # 以下追記箇所
ipt = super(BirdModelForm, self).save(commit=False, *args, **kwargs)
ipt.birdname = ipt.birdname.lower()
print(type(ipt))
print('save is successful')
ipt.save()
return ipt
上記を保存し,ブラウザのURLに"http://127.0.0.1:8000/form_application/form_bird"を入力すると,以下画面に遷移する.内容を記述し,"submit"をクリックする."Birdname"の項目に記載した内容は,大文字で"BUDGERIGAR"である
ターミナルには以下が出力される.
<class 'form_app.models.Bird'>
save is successful
[12/Jul/2021 01:03:37]
"POST /form_application/form_bird/ HTTP/1.1" 200 1014
上記のように,SQLiteをインストールし,"SQLITE EXPLORER"を開き,"form_app_bird"を右クリックをし,"Show Table"をクリックする.以下赤枠の内容を確認すると,"Birdname"の項目に記載した大文字の内容は,小文字の"budgerigar"になった.
“form_app/forms.py"を以下のように変更する.クラス"BaseForm"を追記し,ログが出力できるようにした.また,"BirdModelForm"が"BaseForm"を継承できるようにした.新たなFormを作成する際,"BaseForm"経由にすることで共通の機能(50行目のprint)を持つことができる.
from django import forms
from django.core import validators
from .models import Bird
class ChocolateInfo(forms.Form):
chocolate_name = forms.CharField(label='チョコレート名', max_length=50)
chocolate_maker = forms.CharField(label='チョコレートメーカー', max_length=50)
mail = forms.EmailField(label='メールアドレス')
confirmed_mail = forms.EmailField(label='メールアドレス再入力')
is_available_for_sale = forms.BooleanField(label='販売中')
birthday = forms.DateField(required=False, label='開発日')
price = forms.DecimalField(initial=300, max_value=1000, label='価格',validators=[validators.MinValueValidator(1, message='1以上にしてください')])
flavor = forms.ChoiceField(choices=(
(1, 'Original Chocolat'),
(2, 'Double Chocolat'),
(3, 'Original White'),
(4, 'Rich Matcha'),
), widget=forms.RadioSelect, label='味')
maker_factory = forms.MultipleChoiceField(choices=(
(1, 'Ibaraki Moriya-city'),
(2, 'Saitama Sakado-city'),
(3, 'Aichi Inazawa-city'),
(4, 'Osaka Takatsuki-city'),
), widget=forms.CheckboxSelectMultiple, label='メーカー工場')
homepage = forms.URLField(
label='ホームページ',
widget=forms.TextInput(attrs={'class':'url_class','placeholder':'https://www.shelokuma.com'})
)
def __init__(self,*args,**kwargs):
super(ChocolateInfo, self).__init__(*args, **kwargs)
self.fields['flavor'].widget.attrs['id'] = 'id_flavor'
self.fields['maker_factory'].widget.attrs['class'] = 'maker_factory_class'
def clean_chocolatename(self):
chocolate_name = self.cleaned_data['chocolate_name']
if not chocolate_name.startswith('F'):
raise forms.ValidationError('名前は「F」から始めてください')
def clean(self):
cleaned_data = super().clean()
mail = cleaned_data['mail']
confirmed_mail = cleaned_data['confirmed_mail']
if mail != confirmed_mail:
raise forms.ValidationError('メールアドレスが一致しません')
class BaseForm(forms.ModelForm): # 追記箇所(48~51行目)
def save(self, *args, **kwargs):
print(f'{self.__class__.__name__} is successful')
return super(BaseForm, self).save(*args,**kwargs)
class BirdModelForm(BaseForm): # 変更箇所
review = forms.CharField(
widget=forms.Textarea(attrs={
'rows':15,
'cols':25,
})
)
class Meta:
model = Bird
fields = '__all__'
def save(self, *args, **kwargs):
ipt = super(BirdModelForm, self).save(commit=False, *args, **kwargs)
ipt.birdname = ipt.birdname.lower()
print(type(ipt))
print('save is successful')
ipt.save()
return ipt
上記を保存し,ブラウザのURLに"http://127.0.0.1:8000/form_application/form_bird"を入力すると,以下画面に遷移する.内容を記述し,"submit"をクリックする.
ターミナルには以下が出力される.
BirdModelForm is successful
<class 'form_app.models.Bird'>
save is successful
[12/Jul/2021 15:21:28]
"POST /form_application/form_bird/ HTTP/1.1" 200 1011
以上