Django简单入门教程(五)表单


 

参考:《The Django Book》 第7章

 

1. HttpRequest对象的信息

 

request.path                                 除域名以外的请求路径,斜杠开头                      “/hello/”

request.get_host()                      主机名                                                                      “127.0.0.1:8000”  or “www.xxxx.com”

request.get_full_path()              请求路径,可能包含查询字符串                          “/hello/?print=true”

request.is_secure()                    是否通过Https访问                                                是为True,否为False

request.META                               一个字典,包含本次Http请求的Header信息    例如 REMOTE_ADDR 表示客户端ip,

                                                                                                                                                    HTTP_USER_AGENT表示用户浏览器user-agent字符串

                                                                                                                                                    HTTP_REFERER表示进站前连接网页 

request.POST                              一个类字典对象保存html中<form>标签提交的内容

request.GET                                 一个类字典对象保存html中<form>标签提交的内容或者URL中的查询字符串


例子:显示request.META 中的所有信息

在view.py中加入下面代码。并在urls.py加上对应的URLpattern

 

def test(request):
    values = request.META.items()
    values.sort()
    html = []
    for k,v in values:
        html.append('<tr><td>%s</td><td>%s</td></tr>' %  (k,v))
    return HttpResponse('<table>%s</table>' % '\n'.join(html))

 

 

2. 一个简单的表单处理


在templates中创建search_form.html模板:

 

<html>
<head>
    <title>Search</title>
</head>
<body>
    {% if error  %}
        <p style="color: red;">Please submit a search term.</p>
    {% endif %}
    <form action='' method='get'>
        <input type='text' name='q'>
        <input type='submit' value='Search'>
    </form>
</body>
</html>

 


其中action=''  表示表单将提交给与当前页面相同的URL

 

 

再添加search_results.html 

 

 

<p>You searched for: <strong>{{ query }}</strong></p>

{% if books %}
<p>Found {{ books|length }} book{{ books|pluralize }}.</p>
    <ul>
        {% for book in books %}
        <li>{{ book.title }}</li>
        {% endfor %}
    </ul>
{% else %}
    <p> No books matched your search criteria. </p>
{% endif %}

 

 

在view.py中添加:

 

def search(request):
    error = False
    if 'q' in request.GET:
        q = request.GET['q']
        if not q:
            error = True
        else:
            books = Book.objects.filter(title__icontains=q)
            return render_to_response('search_results.html',
                    {'books':books,'query':q})
    return render_to_response('search_form.html',
            {'error':error})
    

 

 

 

其中Book.objects.filter(title__icontains=q) 是用来获取数据库中标题包含q的书籍。icontains是一个查询关键字。

 

在urls.py 中添加

 

  (r'search/$',search),

 


然后在浏览器输入 http://127.0.0.1:8000/search/  可以看到一个简单的搜索界面。

 

3. 简单的验证


可以用Javascript在客户端浏览器进行验证,但可能有些人会将Javascript关闭,并且还有一些怀有恶意的用户会尝试提交非法的数据来探测是否有可以攻击的机会。所以除了用Javascript在客户端浏览器进行验证外,还需要在服务器验证一次。上面的代码,只对 输入为空做了验证。下面添加一个验证搜索关键字是否小于20个字符的验证。


例子:

修改view.py

 

def search(request):
    errors = []
    if 'q' in request.GET:
        q = request.GET['q']
        if not q:
            errors.append('Enter a search term')
        elif len(q) > 20:
            errors.append('Please enter at most 20 characters.')
        else:
            books = Book.objects.filter(title__icontains=q)
            return render_to_response('search_results.html',
                    {'books':books,'query':q})
    return render_to_response('search_form.html',
            {'errors':errors})

 

 

修改 search_form.html :

 

<html>
<head>
    <title>Search</title>
</head>
<body>
    {% if errors  %}
        {% for error in errors%}
        <p style="color: red;">{{ error }}</p>
        {% endfor %}
    {% endif %}
    <form action='' method='get'>
        <input type='text' name='q'>
        <input type='submit' value='Search'>
    </form>
</body>
</html>

 

 

4. 编写Contact表单

 


一个较为复杂的例子:这个表单包括用户提交的反馈信息,一个可以选择填不填的e-mail地址。


view.py 中添加:

 

def contact(request):
    errors = []
    if request.method == 'POST':
        if not request.POST.get('subject', ''):
            errors.append('Enter a subject.')
        if not request.POST.get('message', ''):
            errors.append('Enter a message.')
        if request.POST.get('email') and '@' not in request.POST['email']:
            errors.append('Enter a valid e-mail address.')
        if not errors:
            send_mail(
                request.POST['subject'],
                request.POST['message'],
                request.POST.get('email', 'noreply@example.com'),
                ['siteowner@example.com'],
            )
            return HttpResponseRedirect('/contact/thanks/')
    return render_to_response('contact_form.html', {
        'errors': errors,
        'subject': request.POST.get('subject', ''),
        'message': request.POST.get('message', ''),
        'email': request.POST.get('email', ''),
    },context_instance=RequestContext(request))

 


用POST不用GET,因为这个表单会有一个服务器端的操作:send_mail。


在templates中添加contact_form.html :

 

<html>
<head>
    <title>Contact us</title>
</head>
<body>
    <h1>Contact us</h1>

    {% if errors %}
        <ul>
            {% for error in errors %}
            <li>{{ error }}</li>
            {% endfor %}
        </ul>
    {% endif %}

    <form action="/contact/" method="post">
        {% csrf_token %}
        <p>Subject: <input type="text" name="subject" value="{{ subject }}"></p>
        <p>Your e-mail (optional): <input type="text" name="email" value="{{ email }}"></p>
        <p>Message: <textarea name="message" rows="10" cols="50">{{ message }}</textarea></p>
        <input type="submit" value="Submit">
    </form>
</body>
</html>

 

 

这个例子看起来杂乱,解决方法看下面用forms。


5. from类


上面的那个例子,看起来杂乱,并且容易出错。

Django带有一个form库,称为django.forms,这个库可以处理HTML表单显示以及验证。


新建 forms.py 加入

 

from django import forms

class ContactForm(forms.Form):
    subject = forms.CharField()
    email = forms.EmailField(required=False)
    message = forms.CharField()

 

 

很像模块中用的语法。默认是每个字段必填的,如果要是可以不填的要指定required=False,就像上面的email字段一样。


来看看forms类到底是什么:它做的第一个是就是将自己显示成HTML

 

>>> from contact.forms import ContactForm
>>> f = ContactForm()
>>> print f
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" name="subject" type="text" /></td></tr>
<tr><th><label for="id_email">Email:</label></th><td><input id="id_email" name="email" type="email" /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input id="id_message" name="message" type="text" /></td></tr>

 

 

默认是按照 <table> 现实输出的,还可以:

 

>>> print f.as_ul()
<li><label for="id_subject">Subject:</label> <input id="id_subject" name="subject" type="text" /></li>
<li><label for="id_email">Email:</label> <input id="id_email" name="email" type="email" /></li>
<li><label for="id_message">Message:</label> <input id="id_message" name="message" type="text" /></li>
>>> print f.as_p()
<p><label for="id_subject">Subject:</label> <input id="id_subject" name="subject" type="text" /></p>
<p><label for="id_email">Email:</label> <input id="id_email" name="email" type="email" /></p>
<p><label for="id_message">Message:</label> <input id="id_message" name="message" type="text" /></p>

 


还可以这样显示

 

>>> print f['subject']
<input id="id_subject" name="subject" type="text" />

 


forms对象做的第二件事是校验数据。现在先添加数据:

 

>>> f = ContactForm({'subject': 'Hello', 'email': 'adrian@example.com', 'message': 'Nice site!'})
>>> 

 

 

一旦对一个forms对象实体赋值,就可以得到一个绑定的form:

 

>>> f.is_bound
True
>>> 

 


还可以验证数据是否合法

 

>>> f.is_valid()
True
>>> 

 

如果合法为True。如果不合法为False。如:如果subject或者,essage为空。f.is_valid()就会返回False


也可以查看每个字段的error

 

>>> f = ContactForm({'subject': 'Hello', 'message': ''})
>>> f['message'].errors
[u'This field is required.']
>>> f['subject'].errors
[]
>>> 

 

 

还可以通过errors属性查看错误

 

>>> f.errors
{'message': [u'This field is required.']}
>>> 

 

 


如果forms合法,他就有一个cleaned_data属性,将数据转化成Python类型数据,存放在cleaned_data中

例如:如果是字符串就会被清理成Unicode对象,整数会被清理成Python整数,日期清理成datetime.date型对象

 

>>> f = ContactForm({'subject': 'Hello', 'email': 'adrian@example.com', 'message': 'Nice site!'})
>>> f.is_valid()
True
>>> f.cleaned_data
{'message': u'Nice site!', 'email': u'adrian@example.com', 'subject': u'Hello'}

 

 

 

6. 在视图中使用Form对象

 

例子:

 

view.py

 

from django.shortcuts import render
from contact.forms import ContactForm
from django.http import HttpResponseRedirect
from django.core.mail import send_mail

def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            cd = form.cleaned_data
            send_mail(
                cd['subject'],
                cd['message'],
                cd.get('email', 'noreply@example.com'),
                ['siteowner@example.com'],
            )
            return HttpResponseRedirect('/contact/thanks/')
    else:
        form = ContactForm()
    return render(request, 'contact_form.html', {'form': form})

 

 

 

contact_form.html 

 

<html>
<head>
    <title>Contact us</title>
</head>
<body>
    <h1>Contact us</h1>

    {% if form.errors %}
        <p style="color: red;">
            Please correct the error{{ form.errors|pluralize }} below.
        </p>
    {% endif %}

    <form action="" method="post">
        <table>
            {{ form.as_table }}
        </table>
        {% csrf_token %}
        <input type="submit" value="Submit">
    </form>
</body>
</html>

 

 

上面的例子有个缺陷,message这个表单变成了 input type=“text”。

我们可以通过设置widget来修改它:

 

from django import forms

class ContactForm(forms.Form):
    subject = forms.CharField()
    email = forms.EmailField(required=False)
    message = forms.CharField(widget=forms.Textarea)

 

 

还可以设置最大长度

设置max_length

 

from django import forms

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=20)
    email = forms.EmailField(required=False)
    message = forms.CharField(widget=forms.Textarea)

 

 

设置初始值:

 

在view.py中ContectForm中添加 initial 参数:

 

from django.shortcuts import render
from contact.forms import ContactForm
from django.http import HttpResponseRedirect
from django.core.mail import send_mail

def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            cd = form.cleaned_data
            send_mail(
                cd['subject'],
                cd['message'],
                cd.get('email', 'noreply@example.com'),
                ['siteowner@example.com'],
            )
            return HttpResponseRedirect('/contact/thanks/')
    else:
        form = ContactForm(
            initial={'subject': 'AAAAAA'}
        )
    return render(request, 'contact_form.html', {'form': form})

 

注意:默认值与表单传入是有区别的,区别在于,如果仅仅传入默认值,是没有绑定的。

 

7. 自定义校验规则

 

例如我们需要添加 message 字段有一个额外的校验,我们就要摘forms类中添加 clean_message() 方法

 

例子:添加校验 messge中的单词数量(注意不是字母数量,看 split() )要不少于4个.

 

from django import forms

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    email = forms.EmailField(required=False)
    message = forms.CharField(widget=forms.Textarea)

    def clean_message(self):
        message = self.cleaned_data['message']
        num_words = len(message.split())
        if num_words < 4:
            raise forms.ValidationError("Not enough words!")
        return message