“D”不发音
文中所有交互的部分都是在 python manage.py shell 下执行的,
将数据驱动的 web 应用按数据存取逻辑、表现逻辑和业务逻辑划分结构的一种概念被称为 Model-View-Controller 模式(MVC)。这种模式在 Django 上的体现便是 MTV 模式,分别为负责数据库业务的 Model、负责页面表现的 Template 和负责组织业务的 View。
Project 可以看做是包含了用于创建和运行整个网站的所有文件的一个文件夹,而 app 则是 project 下一级目录内的多个子文件夹,每个 app 都包含有独立的功能,apps 的整体组成了整个网站。嘛,大约可以看做是“对象”和“方法”的关系吧。
创建 project 可以使用命令行:django-admin.py startproject site_name
django-admin.py 位于 Python 安装目录下的 Sripts 文件夹。这条命令会在执行目录下创建一个文件夹,文件夹内包含一个项目管理文件 manage.py 和一个与“site_name”同名的子文件夹,该文件夹内默认包含 __init__.py , settings.py , urls.py , wsgi.py 四个项目配置文件。
使用 manage.py 创建 app 后,appname 文件夹里面初始包含五个文件,__inti__.py 的功能与上面一样,其他为:
视图函数包含于项目文件夹和 apps 文件夹的 views 文件内。每一个函数可以处理一类业务,并返回一个相应页面。
对“什么样的 url 地址调用什么样的函数”的问题,则配置于项目文件夹内的 urls 文件内。urls 的 urlpatterns 变量在创建的时候就带有两条被注释起来的样例,以提醒 patterns 对象的标准语法:
urlpatterns = patterns( '' , # Examples: # url(r'^$', 'mysite.views.home', name='home'), # url(r'^blog/', include('blog.urls')), )
可以看到 patterns 对象的第一个参数是一个空字符串 '' ,这其实是一个公共视图前缀,将加在后面的每一个字符串函数路径的前面。
同时,为了对应存在多个不同的视图前缀的情况,比如给每一个 app 使用一个公共视图前缀,urlpatterns 对象是可以自加的( += ):
urlpatterns = patterns( ' app1.views ' , url(r ' ^app1_url$ ' , ' func_in_app1 ' ) ) urlpatterns += patterns( ' app2.views ' , url(r ' ^app2_url$ ' , ' func_in_app2 ' ) )
url 参数可由 urlpattern 以正则表达式的形式捕获,并自动传入视图函数。捕获方式与“子组”类似,使用 ( ) 来完成:
urlpatterns = patterns( '' , (r ' ^(\d{1,2})/$ ' , ' app.views.func ' ), )
这里的 (\d{1,2}) 会匹配一个一到两位的数字,并以字符串的形式传入视图函数,这类参数总是以字符串的形式传入。
另外在视图函数中,也应当做好参数类型检查,应当总是认为调用者是不可靠的。
除了使用 ( ) 捕获位置参数的方式外,还可以使用一种 (?P<name>pattern) 的形式捕获关键字参数 name=pattern:
urlpatterns = patterns( '' , url(r ' ^articles/(?P<year>\d{4})/$ ' , views.year_archive), url(r ' ^articles/(?P<year>\d{4})/(?P<month>\d{2})/$ ' , views.month_archive), )
虽然前面都没写,但 url 构造器其实还有可选的第三个参数—— 字典封装的关键字参数:
urlpatterns = patterns( '' , url(r ' ^foo/$ ' , views.foobar_view, { ' template_name ' : ' template1.html ' }), url(r ' ^bar/$ ' , views.foobar_view, { ' template_name ' : ' template2.html ' }), ) # views def foobar_view(request, template_name): m_list = MyModel.objects.filter(is_new = True) return render_to_response(template_name, { ' m_list ' : m_list})
1. 伪造捕捉到的 URLConf 值
如果某个视图函数定义了一些 url 参数,而你又想在一个无法提供完整参数的 url 下使用这个视图,那么你可以使用额外的参数来补完它。
2. 创建一个通用视图
待续...
3. 优先级问题
当捕捉到的命名组变量和额外参数发生冲突的时候,额外参数优先
当某种匹配模式有特殊情况,需要为其调用其他视图函数时,可以通过将这个特殊模式添加到 URLConf 的首位的方式来实现,因为 Django 对 urlpatterns 的解析是顺序的。
在最上面提到的,urls 文件内自带的两条注释起来的示例中,就有一个 include 的例子:
urlpatterns = patterns( '' , # Examples: # url(r'^$', 'mysite.views.home', name='home'), # url(r'^blog/', include('blog.urls')), )
可以看到他的特别之处在于:首先,url 表达式的结尾没有 $ ,而是一条 / ;其次,视图函数的地方换成了一个 include。
这个 include 的作用是引入了另一个 urls 文件。或者更确切的说,他会把已经匹配的部分截断,然后将剩余的 url 表达式传入 include 指向的 urls 文件内去匹配。这个功能类似于公共视图前缀,不过实现的方式不同,include 更适用于必须在多个位置配置不同 urls 文件的情况,或者直接使用已配置好的 urls 文件的情况(比如 admin 页面)。
注意:如果在使用 include 的时候,在父 URLConf 中捕获了参数,那么这个参数会被传入子 URLConf 中,子 URLConf 必须对此作出正确响应,否则会引发参数异常。这点对于额外的参数也是一样的。
视图函数的任务简单来说就是解析一个 HttpRequest 对象,然后返回一个 HttpResponse 对象。一个最简单的视图函数(什么也不做)看起来像是这样子的:
def foo(request): return HttpResponse( "" )
request 对象总是作为第一个参数传进来。
当大量视图函数有一些功能独立的公共代码时,可以把这些代码写成装饰器函数,并在定义视图函数时使用。比如下面登录认证的例子:
def requires_login(view): def new_view(request, * args, ** kwargs): if not request.user.is_authenticated(): return HttpResponseRedirect( ' /accounts/login/ ' ) return view(request, * args, ** kwargs) return new_view
request 对象包含的当前请求 url 的一些信息:
request.META 是一个 Python字典,包含了本次 HTTP 请求的 Header 信息。里面常见的键有:
应当使用 request.META.get() 方法来获取键的值,这样可以避免键不存在时引发的异常。
request 还有 GET 和 POST 属性,他们都是类字典对象,都有 get() , keys() , values() 方法。
使用 Form 类的一般方法是在 app 路径内新建一个 forms.py,在其中为模板的每个 <form> 元素建立一个对应的类。
from django import forms class ContactForm(forms.Form): subject = forms.CharField() email = forms.EmailField(required = False) message = forms.CharField()
其中 required=False 表示这个字段是可选项。
使用 print() 函数可以以 html 的形式显示实例化的表单对象,默认使用<table>标签,但也可以用<ul>和<form>标签显示。不过这三种标签最外层的开闭合标记并没有显示出来,以便于编辑其内容:
>>> 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 > >>> 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 > >>> 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 >
在实例化 Form 对象的时候,如果传入对应的字典,就可以得到一个绑定的 Form 对象
>>> f = ContactForm({ ' subject ' : ' Hello ' , ' email ' : ' [email protected] ' , ' message ' : ' Nice site! ' }) >>> f.is_bound True
绑定对象都有 is_valid() 方法
>>> f.is_valid() True
相应的也就有一个 errors 属性,这是一个字段名与其错误信息相对应的字典:
>>> f = ContactForm({ ' subject ' : ' Hello ' , ' message ' : '' }) >>> f.errors { ' message ' : [u ' This field is required. ' ]}
最后,如果一个绑定 Form 对象是有效的,那么它就会有一个 cleaned_data 属性,cleaned_data 字典里的值都是 Python 的数据类型:
>>> f.cleaned_data { ' message ' : ' Nice site! ' , ' email ' : ' [email protected] ' , ' subject ' : ' Hello ' }
def contact(request): if request.method == ' POST ' : form = ContactForm(request.POST) if form.is_valid(): cd = form.cleaned_data . . . return HttpResponseRedirect( ' /contact/thanks/ ' ) else : form = ContactForm() return render_to_response( ' contact_form.html ' , { ' form ' : form})
整个交互工作都是基于 Form 的。
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)
选项min_length参数同样可用。
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
Django的form系统自动寻找匹配的函数方法,该方法名称以clean_开头,并以字段名称结束。 如果有这样的方法,它将在校验时被调用。特别地,clean_message()方法将在指定字段的默认校验逻辑执行之后被调用。