趁老板不在,上班摸会鱼!!!
主子路由部分对应的码云地址:
主子路由
接着上篇继续写,这篇主要写两部分:视图(views)和路由(urls)
路由走向
首先看下url路由查找,详细了解些路由的工作原理。
借助上篇文章的例子解释一下
先看代码块:
views.py
from django.http import HttpResponse
from django.shortcuts import render
# Create your views here.
def test(request):
return HttpResponse('this is a test demo!')
urls.py
from django.contrib import admin
from django.urls import path
from users import views
urlpatterns = [
path('admin/', admin.site.urls),
path('test', views.test),
]
启动服务后,在输入网址:127.0.0.1:8000/test可访问以下页面
继续看http://127.0.0.1:8000/test
解析它的走向:
只要有请求就会有一个请求对象,它也不例外,它的请求对象是request
有请求对象接下来就会进到urls.py(路由配置表)文件中,路由配置表urls.py中有个urlpatterns,它是一个列表。
只要是发过来请求,我知道请求的路径后,就会去urls.py的urlpatterns中去找。这里我请求的是“test”,那就会去urlpatterns中去找我的这个test有没有定义,如果有定义接下来就会去找它所匹配的视图函数。
也就是说127.0.0.1:8000/test中的test会去urlpatterns中查找test,而我正好也定义了path(‘test’, views.test),,所以他能找到,而path(‘test’, views.test),中的test指向了后面views.test中的test函数,views是我提前就导入好的
from users import views
所以接下的动作就是去users里面的views.py里去找test函数
也就是刚才定义的视图函数
def test(request):
return HttpResponse('this is a test demo!')
这个时候其实我的request对象已经产生了,就是127.0.0.1:8000
它会把这个作为参数传给def test(request)
然后会响应this is a test demo!给浏览器展示给用户
简略的画个图来理解一下:
主子路由
在哪里定义路由?
在urls.py中定义路由
路由是一个列表,格式如下:
urlpatterns = [
path('admin/', admin.site.urls),
path('', views.index),
path('test', views.test),
]
urlpatterns = [
path('', views.index, name='index'),
path('<int:question_id>/', views.detail, name='detail'),
path('<int:question_id>/results/', views.results, name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
例:
展示不同的文章
views.py
# -*- coding:utf-8 -*-
articale = ['文章1', '文章2', '文章3', '文章4', '文章5', '文章6', '文章7']
def show_article(request, num):
# 因为索引都是从0开始的,所以我用了num-1,也可以直接num
return HttpResponse(articale[num - 1])
urls.py
from django.contrib import admin
from django.urls import path
from users import views
urlpatterns = [
path('admin/', admin.site.urls),
path('article/<int:num>', views.show_article)
]
用名字展示:
views.py
# -*- coding:utf-8 -*-
from django.http import HttpResponse
from django.shortcuts import render
# Create your views here.
def show_article_byname(request, name):
return HttpResponse('文章:' + name)
urls.py
from django.contrib import admin
from django.urls import path
from users import views
urlpatterns = [
path('admin/', admin.site.urls),
path('article/<str:name>',views.show_article_byname),
]
path函数:
首先看下path函数的底层
"""Functions for use in URLsconfs."""
from functools import partial
from importlib import import_module
from django.core.exceptions import ImproperlyConfigured
from .resolvers import (
LocalePrefixPattern, RegexPattern, RoutePattern, URLPattern, URLResolver,
)
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)
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)
re_path = partial(_path, Pattern=RegexPattern)
看最后两段代码:
path = partial(_path, Pattern=RoutePattern)
re_path = partial(_path, Pattern=RegexPattern)
这里path函数其实用的是偏函数(partial)
真正是其实是由_path这个函数来完成的
接着看这个函数:
def _path(route, view, kwargs=None, name=None, Pattern=None):
这里有两项是必填的,即route和view,剩下的三项选填
所以path函数有两项是必填的,也就是route(路由)和view(视图函数)
只要用到path,第一个就要定义路由名,然后就是这个路由绑定的函数
那么接下来第二个问题来了,视图函数从哪里找呢?
1、新建APP
2、新建的APP里面包含了一个叫做views.py的文件
3、在这个view.py文件里去定义视图函数
re_path函数
re_path函数是一个支持正则的函数
其实re_path和path用的都是_path这个函数,只不过re_path支持正则,它的Pattern变了
参数:
route:路由(支持正则表达式)
view:视图函数
re_path = partial(_path, Pattern=RegexPattern)
语法:
re_path(r'路由名称/正则表达式',绑定视图路径)
一般使用path的比较多,除非path不能支持了,需要正则支持,会用到re_path
路由分发
建APP时,除了users之后还有一个goods,如果想展示商品(goods)该怎么处理呢?
按常规思想应该是:首先需要在goods里面创建视图(views.py),然后再在urls.py里定义商品路由
这样是可行的,但是如果后面有很多app,每个APP又有N多个视图函数,也就意味着我的路由文件里(urls.py)要写一大坨的路由,其中会包含用户信息类的,商品类的,以及后面各种其他类的,这样找起来就显得异常的杂乱。
为了应对这个问题就需要用到主子路由的分发了
也就是主路由定义主干线
用子路由定义我自己的路由部分
接下来就看下如何走主子路由
首先看下主路由是谁,主路由就是urls.py文件,在主路由里面定义主干道
子路由需要自己去做,在每个新建的app里面新建一个urls.py文件
把原始的urls.py文件里的内容复制到这新建的urls.py文件中
也就是下面这堆代码:
urlpatterns = [
]
接下来来如何与主路由建立联系
这个时候需要在主路由里创建与子路由的关联
主路由urls.py
# 1. Import the include() function: from django.urls import include, path
# 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
from django.contrib import admin
from django.urls import path, include
from users import views
urlpatterns = [
path('admin/', admin.site.urls),
#其中“/”表示它还有下一层级
#include代表引入对应路径
path('users/',include('users.urls')),
path('goods/',include('goods.urls')),
]
这个时候主路由与子路由的关系设置好了,接下来就是如何去展示我的APP视图内容,这时候就需要对子路由(APP内的urls.py做设置了)
这里先以users的首页函数(index)为例
代码如下
from django.urls import path
from users.views import index
urlpatterns = [
path('', index),
]
这里先实验一波,启动项目
因为是在users目录下,所以输入/users,在后面什么都不跟的情况下会默认找到我的hello world!
下面继续(其实和之前的路由写法一样!)
直接上代码
from django.urls import path
from users.views import index, test
urlpatterns = [
path('', index),
path('test', test)
]
这里我访问的是http://127.0.0.1:8000/users/test
也就代表是users相关的东西
goods和其他的APP也同理
接下来再写一个例子
goods的商品列表展示:
views.py视图部分:
# -*- coding:utf-8 -*-
from django.http import HttpResponse
from django.shortcuts import render
# Create your views here.
goods = ['苹果', '香蕉', '西瓜', '芒果']
def show_goods(request):
s = "便利蜂商品有:<br>"
for g in goods:
s = s + g + '<br>'
return HttpResponse(s)
子路由urls.py部分:
from django.urls import path
from goods.views import index, show_goods
urlpatterns = [
path('', index),
path('all',show_goods)
]
这时候看一下执行结果:
这里主要看下路由部分
path('all',show_goods)
这里路由会去根据all寻找他的路径也就是show_goods函数,说明http://127.0.0.1:8000/goods/all里的这个goods只是一个主子路由的分发依据
子路由的创建方法:
1、新建APP
2、进入APP中,新建urls.py文件
3、在新建的urls.py文件中添加路由