Django(二):url和views

发布时间:2019-03-01 10:36:10编辑:auto阅读(2340)

      网络通讯的本质是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)

     

     

     

关键字