Successfully reported this slideshow.

理解して使いこなすDjangoのForm機能(2021 Django Congress発表資料)

0

Share

1 of 97
1 of 97

理解して使いこなすDjangoのForm機能(2021 Django Congress発表資料)

0

Share

Download to read offline

2021年のDjango Congress(長野開催)での発表資料です。
発表中に紹介できなかった部分などを追加してます。

2021年のDjango Congress(長野開催)での発表資料です。
発表中に紹介できなかった部分などを追加してます。

More Related Content

Related Books

Free with a 14 day trial from Scribd

See all

理解して使いこなすDjangoのForm機能(2021 Django Congress発表資料)

  1. 1. 理解して使いこなすDjangoのForm機能 きゅうたつ(KyutatsuNishiura) 1
  2. 2. お前誰よ きゅうたつ(Kyutatsu) 株式会社日本システム技研(JSL)所属 Webエンジニア Python,Django,AWS 2
  3. 3. トークの対象者 Formは使ったことがあるが、既存コードをコピペして雰囲気でやっている。 いざ自分で実装しようとすると手が動かない. 公式DocumentのForm関連の項目を読むのが苦痛 3
  4. 4. こんな登録フォームがあったとします お仕事の作業内容を、開始,終了時刻を登録するフォーム. 開始時刻の方には,6時〜23時までしか入力できないValidationがついている. 4
  5. 5. Q:こんな修正要望があったら?1/2 5
  6. 6. Q:こんな修正要望があったら?2/2 要望2.終了時刻の方にも、開始時刻と同じく6時〜23時の範囲チェックつけて! 要望3.開始時刻<終了時刻となってるようチェックして! エラーメッセージはそれぞれのフィールドの上に出して! 6
  7. 7. 目標 公式ドキュメントを読んで、手を動かせる. 作業量を見積もることができる. そんなトークにしたいと思っています. 7
  8. 8. もくじ まえおき 0章.準備編:FormをDjangoshellを使ってさわろう 1章.構造編:Formを構成する要素 2章.フロント編:HTMLをレンダリング 3章.バリデーション編:値をチェック 4章.便利な機能:FormSetでFormをたくさん並べよう! 8
  9. 9. 9
  10. 10. 初心者にとってFormの厄介なところ 複数のクラスが関わってくる。 modelやtemplate,validatorなど、複数の要素が絡んでくる。 パラメータなどを変更できるポイントが多い。 「ラベルを変えたい」だけでも、変更可能な場所が何箇所もある... 10
  11. 11. Formの構成要素をさわって動かしてみて理解する. 11
  12. 12. 0章.Djangoshellを使おう 1.Djangoshellへの入り方 2.Templateへのレンダリングをshellから確認 12
  13. 13. 0章.Djangoshellの入り方 $ python manage.py shell ...省略... (InteractiveConsole) >>> 参照できるfield、ヘルプをその場で見れる。 テストコードを書くハードルが下がる. 13
  14. 14. 0章:Templateへのレンダリングをshellから確認 formの定義 class NameForm(forms.Form): name = forms.CharField() インスタンス化 >>> form = NameForm() 14
  15. 15. 0章:Templateへのレンダリングをshellから確認 DjangoTemplateLanguageでのレンダリング カギカッコのアクセスはTemplateではドットで行う。 メソッドはコールされる。 最終的に__str__()される。 shellで出力結果を手軽に確認するにはprint()すればOK. https://docs.djangoproject.com/ja/3.2/topics/templates/#variables https://docs.djangoproject.com/ja/3.2/ref/templates/language/#variables 15
  16. 16. 0章:Templateへのレンダリングをshellから確認 カギカッコのアクセスはドットで行う。 Template上でnameフィールドをレンダリング. {{ form.name }} ↓ <input type="text" name="name" required id="id_name"> shell上で同じことをする場合. 最終的に__str__()される。->printを呼ぶ。 >>> print(form['name']) # []を使ったアクセス <input type="text" name="name" required id="id_name"> 16
  17. 17. 0章:Templateへのレンダリングをshellから確認 メソッドはコールされる。 Template上でnameフィールドのラベルをレンダリング. {{ form.name.label_tag }} ↓ <label for="id_name">Name:</label> shellで同じことをする場合 最終的に__str__()される。->printを呼ぶ。 >>> print(form['name'].get_label()) # 関数呼び出し <label for="id_name">Name:</label> 17
  18. 18. 1章.構造編:Formを構成する要素 18
  19. 19. 1章.Formを構成する要素 以下のクラスが何者かをざっくり説明します。 詳細は後の方で解説します。 Form Field Widget Validator BoundField 19
  20. 20. 1章.Formを構成する要素 以下のFormを例に説明します。 class MemberForm(forms.Form): name = forms.CharField() # 名前 age = forms.IntegerField() # 年齢 FormにはFieldを定義できる。 Fieldは、デフォルトのwidgetとvalidatorを持っています. 20
  21. 21. 1章.Formを構成する要素 Form Field Widget Validator BoundField 21
  22. 22. 1章.Formを構成する要素:Widget HTMLを生成するためのクラス. form = MemberForm()) form.fields['name'].widget <django.forms.widgets.TextInput at 0x7fe0fb4b04f0> renderメソッドで、inputタグのようなウィジェットのHTMLを作ります。 form.fields['name'].widget.render('これがname属性', 'これがvalue') '<input type="text" name="これがname属性" value="これがvalue">' 22
  23. 23. 1章.Formを構成する要素 Form Field Widget Validator BoundField 23
  24. 24. 1章.Formを構成する要素:Validator 受け取った値が条件を満たすかをチェックする. form = MemberForm() form.fields['name'].validators # listに入ってる. [<django.core.validators.ProhibitNullCharactersValidator at 0x7fe0fc0700a0>] 24
  25. 25. ここまででおぼえておくこと FormにはFieldを定義することができる Fieldは以下のオブジェクトを持っている. HTMLをrenderするWidget 値のチェック(バリデーション)をおこなうValidator 25
  26. 26. 1章.Formを構成する要素 Fieldが持っているwidget,validatorsは、Built-inFieldclassesで確認できます。 https://docs.djangoproject.com/en/3.2/ref/forms/fields/#built-in-field-classes 26
  27. 27. 1章.Formを構成する要素 Form BoundField Field Widget Validator ↓テンプレートでアクセスしているこれです。 {{ form.field_name }} 27
  28. 28. 1章.Formを構成する要素:BoundFieldとField https://docs.djangoproject.com/ja/3.2/ref/forms/api/#django.forms.BoundField Fieldクラス Formのattribueとして定義する。 BoundField テンプレート上でのField関係のレンダリングはこのクラスが取りまとめる FormのField名に[]アクセスしたときに作成される.-> form['name'] 28
  29. 29. 1章.Formを構成する要素:BoundFieldとField Field form.fields['name'] <django.forms.fields.CharField at 0x7fecdcbd7fa0> BoundField form['name'] <django.forms.boundfield.BoundField at 0x7fecdc936100> 29
  30. 30. 1.Formを構成する要素まとめ Form Field Widget→<input>のようなHTMLをつくる Validator→値のチェックを行う. BoundField→各FieldのHTMLへのレンダリングとりまとめる. 30
  31. 31. 2章.フロント編:HTMLをレンダリング 1.BoundFieldの役割とできること 2.Widgetの役割とできること 31
  32. 32. 2章.フロント編:BoundField Field単位でHTMLレンダリングのロジックをまとめている。 32
  33. 33. 2章.フロント編:BoundField {{ form.field_name}} form.<field_name> これはBoundField。 HTMLレンダリングに必要な要素は,BoundFieldから基本的に取得します. 33
  34. 34. 2章.フロント編:BoundField BoundFieldはHTMLレンダリングに必要なメソッドや属性を持ってい る. https://docs.djangoproject.com/ja/3.2/ref/forms/api/#attributes-of-boundfield 利用可能な属性一覧はこちらの公式リンクから確認できます。 # labelタグを生成するメソッド print(form['name'].label_tag()) <label for="id_name">Name:</label> print(form['name'].id_for_label) id_name print(form['name'].name) name 34
  35. 35. 2章.フロント編:BoundField BoundFieldはWidgetやErrorListクラスにHTMLレンダリングを任せて いる. BoundFieldをprintすると,widgetによりinputタグがでている print(form['name']) <input type="text" name="name" required> error自体はFormクラスがまとめている。( form.errors ) ul タグの作成は、ErrorListクラスが行っている。 BoundFieldは、Formクラスから自分のFieldに対応するErrorListを取り出すだけ。 print(form['name'].errors) <ul class="errorlist"><li>この項目は必須です。</li></ul> 35
  36. 36. 2章.フロント編:BoundField BoundFieldはField,Formそれぞれのインスタンス化する時の引数で変更を入れられる. class MemberForm(forms.Form): name = forms.CharField(label='変更したラベル') form = MemberForm(auto_id='変更したID_%s') print(form['name'].label_tag()) <label for="変更したID_name">変更したラベル:</label> 36
  37. 37. 2章.フロント編:BoundField Formのインスタンス化時に設定. https://docs.djangoproject.com/ja/3.2/ref/forms/api/#configuring-form-elements-html-id- attributes-and-label-tags Fieldの引数 https://docs.djangoproject.com/ja/3.2/ref/forms/fields/#core-field-arguments 37
  38. 38. 2章.フロント編:BoundFieldまとめ BoundFieldは form['フィールド名'] にアクセスすることで作成される。 BoundFieldはField単位でのHTML出力をとりまとめている。 BoundFieldは、Field,Formそれぞれのインスタンス化時の引数によって変更を加えられ る。 38
  39. 39. 2章.フロント編:HTMLをレンダリング:Widget https://docs.djangoproject.com/ja/3.2/ref/forms/widgets/ class MemberForm(forms.Form): name = forms.CharField() ↓と同じ. class MemberForm(forms.Form): name = forms.CharField( widget=forms.TextInput, ) 39
  40. 40. 2章.フロント編:Widgetの差し替え <input> などの要素をレンダリングします。 組み込みのwidgetが複数用意されています。 差し替えることで生成されるHTMLタグを簡単に変更できます. 自分でカスタムwidgetを定義することも可能です。 40
  41. 41. 2章.フロント編:Widgetの差し替え Formの定義 class MemberForm(forms.Form): name = forms.CharField() form['フィールド名'] はBoundFieldです。 これをprintすると、BoundFieldからWidgetのrenderがよばれ,HTMLが作られます。 form = MemberForm() print(form['name']) <input type="text" name="name" required id="id_name"> CharFieldのデフォルトwidget(TextInput)によりinputタグが作成される. 41
  42. 42. 2章.フロント編:Widgetの差し替え Formの定義 class MemberForm(forms.Form): name = forms.CharField(widget=forms.Textarea) # TexInput-> Textarea form['フィールド名'] はBoundFieldです。 form = MemberForm() print(form['name']) <textarea name="name" cols="40" rows="10" required id="id_name"> </textarea> input->textareaタグに変更された。 42
  43. 43. 2章.フロント編:Widgetの差し替え HTMLタグのattributeを差し替えることができます。 widgetにはattr引数でdictを渡します。 class MemberForm(forms.Form): name = forms.CharField( # inputタグにclassを設定. widget=forms.TextInput(attrs={'class': 'user-defined-attr'})) form = MemberForm() print(form['name']) # クラスが設定された! <input type="text" name="name" class="user-defined-attr" required id="id_name"> TextInputwidgetに渡したattrにより、printしたHTMLタグにclass属性が追加されてい る. 43
  44. 44. 2章.フロント編:Widgetはテンプレートを持つ 例:TextInputクラスのソースコード上の定義 class TextInput(Input): input_type = 'text' template_name = 'django/forms/widgets/text.html' django/forms/widgets.py より抜粋. {% include "django/forms/widgets/input.html" %} /django/forms/templates/django/forms/widgets/text.html より。 44
  45. 45. 2章.フロント編:Widgetはテンプレートを持つ <input type="{{ widget.type }}" name="{{ widget.name }}"i {% if widget.value != None %} value="{{ widget.value|stringformat:'s' }}" {% endif %} {% include "django/forms/widgets/attrs.html" %}> /django/forms/templates/django/forms/widgets/input.html より改行などを加えて 抜粋。 45
  46. 46. 2章.フロント編:Widgetまとめ Widgetクラスはテンプレート+レンダリングのロジックを再利用可能な形でまとめてい る。 WidgetのrenderメソッドはBoundFieldから呼ばれて利用される。 46
  47. 47. 2章のまとめ Widgetは <input> のようなFormの部品を定義している。 BoundFieldはField単位のHTMLレンダリングの取りまとめ役. BoundFieldはwidgetやErrorListクラスにおねがい して、タグを丸ごとレンダリングす ることもある. 47
  48. 48. 3章.バリデーション編:値をチェック 1.validationの結果:データorエラー 2.エラーを構成するクラス i.ErrorDict ii.ErrorList iii.ValidationError例外 3.validationの方法 i.Form.cleanで複数Fieldのチェック ii.validatorを使ってひとつのFieldをチェック iii.ModelFormのvalidation 48
  49. 49. 3章.バリデーション編:validationの結果 class JslMemberForm(forms.Form): name = forms.CharField() # 名前 age = forms.IntegerField() # 年齢 1.Validation成功->form.cleaned_dataに整形された値が入る. 2.Validation失敗->エラーがform.errorsにまとめられる. 49
  50. 50. 3章.バリデーション編:validation成功 form.cleaned_data (dict)に「cleanされた」値が入る。 form = JslMemberForm({'name': 'たろう ', 'age': '20'}) form.is_valid() Out[69]: True form.cleaned_data Out[70]: {'name': 'たろう', 'age': 20} nameは後ろのスペースが除去されている ageはint型に変更されている. 50
  51. 51. 3章.バリデーション編:validation失敗 validationに失敗(エラーが発生)すると、 form.errors にエラーがまとめられる. form = JslMemberForm({'name': '', 'age': 'にじゅっさい'}) form.is_valid() Out[72]: False form.errors Out[73]: {'name': ['This field is required.'], 'age': ['Enter a whole number.']} 51
  52. 52. 3章.バリデーション編:エラーを構成するクラス 1.ErrorDict 2.ErrorList 3.ValidationError例外 52
  53. 53. 3章.バリデーション編:エラーを構成するクラス form.errors Out[73]: {'name': ['This field is required.'], 'age': ['Enter a whole number.']} 53
  54. 54. 3章.バリデーション編:ErrorDictクラス print(form.errors) <ul class="errorlist"> <li>name<ul class="errorlist"><li>This field is required.</li></ul></li> <li>age<ul class="errorlist"><li>Enter a whole number.</li></ul></li> </ul> __str__ メソッドでas_ul()を呼ぶようになっており,エラーメッセージのHTMLを作る ※ 一部改行とインデントを加えて表示. 54
  55. 55. 3章.バリデーション編:ErrorListクラス 再掲 form.errors Out[73]: {'name': ['This field is required.'], 'age': ['Enter a whole number.']} ErrorDictのvalue部分はlistに見えるが、実際はErrorListと言うクラス. form.errors['name'] Out[74]: ['This field is required.'] print(form.errors['name']) <ul class="errorlist"><li>This field is required.</li></ul> __str__ メソッドでas_ul()を呼ぶようになっており,エラーメッセージのHTMLを作る 55
  56. 56. 3章.バリデーション編:ErrorListクラス ポイント: form['name'] はBoundField. Form._error から自分のFieldに属するエラーを引き出してきている. print(form['name'].errors) <ul class="errorlist"><li>この項目は必須です。</li></ul> BoundFieldのソースコード↓ django/forms/boundfield.py 56
  57. 57. 3章.バリデーション編:ValidationErrorr例外 form.errors['name'] Out[74]: ['This field is required.'] ErrorListクラスはValidationError例外を保持している. Validationの失敗でraiseされるのは、全てこのValidationError例外. form.errors['name'].data Out[84]: [ValidationError(['This field is required.'])] 57
  58. 58. 3章.バリデーション編:ValidationErrorr例外 formでvalidationをするときにraiseすることになっている例外. formには、発生したValidationErrorをまとめる仕組みが実装されている。 58
  59. 59. 3章.バリデーション編:ValidationError例外 https://docs.djangoproject.com/en/3.2/ref/forms/validation/#raising-validationerror 書き方のベストプラクティスという項目があるので、一読しておくと良いともいます. 59
  60. 60. 3章.バリデーション編:エラーを構成するクラスまとめ この二つは自らをHTMLとしてレンダリングすることができる. ErrorDict: form.errors に入っている。 ErrorList:ErrorDictのvalue. 3.ValidationError例外 formでのvalidation失敗時にraiseする例外. 60
  61. 61. 3章.バリデーション編:validationの方法 1.Form.cleanで複数Fieldのチェック 2.validatorを使ってひとつのFieldをチェック 61
  62. 62. 3章.Validationは複数のステップで呼ばれる https://docs.djangoproject.com/ja/3.2/ref/forms/validation/ Form clean() clean_<fieldname>() Field to_python() validate() run_validators() Field.validators:listのvalidatorを順番に実行... 62
  63. 63. 3章.Form.clean():複数Fieldをvalidateする https://docs.djangoproject.com/ja/3.2/ref/forms/validation/#cleaning-and-validating- fields-that-depend-on-each-other 開始時刻(start_at)と終了時刻(finish_at)をsubmitするFormを考える。 開始時刻<終了時刻となるようチェック。 このような場合、Validationはcleanメソッドに記述する. 63
  64. 64. 3章.Form.clean():複数Fieldをvalidateする class WorkTimeForm(forms.Form): start_at = forms.DateTimeField() finish_at = forms.DateTimeField() def clean(self): cleaned_data = super().clean() start_at = cleaned_data.get("start_at") finish_at = cleaned_data.get("finish_at") if finish_at and start_at and finish_at <= start_at: raise forms.ValidationError("終了時刻は開始時刻より後にしてください", code='work_time_order') 64
  65. 65. 3章.Form.clean():複数Fieldをvalidateする form = WorkTimeForm({"start_at": "2020-01-02 08:00:00", "finish_at": "2020-01-01 17:30:00"}) form.errors {'__all__': ['終了時刻は開始時刻より後にしてください']} finish_at<start_atとなるようなデータを渡してerrorsを見ると、エラーが出ている. 特定のFieldに結びついていないため、ErorrsDictのkeyは** __all__ **となっている. 65
  66. 66. 3章. __all__ keyのエラーはform.non_field_errors() で取得できる. form.non_field_errors() Out[101]: ['終了時刻は開始時刻より後にしてください'] print(form.non_field_errors()) <ul class="errorlist nonfield"><li>終了時刻は開始時刻より後にしてください</li></ul> 66
  67. 67. 3章.key: __all__ のエラーはform.non_field_errors() で取得できる. テンプレートでは下のように書くと同じ意味になる. {{ form.non_field_error }} ↓ <ul class="errorlist nonfield"><li>終了時刻は開始時刻より後にしてください</li></ul> 67
  68. 68. 3章.form.add_errorで各Fieldにエラーメッセージをセッ トする https://docs.djangoproject.com/ja/3.2/ref/forms/validation/#cleaning-and-validating- fields-that-depend-on-each-other Form.add_errorsメソッドを呼ぶ. class WorkTimeForm(forms.Form): # .........(省略)........... def clean(self): # .......(省略)....... if finish_at <= start_at: self.add_error('start_at', forms.ValidationError('開始時刻のエラー')) self.add_error('finish_at', forms.ValidationError('終了時刻のエラー')) 68
  69. 69. 3章.form.add_errorで各Fieldにエラーメッセージをセッ トする 再掲.raiseValidationErrorした場合. form.errors {'__all__': ['終了時刻は開始時刻より後にしてください']} 修正後:add_errorを使った場合. form = WorkTimeForm({"start_at": "2020-01-02 08:00:00", "finish_at": "2020-01-01 17:30:00"}) form.errors Out[105]: {'start_at': ['開始時刻が×'], 'finish_at': ['終了時刻が×']} 69
  70. 70. 3章.form.add_errorで各Fieldにエラーメッセージをセッ トする それぞれのFieldに属しているので、レンダリングでエラーメッセージをFieldの隣に配置 するのに役立つ. form['start_at'].errors Out[107]: ['開始時刻が×'] 復習:↑はBoundFieldが、formの_errorsから自分のfieldに出ているエラーを取得することで 表示している。 70
  71. 71. 3章.バリデーション編 3.validationの方法 i.Form.cleanで複数Fieldのチェック ii.validatorを使ってひとつのFieldをチェック iii.ModelFormのvalidation 71
  72. 72. 3章.バリデーション編:validatorでひとつのFieldをチェッ ク Form clean() clean_<field名>() Field to_python() validate() run_validators() Field.validators:listのvalidatorを順番に実行... 72
  73. 73. 3章.validatorの定義1/4 https://docs.djangoproject.com/en/3.2/ref/validators/ ドキュメントがFormとは別の場所にある。 Djangoでのvalidatorは以下のようなものです。 callableである. 引数を一つとる. 特定の条件を満たさない時、ValidationError例外をraiseする. 73
  74. 74. 3章.validatorの定義2/4 from django.core.exceptions import ValidationError def validate_even(value): if value % 2 != 0: raise ValidationError('これは偶数ではありません!') https://docs.djangoproject.com/ja/3.2/ref/validators/を参考に記述. 単なる関数(など)なので、再利用性が高い。 74
  75. 75. 3章.validatorの定義3/4 >>> validate_even(2) # 何も返さない。(None) 奇数(3)を与えると、ValidationErrorをraiseする。 >>> validate_even(3) ...省略... django.core.exceptions.ValidationError: ['これは偶数ではありません!'] 75
  76. 76. 3章.validatorの定義4/4 class NumberForm(forms.Form): even = forms.IntegerField(validators=[validate_even]) form = NumberForm({'even': '3'}) form.errors Out[37]: {'even': ['これは偶数ではありません!']} 76
  77. 77. 3章.build-inのValidatorクラス https://docs.djangoproject.com/en/3.2/ref/validators/#built-in-validators built-inのvalidatorは一度見ておくと良い。 パラメータを渡し、インスタンス化して使うことができる。 from django.core.validators import RegexValidator start_with_jsl = RegexValidator(regex='^jsl.*', message='「jsl」から始めてください') start_with_jsl('jsl00000') # OK start_with_jsl('AAA00000') # NG! # django.core.exceptions.ValidationError: ['「jsl」から始めてください'] 77
  78. 78. 3章.バリデーション編 3.validationの方法 i.Form.cleanで複数Fieldのチェック ii.validatorを使ってひとつのFieldをチェック iii.ModelFormのvalidation 78
  79. 79. 3章.バリデーション編:値をチェック 1.validationの結果:データorエラー 2.エラーを構成するクラス i.ErrorDict ii.ErrorLis iii.ValidationError例外 3.validationの方法 i.Form.cleanで複数Fieldのチェック ii.validatorを使ってひとつのFieldをチェック iii.ModelFormのvalidation 79
  80. 80. 4章.便利な機能:FormSetでFormをたくさん並べよう! 80
  81. 81. 4章.FormSet https://docs.djangoproject.com/ja/3.2/topics/forms/formsets/ https://docs.djangoproject.com/ja/3.2/topics/forms/modelforms/#model-formsets 81
  82. 82. 4章.FormSet 同じ種類のFormを複数並べる時に便利! factory関数を使って簡単にFormSetインスタンスを作成できる. 通常のFormとほぼ同じように扱える。 82
  83. 83. 4章.FormSet formset_factoryとmodelformset_factoryがある。 既存のModelFormから、modelformset_factoryを使ってFormSetを作る例を紹介しま す。 83
  84. 84. 4章.FormSet class Member(models.Model): name = models.CharField(max_length=100) age = models.IntegerField(null=True, blank=True) class MemberModelForm(forms.ModelForm): class Meta: model = Member fields = ('name',) 84
  85. 85. 4章.FormSet from django.forms import modelformset_factory MemberFormSet = modelformset_factory( model=Member, form=MemberModelForm, extra=5, # いくつ空のFormを並べるか。 ) これだけ! 85
  86. 86. 4章.FormSet:Viewでの使い方 通常のモデルフォーム # 空 form = MemberModelForm() # データあり form = MemberModelForm(request.POST) ↓ # 空 form_set = MemberFormSet(queryset=Member.objects.none()) # データあり form_set = MemberFormSet(request.POST, queryset=Member.objects.none()) querysetを指定すると、DB上のデータを投入したFormSetを表示できる(編集) Model.objects.non()を指定すると、空のformsetができます(新規登録) 86
  87. 87. 4章.テンプレートでは https://docs.djangoproject.com/ja/3.2/topics/forms/modelforms/#using-the-formset-in- the-template <form method="POST"> {% csrf_token %} <!-- 補助フィールド --> {{ form_set.management_form }} {% for f in form_set %} {{ f }} {% endfor %} <input type="submit" value="SUBMIT!"> </form> 87
  88. 88. まとめ とっつきにくいDjangoのForm機能ですが、個人的にはいくつかキーとなるポイントを知って おくと、怖くなくなるのではないかと思います djangoshellを使ってFormクラスやModelクラスと仲良くなるのはオススメ Formクラスが含むそれぞれのオブジェクトの役割をイメージすると、ドキュメントが読 みやすくなる。 88
  89. 89. しゃべることのできなかったModelFormのvalidation. 89
  90. 90. 3章.バリデーション編 3.validationの方法 i.Form.cleanで複数Fieldのチェック ii.validatorを使ってひとつのFieldをチェック iii.ModelFormのvalidation 90
  91. 91. 3章.ModelFormとは https://docs.djangoproject.com/ja/3.2/topics/forms/modelforms/ model定義 class Member(models.Model): name = models.CharField(verbose_name='名前', max_length=100) age = models.IntegerField(verbose_name='年齢') Form定義 class MemberModelForm(forms.ModelForm): class Meta: model = Member fields = ('name', 'age') 91
  92. 92. 3章.Modelにもvalidatiorを設定できる. class ProjectCode(models.Model): jsl_code = models.CharField( verbose_name='プロジェクトコード', max_length=7, validators=[RegexValidator(regex='^jsl[0-9]{4}$')]) project_code = ProjectCode(jsl_code='jsl0') project_code.full_clean() django.core.exceptions.ValidationError: {'jsl_code': ['Enter a valid value.']} 92
  93. 93. Modelにもvalidatorを設定できる. https://docs.djangoproject.com/ja/3.2/ref/models/instances/#validating-objects https://docs.djangoproject.com/ja/3.2/ref/models/fields/#validators Model:.full_clean()で以下を実行. clean_fields()->Fieldsのvalidation clean() validate_unique() 93
  94. 94. ModelとModelFormにvalidatorを設定すると... class ProjectCode(models.Model): jsl_code = models.CharField( max_length=7, validators=[RegexValidator(regex='^jsl[0-9]{4}$', message='model側')]) class ProjectCodeModelForm(forms.ModelForm): class Meta: model = ProjectCode fields = ('jsl_code', ) # Form由来は末尾1としてみる. jsl_code = forms.CharField( validators=[RegexValidator(regex='1$', message='Form側')]) 94
  95. 95. ModelとModelFormにvalidatorを設定すると... form = ProjectCodeModelForm({'jsl_code': 'jsl0000'}) form.errors Out[14]: {'jsl_code': ['Form側']} form = ProjectCodeModelForm({'jsl_code': '0001'}) form.errors Out[12]: {'jsl_code': ['model側']} 95
  96. 96. ModelとModelFormにvalidatorを設定すると... Model,Formどちらも満たさない場合. Form側のメッセージしか表示されません. form = ProjectCodeModelForm({'jsl_code': '00'}) form.errors Out[16]: {'jsl_code': ['Form側']} Model側でやっていたチェック(jsl...となること)のメッセージも出したければ Form側に同じvalidatorを設定する必要があります. class ProjectCodeModelForm(forms.ModelForm): # ...省略.... validators=[ RegexValidator(regex='1$', message='Form側'), RegexValidator(regex='^jsl[0-9]{4}$', message='Form側その2')]) 96
  97. 97. ModelとModelFormにvalidatorを設定すると... ModelForm側で設定した2つのバリデーション結果がerrorsに入っている。 form = ProjectCodeModelForm({'jsl_code': '00'}) form.errors Out[28]: {'jsl_code': ['Form側', 'Form側その2']} 97

×