Forms的基础用法

用来校验所有的数据

Django forms是一个强大的组件, 可以用来校验python的 dict object

通常情况下用来校验 但不限于 HTTP request POST 请求,

实例 读取CSV数据保存


import csv

from django.utils.six import StringIO
from django import forms

from .models import Purchase, 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_purchases(rows):
        rows = StringIO.StringIO(rows)
        
        records_added = 0
        errors = []
        # Generate a dict per row, with the first CSV row being the keys.
        for row in csv.DictReader(rows, delimiter=','):
            # Bind the row data to the PurchaseForm.
            form = PurchaseForm(row)
            # Check to see if the row data is valid.
            if form.is_valid():
                # Row data is valid so save the record.
                form.save()
                records_added += 1
            else:
                errors.append(form.errors)
        return records_added, errors

在HTML中使用 POST 方法提交表单

<form action="{   url 'flavor_add'   }" method="POST">

在 HTTP form 提交过程 使用 CSRF Protection 保护

当然在开发API时, API的请求 每次都是登录,验证的, 基于 HTTP cookie的验证不太现实, 可能不需要 CSRF

  • 通过 Ajax发送数据也需要 CSRF protection

需要设置 X-CSRFToken 的 HTTP header 请求头 Cross Site Request Forgery protection

Understand How to Add Django Form Instance Attributes 给 Form 对象增加属性


from django import forms

from .models import Taster

class TasterForm(forms.ModelForm):
    class Meta:
        model = Taster
        
    def __init__(self, *args, **kwargs):
        # set the user as an attribute of the form
        self.user = kwargs.pop('user')
        super(TasterForm, self).__init__(*args, **kwargs)

视图


from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import UpdateView

from .forms import TasterForm
from .models import Taster

class TasterUpdateView(LoginRequiredMixin, UpdateView):
    model = Taster
    form_class = TasterForm
    success_url = '/someplace/'
        
    def get_form_kwargs(self):
        """This method is what injects forms with keyword arguments."""
        # grab the current set of form #kwargs
        kwargs = super(TasterUpdateView, self).get_form_kwargs()
        # Update the kwargs with the user_id
        kwargs['user'] = self.request.user
        return kwargs

理解 form validation 是怎么实现的

调用 form.is_valid() 开始, 会有如下的操作流程

1 If the form has bound data, form.is_valid() calls the form.full_clean() method.

2 form.full_clean() iterates through the form fields and each field validates itself:

    a Data coming into the field is coerced into Python via the to_python() method or raises
    a ValidationError.
    
    b Data is validated against field-specific rules, including custom validators. Failure raises a
    ValidationError.
    
    c If there are any custom clean_<field>() methods in the form, they are called at this
    time.

3 form.full_clean() executes the form.clean() method.

4 If it’s a ModelForm instance, form._post_clean() does the following:

    a Sets ModelForm data to the Model instance, regardless of whether form.is_valid()
    is True or False.
    
    b Calls the model’s clean() method. For reference, saving a model instance through the
    ORM does not call the model’s clean() method.
  • ModelForm 数据先传给 Form 对象 然后是 Model 对象

    1 First, form data is saved to the form instance. 2 Later, form data is saved to the model instance.

保存验证失败的数据


# core/models.py

from django.db import models

class ModelFormFailureHistory(models.Model):
    form_data = models.TextField()
    model_data = models.TextField()

# flavors/views.py

import json

from django.contrib import messages
from django.core import serializers

from core.models import ModelFormFailureHistory

class FlavorActionMixin:
    @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):
        """Save invalid form and model data for later reference."""
        form_data = json.dumps(form.cleaned_data)
        # Serialize the form.instance
        model_data = serializers.serialize('json', [form.instance])
        # Strip away leading and ending bracket leaving only a dict
        model_data = model_data[1:-1]
        ModelFormFailureHistory.objects.create(
            form_data=form_data,
            model_data=model_data
            )
        return super(FlavorActionMixin,
            self).form_invalid(form)

增加验证错误信息 Add Errors to Forms With Form.add_error()

from django import forms

class IceCreamReviewForm(forms.Form):
    # Rest of tester form goes here
    ...
    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:
            # Record errors that will be displayed later.
            msg = 'Coffee Ice Cream is not for Babies.'
            self.add_error('flavor', msg)
            self.add_error('age', msg)
            
        # Always return the full collection of cleaned data.
        return cleaned_data


Buy me a 肥仔水!