TIL#130 Django Form


Django Form検証


張兄札はパイソンコーナーの有効性を検査するのに最適なツールです.
これは、他のプロジェクトからCSVファイルを取得し、モデルに更新する長期アプリケーションがあると仮定したコードです.
# 나쁜 예 

import csv
import StringIO

from .models import Purchase

def add_csv_purchase(rows):

    rows = StringIO.StringIO(rows)
    records_added = 0
    
    for row in csv.DictReader(rows, delimiter=","):
        purchase.objects.create(**row)
        records_added += 1
    
    return records_added
上のコードは検証されていません.add_csv_purchases()関数に検証コードを追加することもできますが、そうすると、データが変更されるたびに検証コードの変更と管理が必要になります.
この場合、入力データをドラムのフォームで有効性チェックします.
import csv
import StringIO

from django import forms

from .models import Pusrchase, Seller


class PurchaseForm(forms.ModelForm):
    
    class Meta:
        model = Purchase
        
    def clean_seller(self):
        seller = self.cleaned_data['seller']
        try:
            Seller.objects.get(name=seller)
        except Seller.DoesNotExist:
            msg = "{0} does not exist in purchase #{1}.".format(
                seller, 
                self.cleaned_data['purchase_number']
            )
            raise forms.ValidationError(msg)
        return seller
        

def add_csv_purchase(rows):

    rows = StringIO.StringIO(rows)
    
    records_added = 0
    errors = []
    
    for row in csv.DictReader(rows, delimiter=","):
    
        form = PurchaseForm(row)
        
        if form.is_valid():
            form.save()
            records_added += 1
        else:
            errors.append(form.errors)

    return records_added, errors

CSRFセキュリティ


この文書には,サイト間の偽造要求を防止するために,サイト間で偽造保護(CSRF)を要求するものが内蔵されている.使い勝手が良く、開発段階で忘れてしまうと、紹介情報も見せてくれます.
CSRFセキュリティを一時的にオフにすると、マシン間でAPIサイトを作成するときです.これらの処理は、django-tastypieやdjango-rest-frameworkのようなAPIフレームワークで自動的に実行されます.

AJAXデータの追加


AJAXを使用してデータを追加する場合は、本明細書のCSRFセキュリティを使用する必要があります.逆に、AJAXでデータを送信する場合は、HTTPヘッダにX-CSRFTokenを設定します.

Formインスタンス属性の追加


フォームのメソッドには、フォームインスタンスのプロパティも必要になる場合があります.次にuserというインスタンス属性を追加するコードを示します.
# forms.py
from django import forms

from .models import Taster

class TasterForm(forms.ModelForm):
    class Meta:
        model = Taster
        
    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user')
        super(TasterForm, self).__init__(*args, **kwargs)
        
        
# views.py
from django.views.generic import UpdateView

from braces.views import LoginRequireMixin

from .forms import TasterForm
from .models import Taster

class TasterUpdateView(LoginRequireMixin, UpdateView):
    model = Taster
    form_class = TasterForm
    success_url = "/someplace/"
    
    def get_form_kwargs(self):
        kwargs = super(TasterUpdateView, self).get_form_kwargs()
        kwargs['user'] = self.request.user
        return kwargs

フォームの検証方法


form.is valid()を呼び出すと

  • フォームがデータを受信した場合、フォーム.is valid()はformです.full clean()メソッドを呼び出します.
  • form.full clean()は、フォームフィールドと各フィールドの有効性を確認し、次の手順を実行します.
  • フィールドのデータについて、to python()を使用してPython形式に変換したり、Python形式に変換したりすると、ValidationErrorが発生します.
  • custom有効性チェックを含む各フィールドの有効性チェックを行います.問題が発生するとValidationErrorが発生します.
  • フォームにclean()メソッドがある場合は、この操作を実行します.
  • form.full clean()フォーマット.clean()メソッドを実行します.
  • ModelFormインスタンスの場合、フォーム.post clean()は、次の操作を行います.
  • form.is valid()がtrueまたはfalseに設定されている場合、ModelFormのデータはモデルインスタンスに設定されます.
  • モデルのclean()メソッドを呼び出します.モデルインスタンスをORMで保存する場合、モデルのclean()メソッドは呼び出されません.
  • モデル・フォーム・データは、フォームに保存され、モデル・インスタンスに保存されます。


    form.save()メソッドが適用されるまでモデルインスタンスとして保存されないため、この分離プロセス自体を利用することができる.
    たとえば、フォーム入力の試行に失敗した詳細をさらに理解する必要がある場合は、ユーザーが入力したフォームのデータとモデルインスタンスの変更を同時に保存できます.
    # models.py
    from django.db import models
      
    class ModelFormFailureHistory(models.Model):
        form_data = models.TextField()
        model_data = models.TextField()
      
    # views.py
    import json
      
    from django.contrib import messages
    from django.core import serializers
    from core.models import ModelFormFailureHistory
      
    class FlavorActionMixin(object):
      
        @property
        def success_msg(self):
            return NotImplemented
      
        def form_valid(self, form):
            messages.info(self.request, self.success_msg)
            return super(FlavorActionMixin, self).form_valid(form)
      
        def form_invalid(self, form):
            form_data = json.dumps(form.cleaned_data)
            model_data = serializers.serialize("json", [form.instance])[1:-1]
      
            ModelFormFailureHistory.objects.create(
                form_data=form_data,
                model_data=model_data
            )
            return super(FlavorActionMixin, self).form_invalid(form)

    Form.add_error()

    from django import forms
      
    class IceCreamReviewForm(forms.Form):
        ...
      
        def clean(self):
            cleaned_data = super(TasterForm, self).clean()
            flavor = cleaned_data.get('flavor')
            age = cleaned_data.get('age')
      
            if flavor == 'coffee' and age < 3:
                msg = u'Coffee Ice Cream is not for Babies.'
                self.add_error('flavor', msg)
                self.add_error('age', msg)
                   
            return cleaned_data