python(十四):Django之url和views
网络通讯的本质是socket,从socket封装到mvc模式,参见另外几篇博客。本节笔记整理自django2.0官方文档。
一、url调度器 - django.urls.path
django2.0中使用path函数替代url函数。path函数源码如下:
def _path(route, view, kwargs=none, name=none, pattern=none): if isinstance(view, (list, tuple)): # for include(...) processing. pattern = pattern(route, is_endpoint=false) urlconf_module, app_name, namespace = view return urlresolver( pattern, urlconf_module, kwargs, app_name=app_name, namespace=namespace, ) elif callable(view): pattern = pattern(route, name=name, is_endpoint=true) return urlpattern(pattern, view, kwargs, name) else: raise typeerror('view must be a callable or a list/tuple in the case of include().') path = partial(_path, pattern=routepattern) # functools.partial re_path = partial(_path, pattern=regexpattern)
path函数接收四个参数:route,view,kwargs和name。它用functools.partial装饰了一下,将路由处理类routepattern作为参数传递给了pattern。
1、path函数的参数[route,view,kwargs,name]
urlpatterns = [ path('homepage', views.homepage), path('userinfo', views.userinfo, name='userinfo'), path('blog', views.blog, name='logout', kwargs={'id':10}) ]
route指定url匹配规则并可以从url中获取参数,view返回一个视图函数或者一个url列表(元组),name主要使模板和url解耦,kwargs为视图函数设置参数。
2、route匹配和获取url参数
path函数默认使用routepattern来匹配url,并从中获取相应参数,该参数需要在视图函数中设置同名形参来接收。
# app01/urls.py from django.urls import path from app01 import views urlpatterns = [ path('items/<name>/<int:id>', views.items_handler), ] # app01/views.py from django.shortcuts import httpresponse def items_handler(request, name, id): return httpresponse("{}, {}".format(name, id))
route可以使用"<val>"获取指定的字符串,甚至可以使用"<type: val>"的方式指定获取的数据类型,参数val需要被接收。
path函数支持str、int、path、slug、uuid等数据类型。str匹配不包含路径分隔符"/"的非空字符串,path匹配包含路径分隔符"/"的非空字符串,int包含有效的整数。
也可以自定义数据类型:
from django.urls import path, register_converter from . import converters, views class fourdigityearconverter: regex = '[0-9]{4}' def to_python(self, value): return int(value) def to_url(self, value): return '%04d' % value register_converter(converters.fourdigityearconverter, 'yyyy') urlpatterns = [ path('articles/2003/', views.special_case_2003), path('articles/<yyyy:year>/', views.year_archive), ... ]
re_path则用正则表达式来匹配url和截取参数。例如:
# urls.py from django.urls import path, re_path from . import views urlpatterns = [ re_path(r'^articles/(?p<year>[0-9]{4})/$', views.year_archive), re_path(r'^articles/(?p<year>[0-9]{4})/(?p<month>[0-9]{2})/$', views.month_archive), re_path(r'^articles/(?p<year>[0-9]{4})/(?p<month>[0-9]{2})/(?p<slug>[\w-]+)/$', views.article_detail), ] # views.py from django.shortcuts import httpresponse def year_archive(request, year): return httpresponse(year) def month_archive(request, year, month, name):return httpresponse("%r, %r" % (year, month)) def article_detail(request, year, month, slug, name):return httpresponse("%r, %r, %r" % (year, month, slug))
3、view参数
path源码可以接收的view参数包括: 函数,被urlpattern处理;列表或元组,被urlresolver。view参数也有两个功能,调用视图函数并传递给其参数,以及拆包。
from django.urls import include, path # 方法一:分别导入属视图函数和urlpatterns(extra_patterns),在urls.py中使用include()函数组合起来from credit import views as credit_views extra_patterns = [ path('reports/', credit_views.report), path('reports/<int:id>/', credit_views.report), path('charge/', credit_views.charge), ] urlpatterns = [ path('help/', include('apps.urls')), # 方法二:直接将urlpatterns写在应用下(apps/urls.py),urls.py中用include导入apps/urls.py即可 path('credit/', include(extra_patterns)), ]
来看一下include源码:
def include(arg, namespace=none): app_name = none if isinstance(arg, tuple): # callable returning a namespace hint. try: urlconf_module, app_name = arg except valueerror: if namespace: raise improperlyconfigured( 'cannot override the namespace for a dynamic module that ' 'provides a namespace.' ) raise improperlyconfigured( 'passing a %d-tuple to include() is not supported. pass a ' '2-tuple containing the list of patterns and app_name, and ' 'provide the namespace argument to include() instead.' % len(arg) ) else: # no namespace hint - use manually provided namespace. urlconf_module = arg if isinstance(urlconf_module, str): urlconf_module = import_module(urlconf_module) patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module) app_name = getattr(urlconf_module, 'app_name', app_name) if namespace and not app_name: raise improperlyconfigured( 'specifying a namespace in include() without providing an app_name ' 'is not supported. set the app_name attribute in the included ' 'module, or pass a 2-tuple containing the list of patterns and ' 'app_name instead.', ) namespace = namespace or app_name # make sure the patterns can be iterated through (without this, some # testcases will break). if isinstance(patterns, (list, tuple)): for url_pattern in patterns: pattern = getattr(url_pattern, 'pattern', none) if isinstance(pattern, localeprefixpattern): raise improperlyconfigured( 'using i18n_patterns in an included urlconf is not allowed.' ) return (urlconf_module, app_name, namespace)
它可以传入文件路径字符串或者一个包含多个元组的列表(触发else:urlconf_module = arg),并使用importlib.import_module导入文件,也可以传递一个当一个请求进来时,通过反射调用相应的视图函数(pattern = getattr(url_pattern, 'pattern', none))。
4、path参数类型和作用域
path函数的参数分为三种:kwargs、route和request。尽管request不属于path,这里为了比较姑且这样写。
kwargs参数作用域最大,不仅涉及include的所有子路由,而且涉及所有能被route捕捉和匹配的当前路由。kwargs设定的参数需要属兔函数设置同名形参来接收。一般用于后台设置。
# urls.py from django.contrib import admin from django.urls import re_path, path, include from app01 import views extra_pattern = [ re_path(r'^articles/(?p<year>[0-9]{4})/$', views.year_archive), re_path(r'^articles/(?p<year>[0-9]{4})/(?p<month>[0-9]{2})/$', views.month_archive), re_path(r'^articles/(?p<year>[0-9]{4})/(?p<month>[0-9]{2})/(?p<slug>[\w-]+)/$', views.article_detail), ] urlpatterns = [ path('admin/', admin.site.urls), path('app01/', include(extra_pattern), {"name": "jan"}), # path('app01/', include('app01.urls')) ] # app01/views.py from django.shortcuts import httpresponse # 每一个子路由对应的视图函数都要声明name参数 def year_archive(request, year, name): return httpresponse("{}, {}".format(year, name)) def month_archive(request, year, month, name): print(name) return httpresponse("%r, %r" % (year, month)) def article_detail(request, year, month, slug, name): print(name) return httpresponse("%r, %r, %r" % (year, month, slug))
route参数是匹配符合规则的url,并从url中获取参数。它的作用域为这些符合规则的url,并且只影响一个视图函数。
kwargs和route所设置的参数,都是需要视图函数声明。request参数可以接收get和post请求,它需要在视图函数中作为第一个参数声明。request在url之前已经封装好了。
二、视图函数
1、django.shortcuts
该模块收集了常见的response工具函数,用于快速的完成视图函数。
1.render函数
def render(request, template_name, context=none, content_type=none, status=none, using=none): """ return a httpresponse whose content is filled with the result of calling django.template.loader.render_to_string() with the passed arguments. """ content = loader.render_to_string(template_name, context, request, using=using) return httpresponse(content, content_type, status)
content_type指定文档的mime类型,status指定状态码,using参数用于指定加载模板的模板引擎。
from django.shortcuts import render
def my_view(request): return render(request, 'myapp/index.html', {'foo': 'bar'}, content_type='application/xhtml+xml')
它相当于:
from django.http import httpresponse from django.template import loader def my_view(request): t = loader.get_template('myapp/index.html') c = {'foo': 'bar'} return httpresponse(t.render(c, request), content_type='application/xhtml+xml')
2、redirect函数
def redirect(to, *args, permanent=false, **kwargs): """ return an httpresponseredirect to the appropriate url for the arguments passed. the arguments could be: * a model: the model's `get_absolute_url()` function will be called. * a view name, possibly with arguments: `urls.reverse()` will be used to reverse-resolve the name. * a url, which will be used as-is for the redirect location. issues a temporary redirect by default; pass permanent=true to issue a permanent redirect. """
# httpresponsepermanentredirect和httpresponseredirect在django.http模块中 redirect_class = httpresponsepermanentredirect if permanent else httpresponseredirect return redirect_class(resolve_url(to, *args, **kwargs))
redirect的三种重定向方式:接收参数为一个model并且它实现了get_absolute_url方法;接收一个django.urls.reverse通过视图函数反向生成的url;直接接收重定向的url路径。
# views.py from django.shortcuts import redirect, httpresponse, httpresponseredirect from django.urls import reverse class person: @staticmethod def get_absolute_url(): return reverse('icon_handler', kwargs={"name": "jan"}) def items_handler(request): # return redirect("/app01/icon") # 返回一个url # return httpresponseredirect(reverse('icon_handler', kwargs={"name": "jan"})) # 返回一个reverse return redirect(person) # 返回一个model def icon_handler(request, name): return httpresponse(name) # urls.py from django.urls import path from app01 import views urlpatterns = [ path('items', views.items_handler), path('icon/<name>', views.icon_handler, name='icon_handler'), ]
3、get_object_or_404和get_list_or_404
get_list_or_404
from django.shortcuts import get_object_or_404 def my_view(request): my_object = get_object_or_404(mymodel, pk=1)
from django.shortcuts import get_list_or_404
def my_view(request): my_objects = get_list_or_404(mymodel, published=true)