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

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)

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM