“D”不发音
文中所有交互的部分都是在 python manage.py shell 下执行的,
MTV 模式:
将数据驱动的 web 应用按数据存取逻辑、表现逻辑和业务逻辑划分结构的一种概念被称为 Model-View-Controller 模式(MVC)。这种模式在 Django 上的体现便是 MTV 模式,分别为负责数据库业务的 Model、负责页面表现的 Template 和负责组织业务的 View。
Project & App
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 四个项目配置文件。
- __init__.py 告诉 Python 这个文件夹是一个包
- manage.py 内容很简单,他的功能就是专门用来执行命令行的——“execute_from_command_line(sys.argv)”。比如“python manage.py startapp appname”这条命令就可以创建一个 app。他之所以与其他配置文件独立出来是为了“好看”。。。
- settings.py 配置一些乱七八糟的项目设定
- urls.py 负责定义网站 url 与视图(view)之间的映射关系,就是设定什么样的地址返回什么样的页面。这个文件的主体是一个 urlpatterns 对象,该对象由多条 url 对象组成,url 对象的构造器接受两个参数,一是网站 url 的正则表达式,另一是匹配该表达式的视图对象。
- wsgi.py 提供一个 wsgi 可调用对象以供 server 使用
使用 manage.py 创建 app 后,appname 文件夹里面初始包含五个文件,__inti__.py 的功能与上面一样,其他为:
- admin.py 用于将模型注册到管理界面
- models.py 创建本 app 的数据模型
- tests.py 单元测试文件
- views.py 定义视图函数
View
视图函数包含于项目文件夹和 apps 文件夹的 views 文件内。每一个函数可以处理一类业务,并返回一个相应页面。
URLConfs:
对“什么样的 url 地址调用什么样的函数”的问题,则配置于项目文件夹内的 urls 文件内。urls 的 urlpatterns 变量在创建的时候就带有两条被注释起来的样例,以提醒 patterns 对象的标准语法:
urlpatterns
=
patterns(
''
,
#
Examples:
#
url(r'^$', 'mysite.views.home', name='home'),
#
url(r'^blog/', include('blog.urls')),
)
- url 构造器的第一个参数是正则表达式,用于匹配 url 中域名+"/" 后面的部分;
- 第二个参数是视图函数,格式一般为 'app.views.func_name',使用字符串路径而不是对函数对象的引用可以避免在文件开头 import 太多内容;
- 第三个参数是传递给视图函数的可选的关键字参数,要求以字典的形式提供,上例中用的是关键字参数格式,应该也可以吧。
公共视图前缀
可以看到 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 参数
url 参数可由 urlpattern 以正则表达式的形式捕获,并自动传入视图函数。捕获方式与“子组”类似,使用 ( ) 来完成:
urlpatterns
=
patterns(
''
,
(r
'
^(\d{1,2})/$
'
,
'
app.views.func
'
),
)
这里的 (\d{1,2}) 会匹配一个一到两位的数字,并以字符串的形式传入视图函数,这类参数总是以字符串的形式传入。
另外在视图函数中,也应当做好参数类型检查,应当总是认为调用者是不可靠的。
url 命名正则表达式
除了使用 ( ) 捕获位置参数的方式外,还可以使用一种 (?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 的解析是顺序的。
包含其他URLConf ( include )
在最上面提到的,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
HttpRequest
request 对象包含的当前请求 url 的一些信息:
- request.path 除域名以外的请求路径,以“/”开头
- request.get_host() 主机名(域名)
- request.get_full_path() 除域名外的请求路径,包含查询字符串
- request.is_secure() 是否通过 HTTPS 访问
request.META 是一个 Python字典,包含了本次 HTTP 请求的 Header 信息。里面常见的键有:
- HTTP_REFERER 前一个页面的地址
- HTTP_USER_AGENT 客户端的浏览器
- REMOTE_ADDR 客户端的 IP
应当使用 request.META.get() 方法来获取键的值,这样可以避免键不存在时引发的异常。
request 还有 GET 和 POST 属性,他们都是类字典对象,都有 get() , keys() , values() 方法。
Forms 库
使用 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
'
:
'
adrian@example.com
'
,
'
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
'
:
'
adrian@example.com
'
,
'
subject
'
:
'
Hello
'
}
在视图中使用 Form:
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()方法将在指定字段的默认校验逻辑执行之后被调用。
来源:oschina
链接:https://my.oschina.net/u/660175/blog/214802