概述
http://www.cnblogs.com/ganiks/
6. 模板和表单处理
1 模板
1.1 理解Contexts
1.2 模板语言语法
1.2.1 模板过滤器
1.2.2 标签Tag
1.2.3 Blocks和Extends
跳出当前模板和其他模板交互
1.2.4 include
2 表单
2.1 定义表单
2.1.1 基于模型的表单
2.1.2 保存ModelForm
2.1.3 区别于Model
2.1.4 继承表单
2.2 填写表单
2.3 验证和清理
2.4 显示表单
2.4.1 显示全部表单
2.4.2 逐个显示表单
2.4.3 Widget
2.4.4 重写一个变量的默认widget
1 模板
Django的模板系统绝不仅仅是用于生成HTML,还有 log文件、Email正文、CSV文件等任何文本格式文件。
1.1 理解Contexts
Django 把传递给一个渲染模板的信息称之为 context, 实质上就是包含键值对的类似字典的 Context对象
准备Contexts有两种方式:
一种是上一篇中用到的,将Contexts作为参数传递
render_to_response("person/detail.html", {"person":person})
直接将数据作为render_to_response的参数传递;
或者在使用通用视图时把extra_context参数加上
return object_list(request,
queryset = Person.objects.filter(last__istartswith=last_name)
}
第二种方式是通过 context处理器, 这个东东类似中间件,可以定义各种函数,在模板渲染之前来把键值对附加到所有context上去。
这也是认证框架这样的特性,为什么能保证特定的数据在全站范围里都能访问到的原因。
def breadcrumb_processor(request):
return {
'breadcrumb': request.path.split('/')
}
通常处理器都放在根目录的 context_processors.py文件中或是app的目录中;
另外还需要在 settings.py中激活才可以使用, 通常是在一个叫做TEMPLATE_CONTEXT_PROCESSORS 的元组中, 类似中间件的激活。
1.2 模板语言语法
跟其他一些非XML的 模板语言如Smarty相比, Django的模板语言没有要保持 XHTML兼容性的意思,
而是用特殊的字符来把(模板变量以及逻辑命令)和静态内容(HTML)分开来。
跟其他模板语言一样,Django模板系统也有 单独命令 和 模块级命令 , 分别是{{ variable }}, {% command %}
下面的例子渲染context内容:{"title_text":"My WebPage", "object_list": ["One", "Two", "Three"]}
<html>
<head>
<title>{{ title_text }}</title>
</head>
<body>
<ul>
{% for item in object_list %}
<li>{{ item }}</li>
{% endfor %}
</ul>
</body>
</html>
陷阱:Django在模板中输出context变量的时候,会隐式调用 unicode方法, 所以对象以及其他非字符串变量会被尽量转换成 Unicode字符串。
如果你要输出没有定义 unicode 方法的对象,在模板里是看不到它们的,因为Python表示对象用的是 < > ,正好在html中啥也不是,故不显示。
>>> print object()
<object object at 0x40448>
1.2.1 模板过滤器
类似 UNIX 管道
- string|lower
- person.is_avaliable|yesno:"Yes,No"
1.2.2 标签Tag
if, ifequal, for, endfor, block, include, extends ...
{% ifequal object_list|length 10 %}
{% if object_list|lengthis 10 %}
模块级命令如 if for , 可以修改它们的局部 context, 很实用
如 for 提供了 {{ forloop }}局部变量, 用法有 forloop.first .last .counter .counter0
跳出当前模板和其他模板交互
通过模板继承和模板包含来完成代码的组合和重用
继承通过 blocks extends 实现;包含通过 include 实现
1.2.3 Blocks和Extends
{% extends %}
必须在模板的顶部调用,并告知渲染引擎这个模板是从一个更高级的模板继承而来
{% blocks %}
是一个模块级标签,一个模板中可以定义多个block, 预备让那些要扩展它的模板去填充的小节
举个例子,三层网站布局
-
/
- /section1/
-
/section2/
- /section1/page1/
- /section1/page2/
- /section1/page1/
- /section1/
#base.html
<html><head><title>{% block title %}My Web Site{% endblock %}</title></head></html>
<div id="content">{% block content %}{% endblock %}</div>
section1.html
{% extends "base.html" %}
{% block title %} Section1 {% endblock %}
page1.html
{% extends "section1.html" %}
{% block content %} This Page 1 {% endblock %}
page2.html
{% extends "section1.html" %}
{% block content %} This Page 2 {% endblock %}
1.2.4 include
很简单,类似PHP的include,实现代码重用
http://www.cnblogs.com/ganiks/p/django-template-and-forms.html
2 表单
Django提供了 forms 库来把框架里的三个主要的组件联系在一起:
- 模型里定义的数据库字段
- 模板里显示的HTML表单标签
- 验证用户输入和显示错误信息的能力
2.1 定义表单
表单处理的核心类Form 其实跟 Model 看上去是基本相同的, 它们都是处理 变量对象集合, 只是代表的意义不同,一个是Web表单,一个是数据表
有了Form类,我们不光可以轻松处理跟Model模型 100%匹配的表单,还可以隐藏或是省略特定变量,或是把多个model里的变量放在一个表单,或是处理和数据库存储毫无关系的表单, 非常灵活。
from django import newforms as forms
class PersonForm(forms.Form):
first = forms.CharField(max_length=100, required=True)
last = forms.CharField(max_length=100, required=True)
middle = forms.CharField(max_length=100)
2.1.1 基于模型的表单
Django允许你使用Form变形 ModelForm 类为任何 Model类或实例取得一个Form子类,
一个ModelForm和普通Form基本一样,但是包含了一个Meta嵌套类(类似Model里的Meta),内含一个必须的属性 model
下面重新定义下上面的 Form
from django import newforms as forms
from mysite.myapp.models import Person
class PersonForm(forms.ModelForm):
class Meta:
model = Person
完美体现了Django的DRY don't repeat yourself 的原则
2.1.2 保存ModelForm
基于模型创建的表单跟手动生成的表单有一个很重要的区别是, save方法
from mysite.myapp.forms import PersonForm
form = PersonForm({'first':'John', 'last':'Doe'})
new_person = form.save()
你经常会需要在数据,从表单提交后,存储到数据库之前,修改它们;
而最好还是在后一个时间段修改,因为这时候你修改的是Python值,而不是POST数据。
为了能让在save之前修改, save方法提供了一个可选的commit参数(默认是True), 控制你是否真的更新数据库。
from mysite.myapp.forms import PersonForm
form = PersonForm({'first':'John', 'last':'Doe'})
new_person = form.save(commit=False)
new_person.middle = 'Danger'
new_person.save()
如果使用 commit=False 来延迟保存的ModelForm包含了相关对象, Django会给表单(不是Model对象的结果)添加一个额外的方法 save_m2m, 让你正确安排时间的顺序。
form = PersonForm(input_including_related_objects)
new_person = form.save(commit=False)
new_person.middle = 'Danger'
new_person.save()
form.save_m2m()
如果没有最后的 save_m2m(), related objects会出问题的。
2.1.3 区别于Model
继承模型的表单是非常方便,但是有时候我们需要排除一些变量,并不是需要所有的变量,不需要重新写一个Form子类出来,有好几种方法:
from django import newforms as forms
from mysite.myapp.models import Person
class PersonForm(forms.ModelForm):
class Meta:
model = Person
exclude = ('middle', )
class PersonForm(forms.ModelForm):
class Meta:
model = Person
fields = ('first', 'last')
这样一来,忽略的变量就不会被save方法保存,所以忽略它时要确保DB中的定义是 null=True的,否则会报错。
有时候,还需要重写forms层里的Field子类,验证和显示特定的变量,如下重新定义了first字段的长度
class PersonForm(forms.ModelForm):
first = forms.CharField(max_length=10)
class Meta:
model = Person
关于“关系表单变量” relationship form fields
- ModelChoiceField -- ForeignKey
- ModelMultipleChoiceField -- ManyToManyField
还记得吗?当时Model定义外键和多对多关系的时候,使用了 limit_choices_to
参数, 其实等效的方法可以在表单层变量里自定义一个 queryset 参数, 用来接收一个 特定的 QuerySet 对象, 如下例子Person模型有一个指向其他Person对象的没有限制的父ForeignKey:
class PersonForm(forms.ModelForm):
class Meta:
model = Person
class SmithChildForm(forms.ModelForm):
parent = forms.ModelChoiceField(queryset=Person.objects.filter(last='Smith'))
class = Meta:
model = Person
2.1.4 继承表单
跟继承模型一样
from django import newforms as forms
class PersonForm(forms.Form):
first = forms.CharField()
last = forms.CharField()
class Person2Form(forms.Form):
first = forms.CharField()
last = forms.CharField()
class AgedPersonForm(PersonForm):
age = forms.IntergerField()
class MixedPerson(PersonForm, PersonForm2):
# mixed extends
2.2 填写表单
Django的表单库中有2种表单
- 绑定的 bound, 和数据有关系
- 没绑定的 unbound, 没有数据
表单有一个特性, 可以随便往表单的数据字典里添加额外的键值对, 表单会自动无视那些和它们定义无关的输入
from mysite.myapp.forms import PersonForm
def process_form(request):
post = request.POST.copy()
form = PersonForm(post)
还可以创建一个虽然未绑定,但是模板打印时载入显示了初始值的表单, 这个命名构造函数参数 initial 是一个字典
每个表单变量也都有一个类似的参数,允许指定它自己的默认值,不过如果有冲突的话, 表单级的 initial 会覆盖变量级的
from django import newforms as forms
from django.shotcuts import render_to_response
class PersonForm(forms.Form):
first = forms.CharField()
last = forms.CharField(initial='Smith')#表单定义时,变量级initial参数
middle = forms.CharField()
def process_form(request):
if not reuqest.POST:
form = PersonForm(initial={'first':'John'}) #创建表单实例时, 表单级initial参数,这里如果也定义了last, 则会覆盖上面的Smith
return render_to_response('myapp/form.html', {'form':form})
使用实例层initial参数的好处在于它的值可以在表单创建的时候才被构造出来, 这样一来,你可以引用在表单或是模型定义的时候还不知道的信息,特别是请求对象里的信息,看下面的例子
from django.shotcuts import get_object_or_404, render_to_response
from mysite.myapp.models import Person, PersonForm
# Views's URL /person/<id>/children/add
def add_relative(request, **kwargs):
if not request.POST:
relative = get_object_or_404(Person, pk=kwargs['id']
form = PersonForm(initial={'last': relative.last})
return render_to_response('person/form.html', {'form':form})
这例子中 parent 的 last 值是后来得到的,通过 initial 赋给form, 孩子自动填写好父亲的姓氏
http://www.cnblogs.com/ganiks/p/django-template-and-forms.html
2.3 验证和清理
要让表单运行验证程序,可以显式使用 is_valid 方法
if request.POST:
form = PersonForm(request.POST)
if form.is_valid:
new_person = form.save()
... ...
执行完验证后,表单对象会得到两个新属性“之一”:
- errors -- 包含错误信息的字典
- cleaned_data -- 原来绑定到表单的值的干净版本
- 干净数据意义在于, 输入的数据需要规范化 --- 从一种或多种可能的输入格式转换为一个统一的输出格式,以方便验证和数据库存储
- 因为 request.POST 中存储的格式一般都是字符串, 经过干净之后
- 数字变量的字符串 -- int、long
- 日期变量的字符串 -- datetime
2.4 显示表单
每个Django表单变量都知道自己在HTML标签里要怎么显示, 这种行为是通过 widget 实现的(后面介绍)
first = forms.CharField(max_length=100, required=True)
<tr>
<th>
<label for="id_first">First:</label>
</th>
<td>
<input id="id_first" type="text" name="first" maxlength="100" />
</td>
</tr>
pf = PersonForm(auto_id=False, label_suffix='')
<tr>
<th>
<label>First</label>
</th>
<td>
<input type="text" name="first" maxlength="100" />
</td>
</tr>
pf = PersonForm(auto_id='%s_id', label_suffix='?')
<tr>
<th>
<label for="first_id">First?</label>
</th>
<td>
<input id="first_id" type="text" name="first" maxlength="100" />
</td>
</tr>
2.4.1 显示全部表单
打印表单有多重方法
- as_table 默认
- as_ul
- as_p
2.4.2 逐个显示表单
2.4.3 Widget
每个Django表单变量都知道自己在HTML标签里要怎么显示, 这种行为是通过 widget 实现的, 这个widget子类(比如TextInput)接受了一个 attrs字典,能直接映射到HTML标签的属性上去。
middle = forms.CharField(max_length=100,
widget=forms.TextInput(attrs={'size':3})
<input id="id_middle" maxlength="100" type="text" name="middle" size="3" />
2.4.4 重写一个变量的默认widget
来自定义一个widget LargeTextarea, 默认拥有 40行和100列
forms.py
from django import newforms as forms
class LargeTextareaWidget(forms.Textarea):
def __init__(self, *args, **kwargs):
kwargs.setdefault('attrs', {}).update({'rows':40, 'cols':100})
super(LargeTextareaWidget, self).__init__(*args, **kwargs))
这里的 setdefault对字典用到一个技巧,在给定的键存在情况下回返回现有的值, 若给定的键不存在,则返回提供的值;
用在这里,是确保 kwargs 关键字参数字典一定共用 attrs 字典, 不管原来的构造函数参数是什么, 可以 update 来更新attrs字典添加我们需要的默认值。
views.py
from django import newsforms as forms
from mysite.myapp.forms import LargeTextareaWidget
class ContentForm(forms.Form):
name = forms.CharField()
markup = forms.ChoiceField(choices=[('markdown', 'Markdown'),('textile', 'Textile')])
text = forms.Textarea(widget=LargeTextareaWidget)
是不是还有优化空间?
Django就是Python, 有需要的时候, 很容易把各种类和对象替换出去, 从而达到更灵活的自定义。
forms.py
from django import newforms as forms
class LargeTextareaWidget(forms.Textarea):
def __init__(self, *args, **kwargs):
kwargs.setdefault('attrs', {}).update({'rows':40, 'cols':100})
super(LargeTextareaWidget, self).__init__(*args, **kwargs))
class LargeTextarea(forms.Field):
widget = LargeTextareaWidget
现在可以更方便的使用这个自定义的widget
views.py
from django import newsforms as forms
from mysite.myapp.forms import LargeTextareaWidget
class ContentForm(forms.Form):
name = forms.CharField()
markup = forms.ChoiceField(choices=[('markdown', 'Markdown'),('textile', 'Textile')])
#text = forms.Textarea(widget=LargeTextareaWidget)
text = LargeTextarea()
出处: http://www.cnblogs.com/ganiks/
本作品由 Ganiks 创作, 欢迎转载,但任何转载必须保留完整文章,在显要地方显示署名以及原文链接。如您有任何疑问,请给我留言。
最后
以上就是等待裙子为你收集整理的跟我一起Django - 06 模板和表单处理1 模板2 表单的全部内容,希望文章能够帮你解决跟我一起Django - 06 模板和表单处理1 模板2 表单所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复