首页 > 技术文章 > django--第二章 路由和视图函数

hoyun 2021-09-24 18:21 原文



前言

Django 中的视图的概念是「一类具有相同功能和模板的网页的集合」。比如,在一个博客应用中,你可能会创建如下几个视图:

  • 博客首页——展示最近的几项内容。

  • 内容“详情”页——详细展示某项内容。

  • 评论处理器——用于响应为一项内容添加评论的操作。

在 Django 中,网页和其他内容都是从视图派生而来。每一个视图表现为一个简单的 Python 函数(或者说方法,如果是在基于类的视图里的话)。Django 将会根据用户请求的 URL 来选择使用哪个视图(更准确的说,是根据 URL 中域名之后的部分)。

在你上网的过程中,很可能看见过像这样美丽的 URL: "ME2/Sites/dirmod.asp?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B" 。别担心,Django 里的 URL 规则 要比这优雅的多!

一个 URL 模式定义了某种 URL 的基本格式——举个例子:/newsarchive/<year>/<month>/。为了将 URL 和视图关联起来,Django 使用了 'URLconfs' 来配置。URLconf 将 URL 模式映射到视图。

每个视图必须要做的只有两件事:返回一个包含被请求页面内容的 HttpResponse 对象,或者抛出一个异常,比如 Http404


简单的视图案例:

向页面展示一句:Hello, world. The first Django.

  1. 打开 app01\views.py文件,输入以下 Python 代码:
# app01\views.py
from django.shortcuts import HttpResponse
def index(request):
    return HttpResponse("Hello, world. The first Django.")
  1. 如果想看见效果,我们需要视图函数与URL进行绑定。在 app01目录中创建urls.py文件,文件中输入以下代码:
# app01\urls.py
from django.urls import path
from app01 import views
urlpatterns = [	path('index/', views.index),  ]
  1. 下一步是要在根 URLconf 文件中指定我们创建的 app01.urls 模块。在 mysite/urls.py 文件的 urlpatterns 列表里插入一个 include(), 如下:
# mysite\urls.py
from django.urls import include, path
urlpatterns = [   path('app01/', include('app01.urls')),   ] # 指定某应用下的URLconf
  1. 终端中运行Django服务器:
$ python manage.py runserver

运行后用你的浏览器访问 http://localhost:8000/app01/index,你应该能够看见 "Hello, world. The first Django." ,这是你在 index 视图中定义的。




FBV(函数)

FBV(function base views) 基于函数的视图,就是在视图里使用函数处理请求。

# app01\views.py
from django.shortcuts import HttpResponse
def index(self, request): 
    if request.method == "GET":
        return HttpResponse("GET 方法")
    if request.method == "POST":
        return HttpResponse("POST 方法")
# mysite\urls.py
from app01 import views
urlpatterns = [re_path('index/', views.index), ]



CBV(类)

CBV(class base views) 基于类的视图,就是在视图里使用类处理请求。不同的请求我们可以在类中使用不同方法来处理,这样大大的提高了代码的可读性。

定义的类要继承父类View,导入方式:from django.views import View

执行对应请求的方法前会优先执行 dispatch 方法(在get/post/put...方法前执行),dispatch() 方法会根据请求的不同调用相应的方法来处理。一般常用的请求方式: get(查)、post(创建)、put(更新)、delete(删除)。

页面中的 From 表单只支持:get、post

页面中的 ajax 支持:get、post、put、delete

其实,在我们前面学到的知识都知道 Django 的 url 是将一个请求分配给可调用的函数的,而不是一个类,那是如何实现基于类的视图的呢?

主要还是通过父类 View 提供的一个静态方法 as_view() ,as_view 方法是基于类的外部接口, 他返回一个视图函数,调用后请求会传递给 dispatch 方法,dispatch 方法再根据不同请求来处理不同的方法。

# app01\views.py
from django.views import View
from django.shortcuts import HttpResponse
class Index(View):
    def get(self, request): return HttpResponse("GET 方法")
    def post(self, request): return HttpResponse("POST 方法")

URL中的类views.Index必须调用as_view()方法,否则视图类不会生效。

# mysite\urls.py
from django.urls import re_path
from app01 import views
urlpatterns = [re_path('index.html$', views.Index.as_view()), ]
  • 改写dispatch方法,在请求前后加其他操作:
# app01\views.py
from django.views import View
from django.shortcuts import HttpResponse
class Test(View):
    def dispatch(self, request, *args, **kwargs):
        print('请求前操作...')
        obj = super(Test,self).dispatch(request, *args, **kwargs)
        print('请求后操作...')
        return obj

    def get(self, request): return HttpResponse('get方法')
    def post(self, request): return HttpResponse('post方法')
# mysite\urls.py
urlpatterns = [  re_path('test/', views.Test.as_view()),  ]




视图函数与路由(URL):

路由(URL)

  • path

path( route , view , kwargs=None , name=None ) ,返回一个包含在 中的元素urlpatterns

导入方式: from django.urls import path

route — 是一个字符串或 gettext_lazy(),是一个匹配 URL 的准则。这些准则不会匹配 GET 和 POST 参数或域名。

view — 是一个视图函数或结果 as_view() 为基于类的视图。当 Django 找到了一个匹配的准则,就会调用这个特定的视图函数,并传入一个 HttpRequest 对象作为第一个参数,被“捕获”的参数以关键字参数的形式传入。

kwargs — 允许传递额外的参数给视图函数或方法。

name — 为你的 URL 取名能使你在 Django 的任意地方唯一地引用它,尤其是在模板中。这个有用的特性允许你只改一个文件就能全局地修改某个 URL 模式。

示例:

from django.urls import include, path
urlpatterns = [
    path('index/', views.index, name='main-view'),
    path('bio/<username>/', views.bio, name='bio'),
    path('articles/<slug:title>/', views.article, name='article-detail'),
    path('articles/<slug:title>/<int:section>/', views.section, name='article-section'),
    path('weblog/', include('blog.urls')),
    ...
]

  • re_path

re_path( route , view , kwargs=None , name=None ) ,返回一个包含在 中的元素urlpatterns

导入方式: from django.urls import re_path

route — 是一个字符串或 gettext_lazy(),其中包含与Python的兼容的正则表达式re模块。

view 、kwargs — 同path的参数。

示例:

from django.urls import include, re_path
urlpatterns = [
    re_path(r'^index/$', views.index, name='index'),
    re_path(r'^bio/(?P<username>\w+)/$', views.bio, name='bio'),
    re_path(r'^weblog/', include('blog.urls')),
    ...
]

  • include

一个函数,它将完整的 Python 导入路径带到另一个应该“包含”在这个地方的 URLconf 模块。或者,也可以指定条目将包含在其中的应用程序命名空间实例命名空间
通常,应用程序命名空间应由包含的模块指定。如果设置了应用程序命名空间,则该namespace参数可用于设置不同的实例命名空间。
include() 还接受返回 URL 模式的可迭代对象或包含此类可迭代对象和应用程序命名空间名称的 2 元组作为参数。

不同的使用方式:

​ 1、include(pattern_list) # 最常用

​ 2、include(module, namespace=None)

​ 3、include( (pattern_list, app_namespace), namesapce=None)

module — 表示一种模型文件

namespace — 表示实例命名空间

pattern_list — 必须是一个可迭代的path() 或者 re_path() 清单

app_namesapce — app命名空间

导入方式: from django.urls import include

例如创建两个app:cmdbopenstack,并分别在这两个app目录下创建urls.py文件:

from django.urls import re_path,include     # 需要先导入include函数

urlpatterns = [
    # re_path(r'^admin/', admin.site.urls),
    re_path(r'^cmdb/',include('cmdb.urls')),
    re_path(r'^openstack/',include('openstack.urls')),
]

include()本质

include()函数实际上就是返回一个元组:([], None, None)
第一个元素可以是一个列表,列表中盛放url()子路由配置;
第二个元素是app_name,可以为None;
第三个元素是namespace,需要反向生成url时,可根据需求填写;

所以urls.py也可以这样写:

from django.conf.urls import url

urlpatterns = [
    url(r'^openstack/',([],None,None,),   # 直接用一个元组替代include()
]

如下,直接进行子路由分发:

from django.conf.urls import url,include
urlpatterns = [
    # url(r'^openstack/',include('openstack.urls',namespace='openstack')),
    # 上下效果相同
    url(r'^openstack/',([
        url(r'^host/',cviews.host,name='host'),
        url(r'^host/add/',cviews.addhost,name='addhost'),
        url(r'^host/(\d+)/change',cviews.changehost,name='changehost'),
    ],None,'openstack')),
]

  • URL name 使用详解
from django.urls import re_path
from django.contrib import admin
from calc import views as calc_views
 
urlpatterns = [
    re_path(r'^add/$', calc_views.add, name='add'),
    re_path(r'^add/(\d+)/(\d+)/$', calc_views.add2, name='add2'),
    # path(r'^admin/', admin.site.urls),
]

比如 path(r'^add/$', calc_views.add, name='add'), 这里的 name='add' 是用来干什么的呢?

简单说,name 可以用于在 templates, models, views ……中得到对应的网址,相当于“给网址取了个名字”,只要这个名字不变,网址变了也能通过名字获取到。

  1. 模板中利用name方式
    不带参数的:
    {% url 'name' %}

    带参数的:参数可以是变量名
    {% url 'name' 参数 %}

    <!-- 以下示例 -->
    不带参数:
    <a href="{% url 'add' %}">添加</a>

    结果:
    <a href="/add/">添加</a>  # 上面模板经过渲染后的标签是这样的:


    带参数的:
    <a href="{% url 'add2' 4 5 %}">link</a>

    结果:
    <a href="/add/4/5/">link</a>  # 经过渲染后:

当 urls.py 进行更改,前提是不改 name(这个参数设定好后不要轻易改),获取的网址也会动态地跟着变,比如改成: re_path(r'^new_add/(\d+)/(\d+)/$', calc_views.add2, name='add2'),

注意: add 变成了 new_add,但是后面的 name='add2'没改,这时 {% url 'add2' 4 5 %} 就会渲染对应的网址成/new_add/4/5/


  1. 向视图传递额外的参数方式

path()中允许你传递一个Python字典作为额外的关键字参数给视图函数。

    from django.urls import re_path
    from . import views

    urlpatterns = [
        re_path(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}),
    ]

在上面的例子中,对于/blog/2005/请求,Django将调用views.year_archive(request, year='2005', foo='bar')。

注意:命名关键字参数和在字典中传递的额外参数要避免重名。


  1. 利用reverse函数反向生成URL方式

urls.py

    from django.urls import re_path
    from app01 import views as views

    urlpatterns = [
        re_path(r'^login/',views.login),
        re_path(r'^index/first/',views.index,name='first'),
    ]

views.py

    from django.shortcuts import render,HttpResponse,redirect
    from django.urls import reverse

    def login(request):
        url = reverse('first')
        print(url)     # 打印结果:/index/first/
        return redirect(url)


    def index(request):
        return HttpResponse('index/first')

  1. 对有参数的url设置name方式

urls.py:

    from django.urls import re_path
    from . import views

    urlpatterns = [
        re_path(r'^articles/([0-9]{4})/$', views.year_archive, name='news-year-archive'),
    ]

模板代码中使用:

    <a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a>   # 注意模版语言的用法,注意参数的传递方法
    {# Or with the year in a template context variable: #}
    <ul>
    {% for yearvar in year_list %}
    <li><a href="{% url 'news-year-archive' yearvar %}">{{ yearvar }} Archive</a></li>
    {% endfor %}
    </ul>		

views.py视图函数:

    from django.urls import reverse
    from django.http import HttpResponseRedirect

    def redirect_to_year(request):
        year = 2006
        # ...注意参数的传递方法
        return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))

  1. 对 URL 设置参数 namespace

假设路由中有两个url 的 name 参数重复了,在反向解析时候这种情况Django是分不清楚的;这就需要给路由加参数 namespace防止name重复情况。

    # mytest/urls.py  # 项目下的路由
    from django.urls import re_path, include

    urlpatterns = [
        re_path('^app01/', include('app01.urls'), namespace='app01'),
        re_path('^web/', include('web.urls')),
    ]
    # app01/urls.py    应用1
    from django.urls import re_path
    from web.views import account

    urlpatterns = [
        re_path('register.html$', account.register, name='register'),
    ]
    # app02/urls.py    应用2
    from django.urls import re_path
    from web import views

    urlpatterns = [
        re_path('register.html$', views.register, name='register'),
    ]

以上两个应用的示例中,name参数重复了;当 app01 应用的路由加了参数 namespace 后,反向解决URL时需要使用:namespace : name格式 (app01:register),来进行反向解析。



视图函数

  • HttpResponse 用法:

HttpResponse : 只支持字符串格式,传入的内容会直接在页面中显示

  1. 在mysite/app01/views.py里写视图函数:
from django.urls import path
from django.shortcuts import HttpResponse

def login(request):
    """    处理用户请求,并返回内容
    :param request: 用户请求相关的信息(对象)
    :return: 页面要显示的内容  
    """
    # HttpResponse  只接收字符串信息
    return HttpResponse("登录页面!")   # 传入什么内容,页面就显示什么内容。

  1. 在mysite/urls.py里写URL绑定视图:
from django.urls import path
from app01 import views

urlpatterns = [
    # URL 与视图函数进行绑定。
    path('login/', views.login),   ]



  • render 用法:

render : 主要是给HTML模板进行渲染,将服务端的数据渲染后传入到页面中。

参数:request、'HTML模板文件名' 、{需要传递的数据}

ps: 最后一个参数为可选项,为字典格式。

  1. 在mysite/app01/views.py里写视图函数:
from django.urls import path
from django.shortcuts import HttpResponse

def login(request):
    """    处理用户请求,并返回内容
    :param request: 用户请求相关的信息(对象)
    :return: 页面要显示的内容  
    """
    # HttpResponse  只接收字符串信息
    return  render(request, 'login.html') 

login.html文件在[mysite\templates\login.html] 路径下。
render 模块会根据[settings]配置文件,自动的从templates目录下查找对应的HTML模板文件,将读取内容返回页面。


  1. 在mysite/templates 目录下,创建login.html文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>用户登录</h1>
    <input type="text" name="name">
</body>
</html>

  1. 在mysite/urls.py里写URL绑定视图:
from django.urls import path
from app01 import views

urlpatterns = [ # URL 与视图函数进行绑定。
                path('login/', views.login),   ]

  • redirect 用法:

redirect : 给页面加个[添加]跳转页面,在[添加]页面往数据库中添加数据后返回指定页面。

  1. 在mysite/app01/views.py里写视图函数:
from django.shortcuts import render, redirect

def index(request):
    if request.method == "POST":
        return redirect('http://www.baidu.com')      # 配置跳转页面
    else:
        return render(request, 'index.html')
  1. 在mysite/urls.py里写URL绑定视图:
from django.urls import re_path
from app01 import views   
urlpatterns = [
    re_path(r'^index/', views.index),  ]
  1. 在mysite/templates 目录下,创建index.html文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
    <form method="post" action="/index/">
        <input type="text" name="text">
        <input type="submit" name="提交">
	</form> 
</body>
</html>




推荐阅读