在第三章,我们已经对基本的Django视图和URL配置做了介绍。 在这一章,将进一步说明框架中这两个部分的高级机能。
URLconf 技巧
URLconf没什么特别的,就象 Django 中其它东西一样,它们只是 Python 代码。 你可以在几方面从中得到好处,正如下面所描述的。
流线型化(Streamlining)函数导入
看下这个 URLconf,它是建立在第三章的例子上:
from django.conf.urls.defaults import *
from mysite.views import hello, current_datetime, hours_ahead
urlpatterns = patterns('',
(r'^hello/$', hello),
(r'^time/$', current_datetime),
(r'^time/plus/(\d{1,2})/$', hours_ahead),
)
正如第三章中所解释的,在 URLconf 中的每一个入口包括了它所关联的视图函数,直接传入了一个函数对象。 这就意味着需要在模块开始处导入视图函数。
但随着 Django 应用变得复杂,它的 URLconf 也在增长,并且维护这些导入可能使得管理变麻烦。 (对每个新的view函数,你不得不记住要导入它,并且采用这种方法会使导入语句将变得相当长。)可以通过导入 views 模块本身来避免这个麻烦。 下面例子的URLconf与前一个等价:
from django.conf.urls.defaults import *
**from mysite import views**
urlpatterns = patterns('',
(r'^hello/$', **views.hello** ),
(r'^time/$', **views.current_datetime** ),
(r'^time/plus/(d{1,2})/$', **views.hours_ahead** ),
)
我也是'module' object has no attribute 'views' 改成这个就行了 from books.views import * from contact.views import contact from views import * 不过觉得这样不好
多个View的时候,将该app的Views全部导入就好了。例如import books.views。在后面引用时就变成(r'^search-form/$', books.views.search_form)。这样直接看url也知道他是哪个app的视图了。
我想请教一个问题,如果我有两个app应用,一个是app1,一个是app2,他们下面都有一个views。py视图文件,文件里面都包含一个hello的视图。那么我在使用from app1 import views和from app2 import views的时候,如果想使用hello的话,使用views.hello他会给我调用哪一个呢???
谢楼上 django 1.11 from app1 import views as app1_views from app1 import views as app2_views
Django 还提供了另一种方法可以在 URLconf 中为某个特别的模式指定视图函数: 你可以传入一个包含模块名和函数名的字符串,而不是函数对象本身。 继续示例:
我用这种方法怎么不行, (r'^search-form/$', 'mysite.books.views.search_form'),404 错误。 The current URL, search_form, didn't match any of these.
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^hello/$', **'mysite.views.hello'** ),
(r'^time/$', **'mysite.views.current_datetime'** ),
(r'^time/plus/(d{1,2})/$', **'mysite.views.hours_ahead'** ),
)
使用1.11版本的django url(r'^hello/$', views,hello),#错误 url(r'^hello/$', mysite.views.hello),#错误 url(r'^hello/$', include(mysite.views.hello)),#错误 url(r'^hello/$', hello),#成功
(注意视图名前后的引号。 应该使用带引号的 'mysite.views.current_datetime' 而不是 mysite.views.current_datetime 。)
使用这个技术,就不必导入视图函数了;Django 会在第一次需要它时根据字符串所描述的视图函数的名字和路径,导入合适的视图函数。
当使用字符串技术时,你可以采用更简化的方式:提取出一个公共视图前缀。 在我们的URLconf例子中,每个视图字符串的开始部分都是[`](#id1)\,造成重复输入。 我们可以把公共的前缀提取出来,作为第一个参数传给\ ```函数:
Inline literal start-string without end-string.
原文是: A further shortcut you can take when using the string technique is to factor out a common "view prefix". In our URLconf example, each of the view strings starts with "mysite.views", which is redundant to type. We can factor out that common prefix and pass it as the first argument to patterns()
from django.conf.urls.defaults import *
urlpatterns = patterns(**'mysite.views'** ,
(r'^hello/$', **'hello'** ),
(r'^time/$', **'current_datetime'** ),
(r'^time/plus/(d{1,2})/$', **'hours_ahead'** ),
)
没有人在 from django.conf.urls.defaults import * 这个语句出现问题么?*的意思是什么呢? 我用的是from django.conf.urls import patterns, include, url
https://docs.djangoproject.com/en/1.10/topics/http/urls/ django1.10的看下这个吧。 我使用的: from mysite import views as mysite_views from contact import views as contact_views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^hello/$', mysite_views.hello), ...... ]
注意既不要在前缀后面跟着一个点号("." ),也不要在视图字符串前面放一个点号。 Django 会自动处理它们。
牢记这两种方法,哪种更好一些呢? 这取决于你的个人编码习惯和需要。
字符串方法的好处如下:
- 更紧凑,因为不需要你导入视图函数。
- 如果你的视图函数存在于几个不同的 Python 模块的话,它可以使得 URLconf 更易读和管理。
函数对象方法的好处如下:
- 更容易对视图函数进行包装(wrap)。 参见本章后面的《包装视图函数》一节。
- 更 Pythonic,就是说,更符合 Python 的传统,如把函数当成对象传递。
两个方法都是有效的,甚至你可以在同一个 URLconf 中混用它们。 决定权在你。
在实践中,如果你使用字符串技术,特别是当你的 URLconf 中没有一个公共前缀时,你最终可能混合视图。 然而,你仍然可以利用视图前缀的简便方式来减少重复。 只要增加多个 patterns() 对象,象这样:
旧的:
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^hello/$', 'mysite.views.hello'),
(r'^time/$', 'mysite.views.current_datetime'),
(r'^time/plus/(\d{1,2})/$', 'mysite.views.hours_ahead'),
(r'^tag/(\w+)/$', 'weblog.views.tag'),
)
新的:
@yhben, (r'^admin/', include('django.contrib.admin.site.urls')) Django 1.6 按照你这个提示找不到 site.urls 还是用回非字符串方式
django 1.6之后这个defaults就deprecate用不了了。 1.6之后应该这样写: from django.conf.urls import url, patterns urlpatterns = patterns('mysite.views', url (r'^hello/$', 'hello'), url (r'^time/$', 'current_datetime'), url (r'^time/plus/(\d{1,2})/$', 'hours_ahead'), ) urlpatterns += patterns('weblog.views', url (r'^tag/(\w+)/$', 'tag'), )
Deprecated since version 1.8: urlpatterns should be a plain list of django.conf.urls.url() instances instead. 貌似不能用了
from django.conf.urls.defaults import *
urlpatterns = patterns('mysite.views',
(r'^hello/$', 'hello'),
(r'^time/$', 'current_datetime'),
(r'^time/plus/(\d{1,2})/$', 'hours_ahead'),
)
urlpatterns += patterns('weblog.views',
(r'^tag/(\w+)/$', 'tag'),
)
django2里面 类似是下面的: urlpatterns = ( url(r'^hello/$', hello), url(r'^time/$', current_datetime), url(r'^time/plus/(\d{1,2})/$', hours_ahead), ) urlpatterns += ( url(r'^tag/(\w+)/$', tag), )
整个框架关注的是存在一个名为 urlpatterns 的模块级别的变量。如上例,这个变量可以动态生成。 这里我们要特别说明一下,patterns()返回的对象是可相加的,这个特性可能是大家没有想到的。
现在已经不必要这么做了,在一个项目中存在多个app的时候可以命名2个urlpatterns模块,然后导入不同app下面的views就可以了,app之间views访问通过include引入,还简便了很多
调试模式中的特例
说到动态构建 urlpatterns,你可能想利用这一技术,在 Django 的调试模式下修改 URLconf 的行为。 为了做到这一点,只要在运行时检查 DEBUG 配置项的值即可,如:
from django.conf import settings
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^$', views.homepage),
(r'^(\d{4})/([a-z]{3})/$', views.archive_month),
)
if settings.DEBUG:
urlpatterns += patterns('',
(r'^debuginfo/$', views.debug),
)
这里 :urlpatterns += patterns('weblog.views', (r'^tag/(\w+)/$', 'tag'), python有‘+=’这个运算符 ?? 没有吧 !
在这个例子中,URL链接/debuginfo/ 只在你的 DEBUG 配置项设为 True 时才有效。
使用命名组
在目前为止的所有 URLconf 例子中,我们使用简单的无命名 正则表达式组,即,在我们想要捕获的URL部分上加上小括号,Django 会将捕获的文本作为位置参数传递给视图函数。 在更高级的用法中,还可以使用 命名 正则表达式组来捕获URL,并且将其作为 关键字 参数传给视图。
关键字参数 对比 位置参数
一个 Python 函数可以使用关键字参数或位置参数来调用,在某些情况下,可以同时进行使用。 在关键字参数调用中,你要指定参数的名字和传入的值。 在位置参数调用中,你只需传入参数,不需要明确指明哪个参数与哪个值对应,它们的对应关系隐含在参数的顺序中。
例如,考虑这个简单的函数:
def sell(item, price, quantity):
print "Selling %s unit(s) of %s at %s" % (quantity, item, price)
为了使用位置参数来调用它,你要按照在函数定义中的顺序来指定参数。
sell('Socks', '$2.50', 6)
为了使用关键字参数来调用它,你要指定参数名和值。 下面的语句是等价的:
sell(item='Socks', price='$2.50', quantity=6)
sell(item='Socks', quantity=6, price='$2.50')
sell(price='$2.50', item='Socks', quantity=6)
sell(price='$2.50', quantity=6, item='Socks')
sell(quantity=6, item='Socks', price='$2.50')
sell(quantity=6, price='$2.50', item='Socks')
>>> 这里的例子没有充分利用函数关键字参数的特性,文中例子如果使用位置参数和默认参数搭配更适合,不容易产生误解。 >>> 至于关键字参数的用法,推荐看下廖雪峰的函数的参数讲解:https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001374738449338c8a122a7f2e047899fc162f4a7205ea3000
最后,你可以混合关键字和位置参数,只要所有的位置参数列在关键字参数之前。 下面的语句与前面的例子是等价:
sell('Socks', '$2.50', quantity=6)
sell('Socks', price='$2.50', quantity=6)
sell('Socks', quantity=6, price='$2.50')
在 Python 正则表达式中,命名的正则表达式组的语法是 (?P<name>pattern) ,这里 name 是组的名字,而 pattern 是匹配的某个模式。
下面是一个使用无名组的 URLconf 的例子:
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^articles/(\d{4})/$', views.year_archive),
(r'^articles/(\d{4})/(\d{2})/$', views.month_archive),
)
(r'^articles/(\d{4})/(\d{2})/$', views.month_archive), 很不明白这里的参数和URL是如何对应的,难道在解析RUL的时候如果遇到正在表达式就认为是后面视图方法的参数吗?
回复Rex, django这种做法短期来看是很不利于初学者,但是从长远来看,是为了将各种各样的url进行正则解析并把解析出来的各种参数传递到view函数中去,试想,django仅用了正则表达式就完成了如此复杂的功能,应该很牛逼很友好了,如果用其他方式实现估计更难懂
下面是相同的 URLconf,使用命名组进行了重写:
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^articles/(?P<year>\d{4})/$', views.year_archive),
(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', views.month_archive),
)
@zebiak 正则表达式里的语法,这里的作用是给正则匹配到的数据('2006')加了一个名字(year),django自动调用视图的时候可以用 名字=值 (year='2006')的形式传参数。 month_archive(request, year='2006', month='03')
TypeError: view must be a callable or a list/tuple in the case of include().用的是11版本这是咋回事
这段代码和前面的功能完全一样,只有一个细微的差别: 取的值是以关键字参数的方式而不是以位置参数的方式传递给视图函数的。
例如,如果不带命名组,请求 /articles/2006/03/ 将会等同于这样的函数调用:
month_archive(request, '2006', '03')
而带命名组,同样的请求就会变成这样的函数调用:
month_archive(request, year='2006', month='03')
使用命名组可以让你的URLconfs更加清晰,减少搞混参数次序的潜在BUG,还可以让你在函数定义中对参数重新排序。 接着上面这个例子,如果我们想修改URL把月份放到 年份的 前面 ,而不使用命名组的话,我们就不得不去修改视图 month_archive 的参数次序。 如果我们使用命名组的话,修改URL里提取参数的次序对视图没有影响。
>>> month_archive(request, year='2006', month='03') 文中例子表达函数关键字参数的特点不够清晰。 >>> 改写成如下方式更清晰. >>> kw={'year': '2006', 'month': '03'} >>> month_archive(request, **kw) >>> 这样避免存在函数内存在默认参数定义的影响。
当然,命名组的代价就是失去了简洁性: 一些开发者觉得命名组的语法丑陋和显得冗余。 命名组的另一个好处就是可读性强。
理解匹配/分组算法
需要注意的是如果在URLconf中使用命名组,那么命名组和非命名组是不能同时存在于同一个URLconf的模式中的。 如果你这样做,Django不会抛出任何错误,但你可能会发现你的URL并没有像你预想的那样匹配正确。 具体地,以下是URLconf解释器有关正则表达式中命名组和 非命名组所遵循的算法:
- 如果有任何命名的组,Django会忽略非命名组而直接使用命名组。
- 否则,Django会把所有非命名组以位置参数的形式传递。
- 在以上的两种情况,Django同时会以关键字参数的方式传递一些额外参数。 更具体的信息可参考下一节。
传递额外的参数到视图函数中
有时你会发现你写的视图函数是十分类似的,只有一点点的不同。 比如说,你有两个视图,它们的内容是一致的,除了它们所用的模板不太一样:
# urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^foo/$', views.foo_view),
(r'^bar/$', views.bar_view),
)
# views.py
from django.shortcuts import render_to_response
from mysite.models import MyModel
def foo_view(request):
m_list = MyModel.objects.filter(is_new=True)
return render_to_response('template1.html', {'m_list': m_list})
def bar_view(request):
m_list = MyModel.objects.filter(is_new=True)
return render_to_response('template2.html', {'m_list': m_list})
我们在这代码里面做了重复的工作,不够简练。 起初你可能会想,通过对两个URL都使用同样的视图,在URL中使用括号捕捉请求,然后在视图中检查并决定使用哪个模板来去除代码的冗余,就像这样:
# urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^(foo)/$', views.foobar_view),
(r'^(bar)/$', views.foobar_view),
)
# views.py
from django.shortcuts import render_to_response
from mysite.models import MyModel
def foobar_view(request, url):
m_list = MyModel.objects.filter(is_new=True)
if url == 'foo':
template_name = 'template1.html'
elif url == 'bar':
template_name = 'template2.html'
return render_to_response(template_name, {'m_list': m_list})
urlpatterns = patterns('', (r'^(foo)/$', views.foobar_view), (r'^(bar)/$', views.foobar_view), ) 这个地方也要加参数才行啊, urlpatterns = patterns('', url(r'^foo/$',foo_view,{'url':'foo'}), url(r'^bar/$',foo_view,{'url':'bar'}), )
urlpatterns = patterns('', (r'^(foo|bar)/$', views.foobar_view, {'foo': 'template1.html', 'bar': 'template2.html'}), )
这种解决方案的问题还是老缺点,就是把你的URL耦合进你的代码里面了。 如果你打算把 /foo/ 改成 /fooey/ 的话,那么你就得记住要去改变视图里面的代码。
对一个可选URL配置参数的优雅解决方法: URLconf里面的每一个模式都可以包含第三个数据: 一个关键字参数的字典:
有了这个概念以后,我们就可以把我们现在的例子改写成这样:
# urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^foo/$', views.foobar_view, {'template_name': 'template1.html'}),
(r'^bar/$', views.foobar_view, {'template_name': 'template2.html'}),
)
# views.py
from django.shortcuts import render_to_response
from mysite.models import MyModel
def foobar_view(request, template_name):
m_list = MyModel.objects.filter(is_new=True)
return render_to_response(template_name, {'m_list': m_list})
如你所见,这个例子中,URLconf指定了 template_name 。 而视图函数会把它当成另一个参数。
这种使用额外的URLconf参数的技术以最小的代价给你提供了向视图函数传递额外信息的一个好方法。 正因如此,这技术已被很多Django的捆绑应用使用,其中以我们将在第11章讨论的通用视图系统最为明显。
下面的几节里面有一些关于你可以怎样把额外URLconf参数技术应用到你自己的工程的建议。
比如说你有匹配某个模式的一堆视图,以及一个并不匹配这个模式但视图逻辑是一样的URL。 这种情况下,你可以通过向同一个视图传递额外URLconf参数来伪造URL值的捕捉。
例如,你可能有一个显示某一个特定日子的某些数据的应用,URL类似这样的:
/mydata/jan/01/
/mydata/jan/02/
/mydata/jan/03/
# ...
/mydata/dec/30/
/mydata/dec/31/
这太简单了,你可以在一个URLconf中捕捉这些值,像这样(使用命名组的方法):
urlpatterns = patterns('',
(r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$', views.my_view),
)
r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$'中的(?P<month>\w{3}) 捕获\w{3},赋值给P<month> \d\d 捕获赋值给P<day> 。
(r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$', views.my_view)不该次而成这样吗url(r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$', views.my_view)。这里不该是一个url函数吗
使用django2.0.3的话,这里可以引入re_path,即from django.urls import path,re_path,相对应的将path该为re_path,之后就可使用正则表达式。
然后视图函数的原型看起来会是:
def my_view(request, month, day):
# ....
总结一下两点: 1.非命名组和非命名组通过捕捉url中的命名(或者非命名)变量对视图函数参数进行传递赋值 2.视图函数中的参数也可以通过url中第三个字典变量进行赋值。比方说:选择相应的模板名(template_name)进行渲染
这种解决方案很直接,没有用到什么你没见过的技术。 当你想添加另外一个使用 my_view 视图但不包含month和/或者day的URL时,问题就出现了。
比如你可能会想增加这样一个URL, /mydata/birthday/ , 这个URL等价于 /mydata/jan/06/ 。这时你可以这样利用额外URLconf参数:
urlpatterns = patterns('',
(r'^mydata/birthday/$', views.my_view, {'month': 'jan', 'day': '06'}),
(r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$', views.my_view),
)
在这里最帅的地方莫过于你根本不用改变你的视图函数。 视图函数只会关心它 获得 了 参数,它不会去管这些参数到底是捕捉回来的还是被额外提供的。month和day
总结一下: 通过url传递给视图函数参数的方法, [1]通过位置参数(无命名组),将url中小括号()中的内容以位置参数(普通参数)的方式传到视图函数的参数列表里。(r'^time/plus/(\d{1,2})/$', hours_ahead), --> 视图函数 def hours_ahead(request, offset): ... [2]通过关键字参数(**arg 有名名组),将小括号()中的内容以关键字参数的方式传到视图函数的参数列表里。 (r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', views.month_archive), --> 视图函数 month_archive(request, year='2006', month='03') [3]通过传递额外的参数(rul中,一个关键字参数的字典)到视图函数中。 (r'^foo/$', views.foobar_view, {'template_name': 'template1.html'}), --> def foobar_view(request, template_name):...
抽取出我们代码中共性的东西是一个很好的编程习惯。 比如,像以下的两个Python函数:
def say_hello(person_name):
print 'Hello, %s' % person_name
def say_goodbye(person_name):
print 'Goodbye, %s' % person_name
我们可以把问候语提取出来变成一个参数:
def greet(person_name, greeting):
print '%s, %s' % (greeting, person_name)
感觉这样是徒增了复杂度,牺牲了代码的可读性 我也许会先def sayHelloTo(name), def sayGreatTo(name),然后到我需要重复的增加不同的问候语的时候再去考虑逻辑的整合
通过使用额外的URLconf参数,你可以把同样的思想应用到Django的视图中。
了解这个以后,你可以开始创作高抽象的视图。 更具体地说,比如这个视图显示一系列的 Event 对象,那个视图显示一系列的 BlogEntry 对象,并意识到它们都是一个用来显示一系列对象的视图的特例,而对象的类型其实就是一个变量。
以这段代码作为例子:
# urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^events/$', views.event_list),
(r'^blog/entries/$', views.entry_list),
)
# views.py
from django.shortcuts import render_to_response
from mysite.models import Event, BlogEntry
def event_list(request):
obj_list = Event.objects.all()
return render_to_response('mysite/event_list.html', {'event_list': obj_list})
def entry_list(request):
obj_list = BlogEntry.objects.all()
return render_to_response('mysite/blogentry_list.html', {'entry_list': obj_list})
这两个视图做的事情实质上是一样的: 显示一系列的对象。 让我们把它们显示的对象的类型抽象出来:
# urls.py
from django.conf.urls.defaults import *
from mysite import models, views
urlpatterns = patterns('',
(r'^events/$', views.object_list, {'model': models.Event}),
(r'^blog/entries/$', views.object_list, {'model': models.BlogEntry}),
)
# views.py
from django.shortcuts import render_to_response
def object_list(request, model):
obj_list = model.objects.all()
template_name = 'mysite/%s_list.html' % model.__name__.lower()
return render_to_response(template_name, {'object_list': obj_list})
urls: (r'test1/$','login.views.test',{'Class':models.English,'template':'t1.html'}), (r'test2/$','login.views.test',{'Class':models.New,'template':'t2.html'}), ---------------------------------------------- views: def test(request,Class,template): c = Class.objects.all() return render_to_response(template,{'c':c}) ------------------- 这样写或许更加简便
(r'^events/$', views.object_list, {'model': models.Event}), (r'^blog/entries/$', views.object_list, {'model': models.BlogEntry}), 大胆猜测: event对应models.Event; blog/entries对应 models.BlogEntry; (r'^{modelname}/{opername}_{methold}$', views.object_{opername}_{methold}, {'model': models.{modelname}}) user/list/ ; user/info/ ; user/add_edit/; user/add_save/; user/delete/; user/update_edit/; user/update_save/;
就这样小小的改动,我们突然发现我们有了一个可复用的,模型无关的视图! 从现在开始,当我们需要一个视图来显示一系列的对象时,我们可以简简单单的重用这一个 object_list 视图,而无须另外写视图代码了。 以下是我们做过的事情:
- 我们通过
model参数直接传递了模型类。 额外URLconf参数的字典是可以传递任何类型的对象,而不仅仅只是字符串。 - 这一行:
model.objects.all()是 鸭子界定 (原文: - 我们使用
model.__name__.lower()来决定模板的名字。 每个Python的类都有一个__name__属性返回类名。 这特性在当我们直到运行时刻才知道对象类型的这种情况下很有用。 比如,BlogEntry类的__name__就是字符串'BlogEntry'。 - 这个例子与前面的例子稍有不同,我们传递了一个通用的变量名给模板。 当然我们可以轻易的把这个变量名改成
blogentry_list或者event_list,不过我们打算把这当作练习留给读者。
“原文”里面的内容,我从英文网站看过后,内容翻译如下:如果它走路像一个鸭子,叫声像一个鸭子,我们就将它当做一个鸭子对待。所以我们不需要知道这里的model到底是什么类型,只要他有一个objects的属性和all()的方法,我们就能够使用他了。
字典中加一个关于模板的键/值 (r'^events/$','mydjango.views.object_list', {'model': models.Book,'template_name':'Book.html'}),
原文: The model.objects.all() line is an example of duck typing: “If it walks like a duck and talks like a duck, we can treat it like a duck.” Note the code doesn’t know what type of object model is; the only requirement is that model have an objects attribute, which in turn has an all() method.
return render_to_response(template_name, {'%s_list' % model.__name__.lower() : obj_list})
后面没翻译:“If it walks like a duck and talks like a duck, we can treat it like a duck.” Note the code doesn’t know what type of object model is; the only requirement is that model have an objects attribute, which in turn has an all() method.
obj_list = model_list.split("_",2)[0].objects.all() template_name = 'mysite/%s.html',%model_list
因为数据库驱动的网站都有一些通用的模式,Django提供了一个通用视图的集合,使用它可以节省你的时间。 我们将会在下一章讲讲Django的内置通用视图。
如果你发布一个Django的应用,你的用户可能会希望配置上能有些自由度。 这种情况下,为你认为用户可能希望改变的配置选项添加一些钩子到你的视图中会是一个很好的主意。 你可以用额外URLconf参数实现。
一个应用中比较常见的可供配置代码是模板名字:
def my_view(request, template_name):
var = do_something()
return render_to_response(template_name, {'var': var})
当冲突出现的时候,额外URLconf参数优先于捕捉值。 也就是说,如果URLconf捕捉到的一个命名组变量和一个额外URLconf参数包含的变量同名时,额外URLconf参数的值会被使用。
例如,下面这个URLconf:
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^mydata/(?P<id>\d+)/$', views.my_view, {'id': 3}),
)
这里,正则表达式和额外字典都包含了一个 id 。硬编码的(额外字典的) id 将优先使用。 就是说任何请求(比如, /mydata/2/ 或者 /mydata/432432/ )都会作 id 设置为 3 对待,不管URL里面能捕捉到什么样的值。
聪明的读者会发现在这种情况下,在正则表达式里面写上捕捉是浪费时间的,因为 id 的值总是会被字典中的值覆盖。 没错,我们说这个的目的只是为了让你不要犯这样的错误。
使用缺省视图参数
例子:
# urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^blog/$', views.page),
(r'^blog/page(?P<num>\d+)/$', views.page),
)
# views.py
def page(request, num='1'):
# Output the appropriate page of blog entries, according to num.
# ...
在这里,两个URL表达式都指向了同一个视图 views.page ,但是第一个表达式没有传递任何参数。 如果匹配到了第一个样式, page() 函数将会对参数 num 使用默认值 "1" ,如果第二个表达式匹配成功, page() 函数将使用正则表达式传递过来的num的值。
@Rex.Ye 我想 @生命至上 的意思是这样吧:urlpatterns=patterns('', (r'^blog/$', views.page, {'num': '1'}), (r'^blog/page(?P<num>\d+)/$', views.page), ) 如此,只有当第一个pattern匹配的时候,才会默认num=1,而第二个pattern匹配的时候,依然是通过正则表达式获得的num值,并不会受到优先级的影响
(注:我们已经注意到设置默认参数值是字符串 ‘1’ ,不是整数1 。为了保持一致,因为捕捉给num 的值总是字符串。
就像前面解释的一样,这种技术与配置选项的联用是很普遍的。 以下这个例子比提供视图配置选项一节中的例子有些许的改进。
def my_view(request, template_name='mysite/my_view.html'):
var = do_something()
return render_to_response(template_name, {'var': var})
特殊情况下的视图
有时你有一个模式来处理在你的URLconf中的一系列URL,但是有时候需要特别处理其中的某个URL。 在这种情况下,要使用将URLconf中把特殊情况放在首位的线性处理方式 。
比方说,你可以考虑通过下面这个URLpattern所描述的方式来向Django的管理站点添加一个目标页面
这句必须贴下原文, For example, you can think of the “add an object” pages in Django’s admin site as represented by a URLpattern like this: 你可以把admin管理页里的“增加一个object”页面,看作以下的URLpattern
urlpatterns = patterns('',
# ...
('^([^/]+)/([^/]+)/add/$', views.add_stage),
# ...
)
这将匹配像 /myblog/entries/add/ 和 /auth/groups/add/ 这样的URL 。然而,对于用户对象的添加页面( /auth/user/add/ )是个特殊情况,因为它不会显示所有的表单域,它显示两个密码域等等。 我们 可以 在视图中特别指出以解决这种情况:
def add_stage(request, app_label, model_name):
if app_label == 'auth' and model_name == 'user':
# do special-case code
else:
# do normal code
不过,就如我们多次在这章提到的,这样做并不优雅: 因为它把URL逻辑放在了视图中。 更优雅的解决方法是,我们要利用URLconf从顶向下的解析顺序这个特点:
urlpatterns = patterns('',
# ...
('^auth/user/add/$', views.user_add_stage),
('^([^/]+)/([^/]+)/add/$', views.add_stage),
# ...
)
在这种情况下,象 /auth/user/add/ 的请求将会被 user_add_stage 视图处理。 尽管URL也匹配第二种模式,它会先匹配上面的模式。 (这是短路逻辑。)
从URL中捕获文本
每个被捕获的参数将被作为纯Python字符串来发送,而不管正则表达式中的格式。 举个例子,在这行URLConf中:
(r'^articles/(?P<year>\d{4})/$', views.year_archive),
尽管 \d{4} 将只匹配整数的字符串,但是参数 year 是作为字符串传至 views.year_archive() 的,而不是整型。
当你在写视图代码时记住这点很重要,许多Python内建的方法对于接受的对象的类型很讲究。 许多内置Python函数是挑剔的(这是理所当然的)只接受特定类型的对象。 一个典型的的错误就是用字符串值而不是整数值来创建 datetime.date 对象:
"一个典型的的错误就是用字符串值而不是整数值来创建 datetime.date 对象"是不是讲反了呢?如果改成“ 一个典型的的错误就是用整数值而不是字符串值来创建 datetime.date 对象:”这样呢?看例子应该是这样。。。。
>>> import datetime
>>> datetime.date('1993', '7', '9')
Traceback (most recent call last):
...
TypeError: an integer is required
>>> datetime.date(1993, 7, 9)
datetime.date(1993, 7, 9)
回到URLconf和视图处,错误看起来很可能是这样:
# urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^articles/(\d{4})/(\d{2})/(\d{2})/$', views.day_archive),
)
# views.py
import datetime
def day_archive(request, year, month, day):
# The following statement raises a TypeError!
date = datetime.date(year, month, day)
因此, day_archive() 应该这样写才是正确的:
def day_archive(request, year, month, day):
date = datetime.date(int(year), int(month), int(day))
注意,当你传递了一个并不完全包含数字的字符串时, int() 会抛出 ValueError 的异常,不过我们已经避免了这个错误,因为在URLconf的正则表达式中已经确保只有包含数字的字符串才会传到这个视图函数中。
3、4楼,楼主不是那个意思,楼主的意思是说这个view可能被其他url调用,而其他url传递进来的参数就不一定经过正则过滤了。作者全面章节说到过类似问题,因此才说有点前后矛盾了。
前后不一,是因为前面那几章里将plus时间的例子,hours_ahead这个view的例子,尽管url正则做了过滤,不保证其他url也用这个view,而那个url可能没有过滤,所以最好还是做一个try比较妥当
决定URLconf搜索的东西
当一个请求进来时,Django试着将请求的URL作为一个普通Python字符串进行URLconf模式匹配(而不是作为一个Unicode字符串)。 这并不包括 GET 或 POST 参数或域名。 它也不包括第一个斜杠,因为每个URL必定有一个斜杠。
例如,在向 http://www.example.com/myapp/ 的请求中,Django将试着去匹配 myapp/ 。在向 http://www.example.com/myapp/?page=3 的请求中,Django同样会去匹配 myapp/ 。
在解析URLconf时,请求方法(例如, POST , GET , HEAD )并 不会 被考虑。 换而言之,对于相同的URL的所有请求方法将被导向到相同的函数中。 因此根据请求方法来处理分支是视图函数的责任。
视图函数的高级概念
说到关于请求方法的分支,让我们来看一下可以用什么好的方法来实现它。 考虑这个 URLconf/view 设计:
# urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
# ...
(r'^somepage/$', views.some_page),
# ...
)
# views.py
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import render_to_response
def some_page(request):
if request.method == 'POST':
do_something_for_post()
return HttpResponseRedirect('/someurl/')
elif request.method == 'GET':
do_something_for_get()
return render_to_response('page.html')
else:
raise Http404()
在这个示例中,some_page() 视图函数对POST 和GET 这两种请求方法的处理完全不同。 它们唯一的共同点是共享一个URL地址: /somepage/.正如大家所看到的,在同一个视图函数中对POST 和GET 进行处理是一种很初级也很粗糙的做法。 一个比较好的设计习惯应该是,用两个分开的视图函数——一个处理POST 请求,另一个处理GET 请求,然后在相应的地方分别进行调用。
我们可以像这样做:先写一个视图函数然后由它来具体分派其它的视图,在之前或之后可以执行一些我们自定的程序逻辑。 下边的示例展示了这个技术是如何帮我们改进前边那个简单的some_page() 视图的:
# views.py
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import render_to_response
def method_splitter(request, GET=None, POST=None):
if request.method == 'GET' and GET is not None:
return GET(request)
elif request.method == 'POST' and POST is not None:
return POST(request)
raise Http404
def some_page_get(request):
assert request.method == 'GET'
do_something_for_get()
return render_to_response('page.html')
def some_page_post(request):
assert request.method == 'POST'
do_something_for_post()
return HttpResponseRedirect('/someurl/')
# urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
# ...
(r'^somepage/$', views.method_splitter, {'GET': views.some_page_get, 'POST': views.some_page_post}),
# ...
)
当assert正确时,继续执行。否则抛出异常。我在一个View函数中这样声明:assert 1 > 2 执行后,显示 AssertionError at /search/ No exception supplied Request Method: GET Request URL: http://127.0.0.1:8000/search/ Django Version: 1.3.1 Exception Type: AssertionError
这个是1.4版本的,后面的更高级版本提供了decorators,感兴趣的童鞋请看 https://docs.djangoproject.com/en/1.5/topics/http/decorators/
刚开始我看的时候猛地也迷糊一个,GET=None这里的GET和method那里的GET不是一回事,GET=None这里传入的是函数,感觉改一下参数名字好一些,比如GetFunction = None,下边那里写成{'GetFunction':views.some_page_get,'PostFunction':views.some_page_post}这样子是不是清除一些~~~
建议吧method_splitter()中的参数GET,POST改成get_method和post_method 容易混淆 这只是为了在url字典中进行传参 参数为函数 名
>>> 最好一个路由也用来体现一个动作(就和一个函数对应一种功能一样),而不是既能GET又能POST,一个路由被设计成执行多个动作,逻辑是复杂的,工作量也大了。
让我们从头看一下代码是如何工作的:
这个。。urlpattersn 里面固定的传GET、POST对应函数名称也不会比在函数里面直接函数里面写好吧,其实都是写死了代码。 if request.method == 'GET' and GET is not None: return some_page_get(request)
设计模式告诉我们,要尽量少用if,但是这里和前面的if request.method == 'GET' and GET is not None: return some_page_get(request)都用了if,所以这里和前面应该在本质上都一样,都不优雅。
我觉得使用url传递参数,费脑子,找来找去的更麻烦,也没见怎么优雅。python格言不是明言胜于晦涩吗?最讨厌跳来跳去的程序,看起来好像牛逼,实际上为了牛逼而牛逼,哪天自己找都费劲。
我们写了一个新的视图,method_splitter() ,它根据request.method 返回的值来调用相应的视图。可以看到它带有两个关键参数,GET 和POST ,也许应该是 视图函数 。如果request.method 返回GET ,那它就会自动调用GET 视图。 如果request.method 返回的是POST ,那它调用的就是POST 视图。 如果request.method 返回的是其它值(如:HEAD ),或者是没有把GET 或POST 提交给此函数,那它就会抛出一个Http404 错误。
在URLconf中,我们把/somepage/ 指到method_splitter() 函数,并把视图函数额外需要用到的GET 和POST 参数传递给它。
最终,我们把some_page() 视图分解到两个视图函数中some_page_get() 和some_page_post() 。这比把所有逻辑都挤到一个单一视图的做法要优雅得多。
注意,在技术上这些视图函数就不用再去检查request.method 了,因为method_splitter() 已经替它们做了。 (比如,some_page_post() 被调用的时候,我们可以确信request.method 返回的值是post 。)当然,这样做不止更安全也能更好的将代码文档化,这里我们做了一个假定,就是request.method 能象我们所期望的那样工作。
这里有点不明白了,some_page_post()这个函数什么时候会被调用?request.method==‘POST’的时候? 为什么会在这个时候被调用?
在url.py中 (r'^somepage/$', views.method_splitter, {'GET': views.some_page_get, 'POST': views.some_page_post}), # ... 只是将POST或GET字典值作为参数传给视图views.method_splitter,然后views.method_splitter判断后。。。通过request(POST或GET字典值)去请求对应的视图模块??
在url.py中 (r'^somepage/$', views.method_splitter, {'GET': views.some_page_get, 'POST': views.some_page_post}), # ... 只是将POST或GET字典值作为参数传给视图views.method_splitter,然后views.method_splitter判断后。。。通过request(POST或GET字典值)去请求对应的视图模块??里边封装着由`` request.method`` 的返回值来分派不同的视图的程序。这个是重点
在method_spliter函数中第一个返回值 return GET(request) 对应的是 return views.some_page_get(request) 这里GET的值是由urls那边传过来的 二个返回的也是同理 这里的GET不同于 request.method== 'GET'里的GET
现在我们就拥有了一个不错的,可以通用的视图函数了,里边封装着由request.method 的返回值来分派不同的视图的程序。关于method_splitter() 就不说什么了,当然,我们可以把它们重用到其它项目中。
然而,当我们做到这一步时,我们仍然可以改进method_splitter 。从代码我们可以看到,它假设Get 和POST 视图除了request 之外不需要任何其他的参数。那么,假如我们想要使用method_splitter 与那种会从URL里捕捉字符,或者会接收一些可选参数的视图一起工作时该怎么办呢?
这里的GET 和POST 是参数,代表了两个函数,也就是some_page_get和some_page_post这两个函数。在上面的例子中,这两个函数的参数只有request。return GET(request)其实就是return some_page_get(request),调用了下面的函数。这时没有向some_page_get传递参数,如果需要传递参数的话,解决方法,敬请期待
>>> 这里对于初学者不是很友好,作者可能偏爱函数式编程吧,如果各位读过 SICP(初学者可以读一下第一章) ,对高阶函数式编程应该一点都不陌生,这里作者就是想体现高阶函数式编程的特点,抽象,抽象,再抽象,有时候过度抽象了也不好,反而变成了过度设计的现象,也就是为了抽象而抽象。
为了实现这个,我们可以使用Python中一个优雅的特性 带星号的可变参数 我们先展示这些例子,接着再进行解释
def method_splitter(request, *args, **kwargs):
get_view = kwargs.pop('GET', None)
post_view = kwargs.pop('POST', None)
if request.method == 'GET' and get_view is not None:
return get_view(request, *args, **kwargs)
elif request.method == 'POST' and post_view is not None:
return post_view(request, *args, **kwargs)
raise Http404
D.pop(k[,d]) -> v, remove specified key and return the corresponding value.If key is not found, d is returned if given, otherwise KeyError is raised. 与get类似,pop会删除指定键,并返回该键所对应的值
pop(key[, default]) If key is in the dictionary, remove it and return its value, else return default. If default is not given and key is not in the dictionary, a KeyError is raised.使用pop()方法确保下次调用时的正确性,使用完后就删除当时的key-value键值对
Python tips: 什么是*args和**kwargs?[ http://www.cnblogs.com/fengmk2/archive/2008/04/21/1163766.html ]
注意要结合上一段代码的url部分来看:urlpatterns = patterns('', # ... (r'^somepage/$', views.method_splitter, {'GET': views.some_page_get, 'POST': views.some_page_post}), # ... ),刚才想了好久才明白为什么带进来的kwargs会有以 GET 和 POST为key的字典。
确实要结合上面的例子一起看,下面说了**两个星号会转成字典,(r'^somepage/$', views.method_splitter, {'GET': views.some_page_get, 'POST': views.some_page_post}这个URL会把GET和POST封装在一个字典kwargs里,使用字典的pop来删除key会返回value给变量,那么变量get_view和post_view就会存GET和POST里的value(即views.some_page_get和views.some_page_post),在对request.method判断来返回对应的function(返回方法对象并传递参数)
>>> 这里不明白的可以先看看廖雪峰老师的python教程,函数的参数这一章,弄明白以后(当然也要理解函数式编程的特点,函数可以用做函数的参数传递和返回)理解起来会很简单。
**kwargs只是为了提取对应函数,而且通过pop的方式,已经将kwargs清空了,为啥后面return的函数里的参数还要带**kwargs?就算没清空,要**kwargs何用?
这里,我们重构method_splitter(),去掉了GET和POST两个关键字参数,改而支持使用args和和kwargs(注意号) 这是一个Python特性,允许函数接受动态的、可变数量的、参数名只在运行时可知的参数。 如果你在函数定义时,只在参数前面加一个号,所有传递给函数的参数将会保存为一个元组. 如果你在函数定义时,在参数前面加两个号,所有传递给函数的关键字参数,将会保存为一个字典
def foo(*args, **kwargs):
print "Positional arguments are:"
print args
print "Keyword arguments are:"
print kwargs
看一下它是怎么工作的
>>> foo(1, 2, 3)
Positional arguments are:
(1, 2, 3)
Keyword arguments are:
{}
>>> foo(1, 2, name='Adrian', framework='Django')
Positional arguments are:
(1, 2)
Keyword arguments are:
{'framework': 'Django', 'name': 'Adrian'}
回过头来看,你能发现我们用method_splitter()和*args接受**kwargs函数参数并把它们传递到正确的视图。any 但是在我们这样做之前,我们要调用两次获得参数kwargs.pop()``GET``POST,如果它们合法的话。 (我们通过指定pop的缺省值为None,来避免由于一个或者多个关键字缺失带来的KeyError)
之所以用pop不用get,我觉得是因为要把GET参数跟POP参数给用字典中移除,防止在 get_view(request, *args, **kwargs)时 它作为参数传入对应的调用函数中。
回头看methodsplitter(),我们用*args和*kwargs 接收 所有参数,再传递至合适的view(视图)。但,在做这件事之前,我们用kargs.pop()方法以获得GET 与POST 参数(如果有的话)。PS:默认值None是避免KeyError 错误
使用pop而不是get:是因为在 get_view = kwargs.pop('GET', None)后在调用return get_view(request, *args, **kwargs)时,**kwargs已经不需要传递视图函数是什么,因为已经找到了( get_view)
>>> 使用pop或者get方法都一样,http请求中的数据被抽象到了request对象中,视图函数在处理请求时只关心请求内容(request对象)是什么。
包装视图函数
我们最终的视图技巧利用了一个高级python技术。 假设你发现自己在各个不同视图里重复了大量代码,就像 这个例子:
def my_view1(request):
if not request.user.is_authenticated():
return HttpResponseRedirect('/accounts/login/')
# ...
return render_to_response('template1.html')
def my_view2(request):
if not request.user.is_authenticated():
return HttpResponseRedirect('/accounts/login/')
# ...
return render_to_response('template2.html')
def my_view3(request):
if not request.user.is_authenticated():
return HttpResponseRedirect('/accounts/login/')
# ...
return render_to_response('template3.html')
这里,每一个视图开始都检查request.user是否是已经认证的,是的话,当前用户已经成功登陆站点否则就重定向/accounts/login/ (注意,虽然我们还没有讲到request.user,但是14章将要讲到它.就如你所想像的,request.user描述当前用户是登陆的还是匿名)
如果我们能够丛每个视图里移除那些 重复代,并且只在需要认证的时候指明它们,那就完美了。 我们能够通过使用一个视图包装达到目的。 花点时间来看看这个:
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
这一段为什么要定义一个new_view?直接这样不行吗? 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
这一段为什么要定义一个new_view?直接这样不行吗? def requires_login(view): if not request.user.is_authenticated(): return HttpResponseRedirect('/accounts/login/') return view(request, *args, **kwargs)
逻辑太乱了,require_login(view1)->return new_view->执行new_view()函数-》判断结果;另外再问一下传递的参数view1视图,到底传递了什么,view1的视图名称,还是POST或GET内容?
之所以还有一层new_view是要返回一个 函数对象,就是func 而不是func(),好好回忆urlpattern是这样写的:url(r'^something/$', func,{...}), 这里就是要一个func。如果没有了new_view 按照楼上所说,那么返回的是 view(request, **args, **kwargs) 是func()了,不符合.... so, 要多一层,这是在下的拙见。。。。
这段建议看原版,更容易理解,reference: This function, requires_login, takes a view function (view) and returns a new view function (new_view). The new function, new_view is defined within requires_login and handles the logic of checking request.user.is_authenticated() and delegating to the original view (view).
>>> 其实很多初学者大可不必纠结这些,看不懂就是看不懂,暂时记住怎么用就好了,不必大费周折去弄懂过于难理解的东西,见识广了以后自然会明白很多以前弄不懂的东西。
函数requires_login,传入一个视图函数view,然后返回一个新的视图函数new_view.这个新的视图函数new_view在函数requires_login内定义 处理request.user.is_authenticated()这个验证,从而决定是否执行原来的view函数
在requires_login的函数中,定义了一个新的视图,这个视图对是否是用户登录进行判断,如果登录失败,就重新定向到'/accounts/login/',否则就进入视图view这个视图的界面。new_view相当于一个变化的视图,由requires_login函数传入的参数view(这是一个视图)的属性决定的。
require_login 中参数为view函数名, 进入函数后进行选择判断。view(*args, **kwargs)这样是因为传入的view函数时名不知道里面的参数 精妙
现在,我们可以从views中去掉if not request.user.is_authenticated()验证.我们可以在URLconf中很容易的用requires_login来包装实现.
from django.conf.urls.defaults import *
from mysite.views import requires_login, my_view1, my_view2, my_view3
urlpatterns = patterns('',
(r'^view1/$', requires_login(my_view1)),
(r'^view2/$', requires_login(my_view2)),
(r'^view3/$', requires_login(my_view3)),
)
有这样一个问题,如果某个试图将来不需要处理登录了,我是在URLConf中把requireLogin的装饰函数去掉,这个行为的变更应该是View去处理呢,还是Controller去处理呢?层次有没有混乱?
逻辑太乱了,require_login(view1)->return new_view->执行new_view()函数-》判断结果;另外再问一下传递的参数view1视图,到底传递了什么,view1的视图名称,还是POST或GET内容?
这里其实是这样的: 例如第一个url 把my_view1这个视图函数作为参数传递到requires_login里面。在requires_login中 如果该用户已经验证->调用view函数[也就是传递进来的my_view1(该函数会返回一个httpresponse)]->作为答复返回给new_view->再返回给requires_login->作为视图函数
我的理解是,例如第一个url,require_login(myview_1)其实就是一个命名空间内有myview_1的new_view函数,传给他的参数就是request,同时处理后可以返回myview_1函数
以my_view1为例,个人理解如下:当访问http://ip/view1时, requires_login(my_view1)将试图函数 my_view1 作为参数传递,然后返回函数 new_view ,接着执行 new_view 中的认证过程,如果认证通过,就返回 view 执行后的结果(这里传递的是 my_view1),最后将结果展现给用户。 大家有疑惑,还是亲自写试试吧,方便加深。
也可以写成这样 def require_login(request, view): if not request.user.is_authenticated(): return HttpResponseRedirect('/account/login') else: return view(request) 然后url字典配置里面 url(r'^view1/$',require_login, {'view':my_view1}) 这样功能是不是差不多呢?
>>> 看来楼上很多还是在纠结,非常推荐初学者去读读sicp,读完第一章就好了,顺便每节习题尽量做一下,不会做网上有答案,读完第一章就能理解抽象的奥秘,python的装饰器本质就是一个高阶函数,很多人只知道装饰器怎么用,但不了解装饰器是怎么实现的,本质还是对高阶函数编程的特性不理解。 >>> 文中的例子就是在告诉你怎么造一个装饰器的轮子。
优化后的代码和前面的功能一样,但是减少了代码冗余 现在我们建立了一个漂亮,通用的函数requires_login()来帮助我们修饰所有需要它来验证的视图
包含其他URLconf
在工程文件夹中urls.py中: (r'^',include('app1.urls')), 在应用文件夹中urls.py中:(r'^aaa/$', view1), 在浏览器URL栏中输入http://127.0.0.1:8000/aaa/,即可访问view1视图
或者:在工程文件夹中urls.py中: (r'^app1/',include('app1.urls')), 在应用文件夹中urls.py中:(r'^aaa/$', view1), 在浏览器URL栏中输入http://127.0.0.1:8000/app1/aaa/,即可访问view1视图。app1是应用名。此种方式合理一点
@楼上,这里用在多个app上比较合适些,一般一个project会有多个app,每个app下面又有自身的url.py, 那么project里的url.py可以包本工程下所有的app里的url.py。
如果你试图让你的代码用在多个基于Django的站点上,你应该考虑将你的URLconf以包含的方式来处理。
在任何时候,你的URLconf都可以包含其他URLconf模块。 对于根目录是基于一系列URL的站点来说,这是必要的。 例如下面的,URLconf包含了其他URLConf:
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^weblog/', include('mysite.blog.urls')),
(r'^photos/', include('mysite.photos.urls')),
(r'^about/$', 'mysite.views.about'),
)
在前面第6章介绍Django的admin模块时我们曾经见过include. admin模块有他自己的URLconf,你仅仅只需要在你自己的代码中加入include就可以了.
这里有个很重要的地方: 例子中的指向 include() 的正则表达式并 不 包含一个 $ (字符串结尾匹配符),但是包含了一个斜杆。 每当Django遇到 include() 时,它将截断匹配的URL,并把剩余的字符串发往包含的URLconf作进一步处理。
比如admin中有这样的URL路由。 `url(r'^login/$', self.login, name='login')` 而mysite中的的URL有这样的路由: `url(r'^admin/', include(admin.site.urls))` 存在这样一个地址 admin/login/ mysite获取的地址是`admin/login/`,它匹配了`admin/`,然后将`admin/`部分截断,传递`login/`给admin模块,admin模块获取到的URL是`login/`,它就直接匹配上self.login函数了。
>>> 个人理解include函数相当于Flask中的蓝图,在app内注册蓝图,添加前缀。这个前缀的功能和django中include函数之前用来匹配的字符串。 >>> 另外说一句,web编程最好先学习下网络编程基础,Flask相对django更简单上手,有一定的flask编程经验再学习django会容易很多
继续看这个例子,这里就是被包含的URLconf mysite.blog.urls :
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^(\d\d\d\d)/$', 'mysite.blog.views.year_detail'),
(r'^(\d\d\d\d)/(\d\d)/$', 'mysite.blog.views.month_detail'),
)
通过这两个URLconf,下面是一些处理请求的例子:
/weblog/2007/:在第一个URLconf中,模式r'^weblog/'被匹配。 因为它是一个include(),Django将截掉所有匹配的文本,在这里是'weblog/'。URL剩余的部分是2007/, 将在mysite.blog.urls这个URLconf的第一行中被匹配到。 URL仍存在的部分为2007/,与第一行的mysite.blog.urlsURL设置相匹配。- /weblog//2007/(包含两个斜杠) 在第一个URLconf中,r’^weblog/’匹配 因为它有一个include(),django去掉了匹配的部,在这个例子中匹配的部分是’weblog/’ 剩下的部分是/2007/ (最前面有一个斜杠),不匹配mysite.blog.urls中的任何一行.
/about/: 这个匹配第一个URLconf中的mysite.views.about视图。
捕获的参数如何和include()协同工作
一个被包含的URLconf接收任何来自parent URLconfs的被捕获的参数,比如:
# root urls.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
)
# foo/urls/blog.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^$', 'foo.views.blog_index'),
(r'^archive/$', 'foo.views.blog_archive'),
)
在这个例子中,被捕获的 username 变量将传递给被包含的 URLconf,进而传递给那个URLconf中的 每一个 视图函数。
注意,这个被捕获的参数 总是 传递到被包含的URLconf中的 每一 行,不管那些行对应的视图是否需要这些参数。 因此,这个技术只有在你确实需要那个被传递的参数的时候才显得有用。
相似的,你可以传递额外的URLconf选项到 include() , 就像你可以通过字典传递额外的URLconf选项到普通的视图。 当你这样做的时候,被包含URLconf的 每一 行都会收到那些额外的参数。
第一个:
# urls.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^blog/', include('inner'), {'blogid': 3}),
)
# inner.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^archive/$', 'mysite.views.archive'),
(r'^about/$', 'mysite.views.about'),
(r'^rss/$', 'mysite.views.rss'),
)
第二个
# urls.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^blog/', include('inner')),
)
# inner.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^archive/$', 'mysite.views.archive', {'blogid': 3}),
(r'^about/$', 'mysite.views.about', {'blogid': 3}),
(r'^rss/$', 'mysite.views.rss', {'blogid': 3}),
)
这个例子和前面关于被捕获的参数一样(在上一节就解释过这一点),额外的选项将 总是 被传递到被包含的URLconf中的 每一 行,不管那一行对应的视图是否确实作为有效参数接收这些选项,因此,这个技术只有在你确实需要那个被传递的额外参数的时候才显得有用。 因为这个原因,这种技术仅当你确信在涉及到的接受到额外你给出的选项的每个URLconf时有用的才奏效。
>>> 我转意一下,如果你准备在一个url字符串匹配中同时使用命名组参数和include函数,那么所有能匹配到该字符串相关的视图函数都被动的接收了命名组定义的参数,所以你需要在所有的视图函数中额外处理这些参数,比如用 def func(request, **kwargs): ... 的形式处理它
下一章
这一章提供了很多高级视图和URLconfs的小提示和技巧。 接下来,在Chapter 9,我们将会将这个先进的处理方案带给djangos模板系统。
GNU Free Document License. Hosting公司殷勤提供