[Django3.0]エラーメッセージで辞書型のvalueを表示する書き方

forms.pyで、全角カナ以外の入力があった場合にname_kanaにエラーメッセージを割り当てています。

forms.py

	def clean_name_kana(self):
		name_kana = self.cleaned_data['name_kana']
		p = re.compile('[\u30A1-\u30F4]+')
		if not(p.fullmatch(name_kana)):
			raise forms.ValidationError("全角カナで入力してください。")

### keyが表示される駄目な例
view側で、form.errorsは辞書型のデータを取得するので、form.name_kana.errorsとすれば辞書型のvalueが表示されますが、errorだけだと、辞書型のkeyを表示します。
以下のように書くとエラーメッセージはname_kana となります。

client_input.html

{% for error in form.errors %}
{{ error }}
{% endfor %}

### 辞書型のvalueが表示される例
このように書くと、辞書型のkeyとvalueを取得できるので、指定したバリデーションメッセージを表示させる事ができます。

client_input.html

{% for key, value in form.errors.items %}
{{ value }}
{% endfor %}

これ修正するのに凄い時間かかった。もー

[Django3.0]Viewでのエラーメッセージの書き方

エラーメッセージがあった場合にはforループで回すのが一般的?

モデルで正規表現でカタカナのみ入力可としてる所で、漢字で入力してエラーメッセージを表示させる。

/sales/models.py


class Clients(models.Model):
	name = models.CharField(max_length=255)
	name_kana = models.CharField(max_length=255, null=True, blank=True, validators=[RegexValidator(r"\u30A1-\u30F4")])
	office = models.CharField(max_length=255, null=True, blank=True)
	department = models.CharField(max_length=255, null=True, blank=True)
	position = models.CharField(max_length=255, null=True, blank=True)
	charge = models.CharField(max_length=255)
	charge_mail = models.EmailField(max_length=255)
	zipcode = models.CharField(max_length=8, validators=[RegexValidator(r"\d{3}-\d{4}")])
	prefecture = models.CharField(max_length=20)
	address = models.CharField(max_length=255)
	tel = models.CharField(max_length=15)
	fax = models.CharField(max_length=15, null=True, blank=True)
	name_top = models.CharField(max_length=255)
	position_top = models.CharField(max_length=100, null=True, blank=True)
	remark = models.TextField(max_length=300, null=True, blank=True)
	created_at = models.DateTimeField(auto_now_add=True)
	updated_at = models.DateTimeField(auto_now=True)

/templates/sales/client_input.html

{% for error in form.errors %}
<div class="text-danger">
    {{ error }}
</div>
{% endfor %}

エラーメッセージを定義していないから、kana_nameだけ表示される。

class ClientsForm(forms.ModelForm):
	class Meta:
		model = Clients
		fields = ['name', 'name_kana', 'office', 'department', 'position', 'charge', 'charge_mail', 'zipcode', 'prefecture', 'address', 'tel', 'fax', 'name_top', 'position_top', 'remark']

	def clean_name_kana(self);
		name_kana = self.cleaned_data['name_kana']
		p = u'^[\u30A1-\u30F4]+$'
		if(re.match(p, name_kana))
			raise forms.ValidationError("全角カナで入力してください")
		return name_kana

上手くいかない。正規表現を変更する。

	def clean_name_kana(self):
		name_kana = self.cleaned_data['name_kana']
		p = re.compile('[\u30A1-\u30F4]+')
		if not(p.fullmatch(name_kana)):
			raise forms.ValidationError("全角カナで入力してください")
{% for error in form.errors %}
				<div class="text-danger">
				{{ form.name_kana.errors }}
			   </div>
				{% endfor %}

上手く行ったが、なんかモヤモヤするな。

[Django3.0]カタカナのバリデーション

まずER図とデータ型を編集しながらmodels.pyを作成します。

/sales/models.py

class Clients(models.Model):
	name = models.CharField(max_length=255)
	name_kana = models.CharField(max_length=255, null=True)
	office = models.CharField(max_length=255, null=True)
	department = models.CharField(max_length=255, null=True)
	position = models.CharField(max_length=255, null=True)
	charge = models.CharField(max_length=255)
	charge_mail = models.EmailField(max_length=255)
	zipcode = models.CharField(max_length=8, validators=[RegexValidator(r"\d{3}-\d{4}")])
	prefecture = models.CharField(max_length=20)
	address = models.CharField(max_length=255)
	tel = models.CharField(max_length=15)
	fax = models.CharField(max_length=15, null=True)
	name_top = models.CharField(max_length=255)
	position_top = models.CharField(max_length=100, null=True)
	remark = models.TextField(max_length=300, null=True)
	created_at = models.DateTimeField(auto_now_add=True)
	updated_at = models.DateTimeField(auto_now=True)

$ python manage.py makemigrations sales
$ python manage.py migrate
mysql> show tables;
mysql> describe sales_clients;
+————–+————–+——+—–+———+—————-+
| Field | Type | Null | Key | Default | Extra |
+————–+————–+——+—–+———+—————-+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| name_kana | varchar(255) | YES | | NULL | |
| office | varchar(255) | YES | | NULL | |
| department | varchar(255) | YES | | NULL | |
| position | varchar(255) | YES | | NULL | |
| charge | varchar(255) | NO | | NULL | |
| charge_mail | varchar(255) | NO | | NULL | |
| zipcode | varchar(8) | NO | | NULL | |
| prefecture | varchar(20) | NO | | NULL | |
| address | varchar(255) | NO | | NULL | |
| tel | varchar(15) | NO | | NULL | |
| fax | varchar(15) | YES | | NULL | |
| name_top | varchar(255) | NO | | NULL | |
| position_top | varchar(100) | YES | | NULL | |
| remark | longtext | YES | | NULL | |
| created_at | datetime(6) | NO | | NULL | |
| updated_at | datetime(6) | NO | | NULL | |
+————–+————–+——+—–+———+—————-+
18 rows in set (0.00 sec)

/sales/forms.py

class ClientsForm(forms.ModelForm):
	class Meta:
		model = Clients
		fields = ['name', 'name_kana', 'office', 'department', 'position', 'charge', 'charge_mail', 'zipcode', 'prefecture', 'address', 'tel', 'fax', 'name_top', 'position_top', 'remark']

/sales/views.py

from .models import Clients
form .forms import ClientsForms

def client_complete(request):
	if(request.method == 'POST'):
		name = request.POST['name']
		name_kana = request.POST['name_kana']
		office = request.POST['office']
		department = request.POST['department']
		position = request.POST['position']
		charge = request.POST['charge']
		charge_mail = request.POST['charge_mail']
		zipcode = request.POST['zipcode']
		prefecture = request.POST['prefecture']
		address = request.POST['address']
		tel = request.POST['tel']
		fax = request.POST['fax']
		name_top = request.POST['name_top']
		position_top = request.POST['position_top']
		remark = request.POST['remark']
		clients = Clients(name=name, name_kana=name_kana, office=office, department=department, position=position, charge=charge, charge_mail=charge_mail, zipcode=zipcode, prefecture=prefecture, address=address, tel=tel,fax=fax, name_top=name_top, position_top=position_top, remark=remark)
		clients.save()        
	return render(request, 'sales/client_complete.html')

def client_input(request):
	params = {
		'form': ClientsForm()
	}
	return render(request, 'sales/client_input.html', params)

/templates/sales/client_input.html
// 省略
-> django-widdget-tweaksではTextFieldは自動的にtextareaになるが、rowsを指定してあげる

{% render_field form.remark class="form-control" rows="2" placeholder="" %}

mysql> select * from sales_clients;
+—-+——————————————–+——————————–+——–+————+—————–+————–+————————-+———-+————+——————————-+————–+————–+————–+—————–+——————–+—————————-+—————————-+
| id | name | name_kana | office | department | position | charge | charge_mail | zipcode | prefecture | address | tel | fax | name_top | position_top | remark | created_at | updated_at |
+—-+——————————————–+——————————–+——–+————+—————–+————–+————————-+———-+————+——————————-+————–+————–+————–+—————–+——————–+—————————-+—————————-+
| 1 | ジャパンソフトウェア株式会社 | ジャパンソフトウェア | 本社 | 営業部 | 代表取締役 | 佐藤太郎 | staro@japansoftware.com | 100-0002 | 東京都 | 千代田区皇居外苑1-2-3 | 03-1234-5678 | 03-1234-5679 | 山本五郎 | 代表取締役 | | 2020-08-30 02:09:35.555187 | 2020-08-30 02:09:35.555259 |
| 2 | アジアテック株式会社 | アジアテック | 本社 | 営業部 | 課長 | 山本太郎 | yamamoto@asiatech.com | 100-0002 | 東京都 | 千代田区皇居外苑1-2-3 | 03-1234-5678 | 03-1234-5679 | 山田一郎 | 代表取締役 | 佐藤氏の紹介 | 2020-08-30 02:54:20.407693 | 2020-08-30 02:54:20.407756 |
+—-+——————————————–+——————————–+——–+————+—————–+————–+————————-+———-+————+——————————-+————–+————–+————–+—————–+——————–+—————————-+—————————-+
2 rows in set (0.00 sec)

zipcodeのバリデーションを\d{3}-\d{4}として、会社名カナのバリデーションを\u30A1-\u30F4とした場合に、form.is_valid():として複数のエラーメッセージを出し分ける方法がわからんな。

[Django3.0] ModelFormのバリデーション

Djangoでは、Forms.FormのバリデーションとModelFormのバリデーション2種類がある
ModelFormでのバリデーションについて考える。

文字数とnull, Emailは既にモデルに書かれているので、郵便番号を英数字とハイフンだけにする
正規表現で、\d{3}-\d{4}とする

/sales/models.py

from django.db import models
from django.core.validators import RegexValidator

class Master(models.Model):
	name = models.CharField(max_length=255)
	office = models.CharField(max_length=255, null=True)
	zipcode = models.CharField(max_length=8, validators=[RegexValidator(r"\d{3}-\d{4}")])
	prefecture = models.CharField(max_length=20)
	address = models.CharField(max_length=255)
	tel = models.CharField(max_length=15)
	fax = models.CharField(max_length=15, null=True)
	mail = models.EmailField(max_length=255)
	name_top = models.CharField(max_length=255)
	position_top = models.CharField(max_length=100, null=True)
	created_at = models.DateTimeField(auto_now_add=True)
	updated_at = models.DateTimeField(auto_now=True)

/sales/views.py

def master_complete(request):
	data = Master.objects.get(id=1)
	if(request.method == 'POST'):
		master = MasterForm(request.POST, instance=data)
		if(master.is_valid()):
			master.save()
			return render(request, 'sales/master_complete.html')
		else:
			params = {
				'form': MasterForm(instance=data),
				'message': "郵便番号は数字7桁とハイフンで入力してください"
			}
			return render(request, 'sales/master.html', params)
	return render(request, 'sales/master_complete.html')

/sales/templates/sales/master.html

<div class="text-danger">{{ message}}</div>

OK
自社基本情報の更新までできました。
続いて得意先の登録に行きます。

[Django]バリデータ関数の作成とエラーメッセージ

### バリデータ作成
– reがpythonの正規表現モジュール
/hello/models.py

import re
from django.db import models
from django.core.validators import ValidationError

def number_only(value):
	if(re.match(r'^[0-9]*$', value) == None):
		raise ValidationError(
				'%(value)s is not Number!', params={'value': value},
			)

class Friend(models.Model):
	name = models.CharField(max_length=100, validators=[number_only])
	mail = models.EmailField(max_length=200)
	gender = models.BooleanField()
	age = models.IntegerField()
	birthday = models.DateField()

### エラーメッセージ作成
– フォーム全体のエラーメッセージを取り出すにはform.errorsを使う
– フォームの項目からエラーを取り出すことができる
/hello/templates/hello/check.html

<body class="container">
	<h1 class="display-4 text-primary">{{title}}</h1>
	<p>{{message|safe}}</p>
	<ol class="list-group">
		{% for item in form %}
		<li class="list-group-item py-2">{{ item.name }}{{ item.value }}:{{ item.errors.as_text }}</li>
		{% endfor %}
	</ol>
	<table class="table mt-4">
		<form action="{% url 'check' %}" method="post">
			{% csrf_token %}
			<tr>
				<th>名前</th>
				<td>{{ form.name }}</td>
			</tr>
			<tr>
				<th>メール</th>
				<td>{{ form.mail }}</td>
			</tr>
			<tr>
				<th>性別</th>
				<td>{{ form.gender }}</td>
			</tr>
			<tr>
				<th>年齢</th>
				<td>{{ form.age }}</td>
			</tr>
			<tr>
				<th>誕生日</th>
				<td>{{ form.birthday }}</td>
			</tr>
			<tr><th></th><td>
			<input type="submit" value="click" class="btn btn-primary">
			</td></tr>
		</form>
	</table>
</body>

### ModelFormのカスタマイズ
/hello/forms.py

class FriendForm(forms.ModelForm):
	class Meta:
		model = Friend
		fields = ['name','mail','gender','age','birthday']
		widgets = {
			'name': forms.TextInput(attrs={'class':'form-control'}),
			'mail': forms.EmailInput(attrs={'class':'form-control'}),
			'age': forms.NumberInput(attrs={'class':'form-control'}),
			'birthday': forms.DateInput(attrs={'class':'form-control'}),
		}

/hello/templates/hello/check.html

<body class="container">
	<h1 class="display-4 text-primary">{{title}}</h1>
	<p>{{message|safe}}</p>
	<ol class="list-group mb-4">
		{% for item in form %}
		<li class="list-group-item py-2">{{ item.name }}{{ item.value }}:{{ item.errors.as_text }}</li>
		{% endfor %}
	</ol>
		<form action="{% url 'check' %}" method="post">
			{% csrf_token %}
			<div class="form-group">名前{{ form.name }}</div>
			<div class="form-group">メール{{ form.mail }}</div>
			<div class="form-group">性別{{ form.gender }}</div>
			<div class="form-group">年齢{{ form.age }}</div>
			<div class="form-group">誕生日{{ form.birthday }}</div>
			<div class="form-group">
			<input type="submit" value="click" class="btn btn-primary">
			</div>
</body>

[Django]ModelFormのバリデーション

– バリデーションはsaveの時に実行されるので、モデルの更新時にはモデル側でバリデーションをかける

/hello/forms.py

class FriendForm(forms.ModelForm):
	class Meta:
		model = Friend
		fields = ['name','mail','gender','age','birthday']

/hello/models.py

class Friend(models.Model):
	name = models.CharField(max_length=100)
	mail = models.EmailField(max_length=200)
	gender = models.BooleanField()
	age = models.IntegerField(default=0)
	birthday = models.DateField()

– save以外に自分でチェックをかけることも可能
/hello/views.py

def check(request):
	params = {
		'title': 'Hello',
		'message': 'check validation.',
		'form': FriendForm(),
	}
	if(request.method == 'POST'):
		obj = Friend()
		form = FriendForm(request.POST, instance=obj)
		params['form'] = form
		if(form.is_valid()):
			params['message'] = 'OK!'
		else:
			params['message'] = 'no good.'
	return render(request, 'hello/check.html', params)

/hello/templates/hello/check.html

<body class="container">
	<h1 class="display-4 text-primary">{{title}}</h1>
	<p>{{message|safe}}</p>
	<form action="{% url 'check' %}" method="post">
		{% csrf_token %}
		<table class="table">
		{{ form.as_table }}
		<tr><th></th><td>
		<input type="submit" value="click" class="btn btn-primary mt-2">
		</td></tr>
		</table>
	</form>
</body>

– forms.FormとModelFormのバリデーションは異なる

### モデルにバリデーションの組み込み
/hello/models.py

from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator

class Friend(models.Model):
	name = models.CharField(max_length=100)
	mail = models.EmailField(max_length=200)
	gender = models.BooleanField()
	age = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(150)])
	birthday = models.DateField()

### モデルで使えるバリデータ
– MinValueValidator/MaxValueValidator, MinLengthValidator/MaxLengthValidator

from django.db import models
from django.core.validators import MinLengthValidator

class Friend(models.Model):
	name = models.CharField(max_length=100, validators=[MinLengthValidator(10)])
	mail = models.EmailField(max_length=200, validators=[MinLengthValidator(10)])
	gender = models.BooleanField()
	age = models.IntegerField()
	birthday = models.DateField()

– EmailValidator/URLValidator
– ProhibitNullCharactersValidator
– RegexValidator

from django.core.validators import RegexValidator

class Friend(models.Model):
	name = models.CharField(max_length=100, validators=[RegexValidator(r'^[a-z]*$')])

バリデーション定義によってmodelに組み込んでいくイメージです。

[Django]バリデーションの種類

### CharFieldのバリデーション
– required
– min_length, max_length
– empty_value

class CheckForm(forms.Form):
	empty = forms.CharField(label='Empty', empty_value=True, widget=forms.TextInput(attrs={'class':'form-control'}))
	min = forms.CharField(label='Min', min_length=10, widget=forms.TextInput(attrs={'class':'form-control'}))	
	max = forms.CharField(label='Max', max_length=10, widget=forms.TextInput(attrs={'class':'form-control'}))

### IntegerField/FloatFieldのバリデーション
– required
– min_value, max_value

class CheckForm(forms.Form):
	required = forms.IntegerField(label='Required', widget=forms.NumberInput(attrs={'class':'form-control'}))
	min = forms.IntegerField(label='Min', min_value=100, widget=forms.NumberInput(attrs={'class':'form-control'}))	
	max = forms.IntegerField(label='Max', max_value=1000, widget=forms.NumberInput(attrs={'class':'form-control'}))

### 日時関連のバリデーション
– DateField, TimeField, DateTimeFieldでは日時の各値を表す記号を組み合わせて作成する
– %y, %m, %d, %H, %M, %S

class CheckForm(forms.Form):
	date = forms.DateField(label='Date', input_formats=['%d'], widget=forms.DateInput(attrs={'class':'form-control'}))
	time = forms.TimeField(label='Time', widget=forms.TimeInput(attrs={'class':'form-control'}))
	datetime = forms.DateTimeField(label='DateTime', widget=forms.DateTimeInput(attrs={'class':'form-control'}))

### バリデーションを追加
– Formクラスにメソッドを追加する
– raise ValidationError(“Error message”)
/hello/forms.py

from django import forms

class CheckForm(forms.Form):
	str = forms.CharField(label='String', widget=forms.TextInput(attrs={'class':'form-control'}))

	def clean(self):
		cleaned_data = super().clean()
		str = cleaned_data['str']
		if(str.lower().startswith('no')):
			raise forms.ValidationError('Your input "No!"')

[Django]バリデーション基礎

if(<

>.is_valid()):
# エラー時の処理
else:
# 正常時の処理

### バリデーションを使ってみる
/hello/templates/hello/check.html

<body class="container">
	<h1 class="display-4 text-primary">{{title}}</h1>
	<p>{{message|safe}}</p>
	<form action="{% url 'check' %}" method="post">
		{% csrf_token %}
		{{ form.as_table }}
		<input type="submit" value="click" class="btn btn-primary mt-2">
	</form>
</body>

/hello/urls.py

	path('check', views.check, name='check')

/hello/forms.py

class CheckForm(forms.Form):
	str = forms.CharField(label='Name', widget=forms.TextInput(attrs={'class':'form-control'}))	

/hello/views.py

from .forms import CheckForm

def check(request):
	params = {
		'title': 'Hello',
		'message': 'check validation.',
		'form': CheckForm(),
	}
	if(request.method == 'POST'):
		form = CheckForm(request.POST)
		param['form'] = form
		if(form.is_valid()):
			params['message'] = 'OK!'
		else:
			params['message'] = 'no good.'
	return render(request, 'hello/check.html', params)

スペルミスでmethod=”post”がmetho=”post”になってた。道理で動かない訳だ。