• django表单


    表单是交互性网站的支柱。

    本文内容包括django对表单提交数据的访问,有效性检查以及其他处理,还有HttpRequest对象和Form对象。

    一、HttpRequest的URL相关信息

    定义views.py

    def current_url_view(request):
        return HttpResponse("Welcome to the page at %s,host is %s,full path is %s,is_secure is %s" % (request.path,request.get_host(),request.get_full_path(),request.is_secure()))

    可以显示出:

    Welcome to the page at /url/,host is 10.1.101.227:9000,full path is /url/,is_secure is False

    二、request.META

    显示所有META,在views.py里增加函数display_meta。

    def display_meta(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>' % '
    '.join(html))

    三、request.GET和request.POST

    POST数据是来自form表单提交的,GET数据可能来自form表单也可能来自url。

    当提交表单仅仅需要获取数据就使用GET,当提交表单需要更改服务器数据的状态或者发e-mail,或者其他不仅仅是获取显示数据的情况就使用POST。

    判断request.method的值能很好的帮助我们将表单显示[GET]表单处理[POST]隔离开来。

    四、GET表单处理的例子

    还是以书籍、作者、出版商为例。

    表单的开发分为两个部分:前端HTML页面用户接口和后台view函数对所提交数据的处理过程。

    1、通过一个特别简单的例子确认用户提交的数据是有效的

    首先建立前端html页面接口,即通过建立一个view来显示一个搜索表单,涉及三个文件:

    urls.py

    from books.views import *
    
    urlpatterns = patterns('',
        # ...
        (r'^search-form/$', search_form),
        # ...
    )
    View Code

    views.py

    liuxiaoyan@development:~/mysite/books$ cat views.py
    # Create your views here.
    from django.shortcuts import render_to_response
    
    def search_form(request):
        return render_to_response('search_form.html')
    View Code

    search_form.html模板

    liuxiaoyan@development:~/mysite$ cat templates/search_form.html 
    <html>
    <head>
        <title>Search</title>
    </head>
    <body>
        <form action="/search/" method="get">
            <input type="text" name="q">
            <input type="submit" value="Search">
        </form>
    </body>
    </html>
    View Code

    现在访问/search-form/,可以看到一个简单的搜索界面。你真正通过该form表单提交数据查找时会404,因为form指向的URL/search/还没有实现。现在实现。

    添加第二个视图函数并设置URL。

    urls.py

    liuxiaoyan@development:~/mysite$ cat urls.py
    from django.conf.urls.defaults import patterns, include, url 
    
    from views import *
    from books.views import *
    
    urlpatterns = patterns('',
        (r'^search-form/$',search_form),
        (r'^search/$',search),
    )
    View Code

     views.py

    liuxiaoyan@development:~/mysite/books$ cat views.py
    # Create your views here.
    from django.shortcuts import render_to_response
    from django.http import HttpResponse   
    
    def search_form(request):
        return render_to_response('search_form.html')
    def search(request):
        if 'q' in request.GET:
            message='You searched for : %r ' % request.GET['q']
        else:
            message='You submitted an empty form.'
        return HttpResponse(message)

    现在就可以显示用户搜索的词。我们可以看到搜索数据在django中的传递。

    a、在html里定义了一个变量q,当表单提交时,变量q的值通过GET(method="get")附加在URL/search/上。

    b、处理/search/的视图通过request.GET来获取q的值。明确判断过q是否包含在request.GET中。

    2、接下来从数据库查询这个有效的数据。

    修改search视图。

    liuxiaoyan@development:~/mysite/books$ cat views.py   
    # Create your views here.
    from django.shortcuts import render_to_response
    from django.http import HttpResponse   
    from books.models import Book #记得导入Book
    
    def search_form(request):
        return render_to_response('search_form.html')
    def search(request):
        if 'q' in request.GET and request.GET['q']: #判断q是否存在于requests.GET并且request.GET['q']是否为空
            q = request.GET['q']
            books = Book.objects.filter(title__icontains=q)#获取数据库中包含q的书籍,icontains是一个查询关键字,不区分大小写
            return render_to_response('search_results.html',
                {'books': books, 'query': q})#给模板传递一个Book对象列表
        else:
            return HttpResponse('Please submit a search term.')

    Note:大量数据的数据库不建议用icontains查询,因为非常慢。

    修改模板

    liuxiaoyan@development:~/mysite/templates$ cat 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 %}

    这里pluralize过滤器在适当的时候输出s(即books)。

    结果:

    3、改进表单 

    现在搜索表单对空字符串的处理相当薄弱,仅显示一条"Please submit a search term"提示信息,如下图,若用户要重新填写表单必须自行点击“后退”按钮,这种做法糟糕又不专业。

    在检查到空字符串时更好的解决方法是重新显示表单,并在表单上面给出错误提示以便用户立刻重新填写。最简单的方法就是添加else分句重新显示表单。

    视图

    liuxiaoyan@development:~/mysite/books$ cat views.py
    #
    Create your views here. from django.shortcuts import render_to_response from django.http import HttpResponse from books.models import Book def search_form(request): return render_to_response('search_form.html') def search(request): if 'q' in request.GET and request.GET['q']: q = request.GET['q'] books = Book.objects.filter(title__icontains=q) return render_to_response('search_results.html', {'books': books, 'query': q}) else: # return HttpResponse('Please submit a search term.')
        #字符串为空时,给search_form.html传递一个变量error,并重新显示
        return render_to_response('search_form.html',{'error':True})

    模板:需要检测error变量,调用search_form视图时,没有error变量,所以不会显示错误信息。

    liuxiaoyan@development:~/mysite/templates$ cat 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="/search/" method="get">
            <input type="text" name="q">
            <input type="submit" value="Search">
        </form>
    </body>
    </html>

    现在

    4、进一步改进表单

    没有必要专门编写search_form()来显示表单!!

    修改视图:

    liuxiaoyan@development:~/mysite/books$ cat views.py
    # Create your views here.
    from django.shortcuts import render_to_response
    from django.http import HttpResponse   
    from books.models import Book
    
    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})

    如果用户访问/search/并且没有带GET数据,那么他将看到一个没有错误信息的表单。(因为error=False,'q' in request.GET为假,直接执行最后一行跳转到search_form.html,且error为假,不显示提示信息);

    如果用户提交了一个空表单,他将看到错误提示信息,还有表单;(因为if not q为真,error被置为真,执行最后一行跳转到search_form.html,提示出错i型哪些);

    如果用户提交了一个非空值,他将看到出错结果。

    模板:去掉冗余代码

    liuxiaoyan@development:~/mysite/templates$ cat 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>

    Note:

    表单中action="/search/"改为search="",action=""意味着表单将提交给与当前页面相同的URL。

    5、提交数据有效性验证

    在客户端可用JavaScript对数据进行验证,同时在服务器端必须再验证一次,因为有些家伙可能关闭javascript对服务器攻击。以下介绍的是服务器端的验证。

    改进search()视图,让它验证搜索关键字是否小于或者等于20个字符。

    修改视图

    最简单的方式就是将逻辑嵌入到视图。要注意一点处理多种出错信息,所以不再使用布尔类型,而用列表来记录出错信息。

    liuxiaoyan@development:~/mysite/books$ cat views.py
    # Create your views here.
    from django.shortcuts import render_to_response
    from django.http import HttpResponse   
    from books.models import Book
    
    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})

    修改模板:

    liuxiaoyan@development:~/mysite/templates$ cat search_form.html 
    <html>
    <head>
        <title>Search</title>
    </head>
    <body>
        {% if errors %}
            <ul>
                {% for error in errors %}
                <li style="color:red">{{ error }}</li>
                {% endfor %}
            </ul>
        {% endif %}
        <form action="" method="get">
            <input type="text" name="q">
            <input type="submit" value="Search">
        </form>
    </body>
    </html>

    视图根据不同查找信息返回不同错误或者结果:

    五、POST表单处理的例子

    以下开始一个新的例子——站点联系表单,并且涉及服务器端的操作,所以使用POST,记住一点,我们应该每次都给成功的POST请求做重定向。

    因为站点联系表单跟books模型没有半毛钱关系,所以我们新建一个contact 的APP。

    liuxiaoyan@development:~/mysite$ python manage.py startapp contact

    表单功能:用户反馈,即表单包括用户提交的反馈信息,一个可选的e-mail回信地址,当这个表单提交并且数据通过验证后,系统将自动发送一封包含用户提交信息的e-mail给站点工作人员。【//lxy:其实就是根据用户提交信息发邮件,邮箱要自己给定】

    1、settings.py邮件配置

    因为涉及到发邮件,先说一下settings.py邮件相关配置

    配置:

    首先在MIDDLEWARE_CLASSES中注释掉'django.middleware.csrf.CsrfViewMiddleware'。

    然后配置好邮件代理服务器

    #send email 
    EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
    EMAIL_HOST='smtp.126.com' #SMTP服务器
    EMAIL_PORT = 25       #SMTP端口
    EMAIL_HOST_USER = 'starof@126.com' #用该邮箱发邮件,邮箱必须有效
    EMAIL_HOST_PASSWORD = '******'  #邮箱密码,必须正确
    EMAIL_USE_TLS = True #与SMTP服务器通信时,是否启动TLS连接(安全连接),默认为false

    配置好后可以先用一个小demo测试一下:

    liuxiaoyan@development:~/mysite$ cat testSendEmail.py
    from django.core.mail import send_mail 
    from django.http import HttpResponse
    
    def sendEmail(request):
            send_mail('subject', 'this is the message of email', 'starof@126.com', ['reseive@example.com'], fail_silently=True)
            return HttpResponse('successful')

    url配置

    from testSendEmail import *
    
     urlpatterns = patterns('',
    ...
        (r'^mail/$',sendEmail),
    ...
    )

    2、站点联系表单

    现在测试我们发邮件成功,说明配置正确,接下来回到contact。先看contact_form.html

    liuxiaoyan@development:~/mysite/templates$ cat 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="" method="post">
            {% csrf_token %} 
            <p>Subject:<input type="text" name="subject"></p>
            <p>Your e-mail (optional):<input type="text" name="email"></p>
            <p>Message:<textarea name="message" row="10" cols="50"></textarea></p>
            <input type="submit" value="Submit">
        </form>
    </body>
    </html>

    在视图中加入

    from django.core.mail import send_mail 

    liuxiaoyan@development:~/mysite/contact$ cat views.py
    # Create your views here.
    from django.core.mail import send_mail
    from django.http import HttpResponseRedirect
    from django.shortcuts import render_to_response
    from django.template import RequestContext
    
    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']: #弱弱的验证一下email
                errors.append('Enter a valid e-mail address.')
            if not errors:
                send_mail( #包括主题,正文,寄信人,收信人列表
                    request.POST['subject'], #request.POST获取提交过来的数据
                    request.POST['message'],
                    #request.POST.get('email', 'starof@126.com'),
                    'starof@126.com',  #settings.py中配置的发邮件的邮箱
                    ['925xxxx23@qq.com','xxx@xxx.com'], #收邮件的邮箱,可配置多个
                    fail_silently=True
                )
                return HttpResponseRedirect('/contact/thanks/') #邮件发送成功重定向页面
        return render_to_response('contact_form.html',{'errors': errors},context_instance=RequestContext(request))
        #return render_to_response('contact_form.html',{'errors': errors})
    def thank(request):
        return render_to_response('thank.html')

    邮件发送成功为什么用HttpResponseRedirect重定向而不用render_to_response输出?

    如果用户刷新一个包含POST表单的页面,那么请求将重新发送造成重复。会造成非期望的结果,比如重复的数据库记录,在本例将导致发送两封同样的邮件。如果用户在POST表单之后重定向至另外的页面,就不会造成重复的请求了。每次都应该给成功的POST做重定向,这是web开发的最佳实践。

    邮件发送后会跳转到/contact/thanks/的url,

    urls.py配置如下:

        (r'^contact/$',contact),
        (r'^contact/thanks/$', thank),

    所以再添加一个发邮件成功thank.html模板

    liuxiaoyan@development:~/mysite/templates$ cat thank.html 
    <html>
    <body>
    <h2>thank you very much!!!</h2>
    </body>
    </html>

    为了有更好的用户体验,让数据验证失败后,返回客户端的表单中各字段最好是填有原来提交的数据,以便用户查看哪里出现错误(用户也不需要再次填写正确的字段值)。手动将原来的提交数据返回给模板。

    liuxiaoyan@development:~/mysite/contact$ cat views.py
    # Create your views here.
    from django.core.mail import send_mail
    from django.http import HttpResponseRedirect
    from django.shortcuts import render_to_response
    from django.template import RequestContext
    
    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', 'starof@126.com'),
                    'starof@126.com',
                    ['xxx@xxx.com','9xxxx3@qq.com'],
                    fail_silently=True
                )
                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))
        #return render_to_response('contact_form.html',{'errors': errors})
    def thank(request):
        return render_to_response('thank.html')
    View Code

    3、可能出现的问题:CSRF verification failed. Request aborted.

    解决办法:

    第一步,在template中,为每个POSTform增加一个{% csrf_token %}标签。如下:

    <form>
        {% csrf_token %}
        ...
    </form>

    第二步,在view中,使用django.template.RequestContext而不是Context。render_to_response,默认使用Context,需要改成RequestContext。RequestContext会处理csrf_token这个tag,自动为表单添加一个名为csrfmiddlewaretoken的input。

    具体做法如下:

    导入class

    from django.template import RequestContext

    给render_to_response增加一个参数:

    def your_view(request):
        ...
        return render_to_response('template.html',
              your_data,
              context_instance=RequestContext(request)
        )

    原理:

    • HTTP响应时django自动添加一个csrftoken到cookie里,值是自动生成的token
    • 所有的POST表单,必须包含一个csrfmiddlewaretoken自动(打tag自动生成)
    • POST提交之前,django验证cookie里的csrftoken字段的值和提交的表单里的csrfmiddlewaretoken字段的值是否一样。一样表明请求合法。否则请求可能来自于别人的csrf攻击,返回403 Forbidden。

    六、在视图中使用Form对象

    Django自带一个form库,用做HTML表单显示以及验证。就是不用自己写页面的form表单,写个Form类再加一堆使用Form的代码django 自己给你一个活生生的表单,还能帮你验证。【//lxy:可能单纯的后台程序员会喜欢这样用,如果习惯写页面的人,更倾向于自己写个fashion的表单自己加js验证吧,但是存在即合理,看看这个传说中的djangoForm表单框架吧。此为个人观点】

    1、表单框架的原理

    为每一个将要处理的HTML"<Form>" 定义一个Form类,惯例是把Form类都放在一个文件中:forms.py。

    liuxiaoyan@development:~/mysite/contact$ cat forms.py
    from django import forms
    
    class ContactForm(forms.Form):
        subject=forms.CharField()
        email=forms.EmailField(required=False)
        message=forms.CharField()

    表单的每个字段(域)作为Form类的属性,被展现为Field类。字段默认都是必填的,要让email可选,特别指定reqired=False。

    现在到关键时刻了,看看Form框架幕后的工作:

    第一步,先看显示

    liuxiaoyan@development:~/mysite$ python manage.py shell
    Python 2.7.3 (default, Feb 27 2014, 19:58:35) 
    [GCC 4.6.3] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    (InteractiveConsole)
    >>> from contact.forms import ContactForm
    >>> f = ContactForm()
    >>> print f
    <tr><th><label for="id_subject">Subject:</label></th><td><input type="text" name="subject" id="id_subject" /></td></tr>
    <tr><th><label for="id_email">Email:</label></th><td><input type="text" name="email" id="id_email" /></td></tr>
    <tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>
    >>> 

    可以看到Djagno默认用<table>格式输出表单。

    如果用列表ul格式:

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

    如果用段落p格式:

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

    快捷显示

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

    第二步,再看校验数据。

    创建一个新的Form对象,并传入数据。

    liuxiaoyan@development:~/mysite$ python manage.py shell
    Python 2.7.3 (default, Feb 27 2014, 19:58:35) 
    [GCC 4.6.3] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    (InteractiveConsole)
    >>> from contact.forms import ContactForm
    >>> f = ContactForm({'subject': 'Hello', 'email': 'adrian@example.com', 'message': 'Nice site!'})
    >>> 

    给一个Form实体赋值后,就得到了一个绑定的form,之后在视图中也会这么用。

    >>> f.is_bound
    True

    调用任何已绑定form的is_valid()方法,就可以指定它的数据是否合法。

    现在什么值都给了,当然是合法的了。

    >>> f.is_valid()
    True

    如果不给subject或message赋值,整个Form就不再合法了。

    >>> f = ContactForm({'subject': 'Hello'}) 
    >>> f.is_valid()
    False
    >>> f = ContactForm({'subject': 'Hello', 'message': ''})
    >>> f.is_valid()
    False
    >>> 

    第三步,错误消息

    接下来看看错误消息,可通过每个字段来看,也可直接通过Form实体来看。

    逐一查看字段的出错消息:

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

    因为每个绑定的Form实体都有一个errors属性,提供一个字段与出错消息相映射的字典表。之后的模板中会用Form实体的errors属性去处理错误。

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

    第四步,cleaned_data

    如果一个Form实体的数据是合法的,它就会有一个可用的cleaned_data属性。这是一个包含干净的提交数据的字典。Django的Form框架不但校验数据,还将其转换成相应的Python类型数据,这叫做清理数据。

    >>> 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'}
    >>> 

    如果数据不合法会报错:

    >>> f = ContactForm({'subject': 'Hello', 'message': ''})
    >>> f.is_valid()  
    False
    >>> f.cleaned_data
    Traceback (most recent call last):
      File "<console>", line 1, in <module>
    AttributeError: 'ContactForm' object has no attribute 'cleaned_data'
    >>> 

    2、在视图中使用Form对象

    djangoForm框架页没什么神秘的,使用它将contact视图改版,确实会轻便不少,如果表单的数据很多的时候其优势就体现的更明显。视图代码:

    liuxiaoyan@development:~/mysite/contact$ cat views.py
    # Create your views here.
    from django.core.mail import send_mail
    from django.http import HttpResponseRedirect
    from django.shortcuts import render_to_response
    from django.template import RequestContext
    from forms import ContactForm #导入ContactForm表单类
    
    def contact(request):
        errors = []
        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','starof@126.com'),
                    'starof@126.com',
                    ['liuxiaoyan@windawn.com'],
                    fail_silently=True
                )
                return HttpResponseRedirect('/contact/thanks/')
        else:
            form=ContactForm()
        return render_to_response('contact_form.html',{'form':form}, context_instance=RequestContext(request))
    def thank(request):
        return render_to_response('thank.html')

    模板也要稍做处理:

    liuxiaoyan@development:~/mysite/templates$ cat 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="/contact/" method="post">
            {% csrf_token %}
            <table>
                {{form.as_table}}
            </table>
            <input type="submit" value="Submit">
        </form>
    </body>
    </html>

     Django的forms框架处理HTML显示、数据校验、数据清理和表单错误重现。

    看看效果,【//lxy:不得不感叹真是个丑陋的表单!!!】

    验证效果如下:

    发邮件完全正确:

    七、 自定义表单框架

    以上看到form框架原生的表单是不是丑到没朋友了。

    1、改进字段显示

    message字段被显示成"input type="text"",而它应该被显示成<"textarea">。

    liuxiaoyan@development:~/mysite/contact$ cat forms.py
    from django import forms
    
    class ContactForm(forms.Form):
        subject=forms.CharField()
        email=forms.EmailField(required=False)
        message=forms.CharField(widget=forms.Textarea)

    forms框架把每一个字段的显示逻辑分离到组件(widget)中。每个类型都有一个默认的组件,可以被替换。

    Field类表现“校验逻辑”,部件表现"显示逻辑"。

    2、设置最大长度

    liuxiaoyan@development:~/mysite/contact$ cat forms.py
    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)

    限制subject在100个字符以内。即为CharField提供max_length参数。超过100个字符就没法输入了,测试时可将100改为3来试一下。

    3、设置初始值

    为字段subject添加初始值"I love your site!"。通过在创建Form实体时,使用initial参数。

    liuxiaoyan@development:~/mysite/contact$ cat views.py
    # Create your views here.
    from django.core.mail import send_mail
    from django.http import HttpResponseRedirect
    from django.shortcuts import render_to_response
    from django.template import RequestContext
    from forms import ContactForm
    
    def contact(request):
        errors = []
        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','starof@126.com'),
                    'starof@126.com',
                    ['liuxiaoyan@windawn.com'],
                    fail_silently=True
                )
                return HttpResponseRedirect('/contact/thanks/')
        else:
            form=ContactForm(
                initial={'subject':'I love your site!'} #初始设置主题为I love your site!
            )
        return render_to_response('contact_form.html',{'form':form}, context_instance=RequestContext(request))
    def thank(request):
        return render_to_response('thank.html')

    仅传入初始值的表单是未绑定状态的。

    4、自定义校验规则(clean_xxx()方法的用法)

    大多数自定义校验都是一次性的,可以直接绑定到form。给"message"字段额外的校验,至少有4个单词。

    liuxiaoyan@development:~/mysite/contact$ cat forms.py   
    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,at least 4 words!")
            return message

    Django的form系统自动寻找以clean_开头并以字段名称结束的方法。如果有这样的方法,它将在默认校验的之后被调用。

    Note:

    在函数的末尾显示返回字段非常重要。如果忘记,原始数据就丢了。即form.cleaned_data['message] 这个值就为None,如果在模板用到这个值就哭去吧。

    5、指定标签

    Form表单标签生成规则为:空格代替下划线,首字母大写。如email的标签是"Email"(同样的逻辑被用于模型(model)中字段的verbose_name值)。现在将email自定义为"Your e-mail address"。

    liuxiaoyan@development:~/mysite/contact$ cat forms.py   
    from django import forms
    
    class ContactForm(forms.Form):
        subject=forms.CharField(max_length=100)
        email=forms.EmailField(required=False,label='Your e-mail address')
        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,at least 4 words!")
            return message

    6、定制Form设计

    之前在模板中使用{{form.as_table}}显示表单,这样自动生成在开发时快捷方便,但是生产环境,可能想要覆盖默认的显示,所以就要重写。

    每一个字段部件(<input type="text">,<select>,<textarea>等)都e抗原通过访问{{form.字段名}}进行单独渲染。这里将主动权交到我们手中了。

    liuxiaoyan@development:~/mysite/templates$ cat 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">
        {% csrf_token %}
            <div class="field">
                {{ form.subject.errors }}
                <label for="id_subject">Subject:</label>
                {{ form.subject }}
            </div>
            <div class="field">
                {{ form.email.errors }}
                <label for="id_email">Your e-mail address:</label>
                {{ form.email }}
            </div>
            <div class="field">
                {{ form.message.errors }}
                <label for="id_message">Message:</label>
                {{ form.message }}
            </div>
            <input type="submit" value="Submit">
        </form>
    </body>
    </html>

    {{form.message.errors}}会在<ul class="errorlist">里面显示,如果字段是合法的,或者form没有被绑定就显示一个空字符串。【//lxy:该ul是默认的】

    可以给它加上css样式,让li显示更突出。

    liuxiaoyan@development:~/mysite/templates$ cat contact_form.html
    <html>
    <head>
        <title>Contact us</title>
    <style type="text/css">
        .field{
            bakcground-color:blue;
        }   
        ul{ 
            margin: 0;
            padding: 0;
        }   
        .errorlist li {
            background-color: red;
            color: white;
            display: block;
            font-size: 10px;
            margin: 0 0 3px;
            padding: 4px 5px;
        }   
    </style>
    </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">
        {% csrf_token %}
            <div class="field">
                {{ form.subject.errors }}
                <label for="id_subject">Subject:</label>
                {{ form.subject }}
            </div>
            <div class="field">
                {{ form.email.errors }}
                <label for="id_email">Your e-mail address:</label>
                {{ form.email }}
            </div>
            <div class="field">
                {{ form.message.errors }}
                <label for="id_message">Message:</label>
                {{ form.message }}
            </div>
            <input type="submit" value="Submit">
        </form>
    </body>
    </html>
    View Code

    下面介绍两种用法,把form.message.errors当做一个布尔值或者当它是list在上面做迭代。【//lxy:只是说可以这么用,但你不一定要这么用】

    liuxiaoyan@development:~/mysite/templates$ cat contact_form.html
    <html>
    <head>
        <title>Contact us</title>
    <style type="text/css">
        .field{
            bakcground-color:blue;
        }   
        ul{ 
            margin: 0;
            padding: 0;
        }   
        .errorlist li {
            background-color: red;
            color: white;
            display: block;
            font-size: 10px;
            margin: 0 0 3px;
            padding: 4px 5px;
        }   
    </style>
    </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">
        {% csrf_token %}
            <div class="field {% if form.subject.errors %}errorlist{% endif %}">
                {% if form.message.errors %}
                    <ul>
                    {% for error in form.subject.errors %}                                                                                    
                        <li><strong>{{ error }}</strong></li>
                    {% endfor %}
                    </ul>
                {% endif %}
                <label for="id_subject">Subject:</label>
                {{ form.subject }}
            </div>
            <div class="field {% if form.email.errors %}errorlist{% endif %}">
                {% if form.message.errors %}
                    <ul>
                    {% for error in form.email.errors %}
                        <li><strong>{{ error }}</strong></li>
                    {% endfor %}
                    </ul>
                {% endif %}
                <label for="id_email">Your e-mail address:</label>
                {{ form.email }}
            </div>
            <div class="field {% if form.message.errors %}errorlist{% endif %}">
                {% if form.message.errors %}
                    <ul>
                    {% for error in form.message.errors %}
                        <li><strong>{{ error }}</strong></li>
                    {% endfor %}
                    </ul>
                {% endif %}
                <label for="id_message">Message:</label>
                {{ form.message }}
            </div>
            <input type="submit" value="Submit">
        </form>
    </body>
    </html>
    View Code

    效果和最后一张图一样。

    资源链接:

    http://djangobook.py3k.cn/2.0/chapter07/

  • 相关阅读:
    记一次css载入指定url失败
    更改MySQL密码后Navicat连接失败错误代码1045
    Maven项目中不显示Maven Dependenciesy依赖
    Mysql导入sql文件报错1064
    nexus-3.2.0-01.zip安装以及如何启动服务
    JS中函数的词法作用域
    关于JS中函数的返回值的一点死思考
    swich语句的小练习
    sublime的小技巧
    RPC failed; curl 18 transfer closed with outstanding read data remaining
  • 原文地址:https://www.cnblogs.com/starof/p/4271810.html
Copyright © 2020-2023  润新知