網絡通訊的本質是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)