python Django框架之URL与视图(3)

文章目录

  • 一、视图的介绍
    • 1.视图是什么?
    • 2.视图模板的配置
    • 3.视图函数的使用
  • 二. URL映射
    • 1.URL路由分发
    • 2.URL反向解析
    • 3.URL正则路径
    • 4.URL命名空间

一、视图的介绍

1.视图是什么?

视图函数(或简称视图)只是一个Python函数,它接受Web请求并返回Web响应。此响应可以是网页的HTML内容,重定向,404错误,XML文档或图像。。。

无论视图本身包含什么逻辑,都要返回响应。代码写在哪里都可以,只要在 Python 目录下面,一般放在项目的 views.py 文件中。

每个视图函数都负责返回一个 HttpResponse 对象,对象中包含生成的响应。视图层中有两个重要的对象:请求对象(request)与响应对象(HttpResponse)。



2.视图模板的配置


在创建一个模板时,我们需要在指定的文件夹下存放html模板时,都必须存放在template目录下,当然这是可以修改的,而修改需要在主目录\settings.py配置文件中修改,如下:
TEMPLATES = [
    {
     
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')] # 这里修改文件名
        ,
        'APP_DIRS': True,
        'OPTIONS': {
     
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

前面也提过了静态文件的存放目录相关配置,这里在回顾一下,如下:
STATIC_URL = '/static/'
# 配置静态文件的名字,这里写的和上面同名,以后的静态文件就可以存放在当前目录下了
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)

视图定义好后,要与URL进行映射,从而保证用户在浏览器中输入指定url可以请求到这个视图函数。 在输入了某个url、请求网站时,django会从项目的urls.py文件中寻找对应的视图,这在settings.py中已经进行配置如下:
ROOT_URLCONF = 'untitled4.urls'

在urls.py文件中有一个urlpatterns变量,django会从这个变量中读取所有的匹配规则。 匹配规则需要使用 django.urls.pathdjango.urls.url函数进行包裹,这个函数会根据传入的参数返回URLPattern或者是URLResolver的对象,如下:
from app01 import views
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),
]

3.视图函数的使用


前面已经提到过,视图一般都在app的views.py中定义,并且视图的第一个参数必须是request(一个HttpRequest对象,准确来说是一个 django.core.handlers.wsgi.WSGIRequest对象),同时返回结果必须是 HttpResponseBase对象或者子类的对象。
视图一般用于完成一些业务逻辑,在Django中一般用于和前后端响应,交互,来达成前后端不分离效果。
响应对象主要有三种形式:
  • 一.HttpResponse
    • 1.Django服务器接收到客户端发送过来的请求后,会将提交上来的这些数据封装成一个 HttpRequest 对象传给视图模板
    • 2.视图函数在处理完相关的逻辑后,也需要返回一个响应给浏览器。而这个响应,可以是一个字符串或一个html标签...
    • 3.而 HttpResponse 则是 HttpResponseBase 用得最多的子类。

  • 二.render
    • 1.同HttpResponseBase一样同样需要上传reuqest对象
    • 2.返回一个视图模板
    • 3.(可选)返回一个字典传到里面的参数将会被渲染到视图模板里,用的相对是比较多的。

  • 三.redirect
    • 1.重定向,跳转新页面。参数为字符串,字符串中填写页面路径。一般用于 form 表单提交后,跳转到新页面。


每种方法都有相应的用武之地,这里我们先拿HttpResponse举例。


例如app01/views.py如下:
def index(request):

    return HttpResponse('我是字符串,我后面的是一个html标签')

urls.py如下:
from app01 import views
from django.urls import path

urlpatterns = [
    path('index/', views.index),
]

此时访问127.0.0.1:8000/index/如下:
python Django框架之URL与视图(3)_第1张图片

显然,可以传入一个或多个参数。

需要注意:
视图函数中的参数名必须与urls.py中尖括号中的参数名保持一致,否则会出错。


还可以传入多个参数,app01/views.py如下:
from django.shortcuts import render, HttpResponse

def index(request):
    return HttpResponse('我是字符串,我后面的是一个html标签')


def index_detail(request, index_id, index_name):
    return HttpResponse('我的id是%s我的名字是%s' % (index_id, index_name))

urls.py如下:
from django.urls import path
from app01 import views

urlpatterns = [
    path('index/',views.index),
    # 这里是可以自行定义类型的哦,类型不同就会报错~
    path('index/int:/', views.index_detail),
]

访问测试如下:

python Django框架之URL与视图(3)_第2张图片

不仅如此,django还可以提供关键字传参,取参数值从request取值使用get()方法获取,且关键字能写多个,即:127.0.0.1:8000/index/?id=1&name=sehun,需要注意的是视图函数中的参数名必须与urls.py中尖括号中的参数名保持一致,否则会出错


views.py如下:
from django.shortcuts import render, HttpResponse


def index(request):
    return HttpResponse('我是字符串,我后面的是一个html标签')


def index_detail(request, index_id, index_name):
    return HttpResponse('我的id是%s我的名字是%s' % (index_id, index_name))


def index_list(request):
    index_id = request.GET.get('id')
    index_name = request.GET.get('name')
    return HttpResponse('id:%s 名字:%s' % (index_id, index_name))

urls.py中路由不需要进行处理,如下:
from django.urls import path
from app01 import views

urlpatterns = [
    path('index/',views.index),
    path('index//', views.index_detail),
    path('index_list/',views.index_list),
]

此时访问显示:

python Django框架之URL与视图(3)_第3张图片

显然什么都不写也不会报错,只是会返回一个none(空)的参数。



二. URL映射


1.URL路由分发

在项目中,一般不可能只有一个app,而是有多个app,如果把所有app的views中的视图都放在urls.py中进行映射,肯定会让代码显得冗长混乱,后期维护也不方便。并且还不能同时导入views会重名,必须使用 as 别名 来实现不重名 相当麻烦,因此Django提供了一个机制,即URL模块化,可以在app内部包含自己的url匹配规则,而在项目的urls.py中再统一包含这个app的urls,这需要用到include函数

  1. 首先我们先在app01下新建urls.py

app01/urls.py如下:
from django.urls import path
from . import views


urlpatterns = [
    path('', views.index),
    path('index_detail//', views.index_detail),
    path('index_list/', views.index_list),
]

  1. 然后,我们通过python manage.py startapp app02命令再创建一个app名为app02。

  2. 在app02下新建urls.py


app02/urls.py如下:
from django.urls import path
from . import views

urlpatterns = [
    path('login/', views.login),
]

主目录/urls.py如下:
from django.contrib import admin
from django.urls import path, include
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include('app01.urls')),
    path('app02/', include('app02.urls')),
]

app02/views.py如下:
from django.shortcuts import render, HttpResponse, redirect

def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        user = request.POST.get('username')
        pwd = request.POST.get('password')
        if user == 'root' and pwd == '123':
        	# 重定向到app01下的index
            return redirect('/app01/index/')
        else:
            return render(request, 'login.html', {
     'msg': '用户名或密码错误'})

render和HttpResponse的区别是多了可传参数的渲染和html模板,而redirect相当于重定向到目标路由位置


主目录/login.html如下:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/app02/login/" method="post">
        {
     % csrf_token %}
        <p>用户
            <input type="text" name="username"><span style="color: red;">{
     {
      msg }}</span>
        </p>
         <p>密码
            <input type="password" name="password">
        </p>
        <input type="submit" value="提交">
    </form>
</body>
</html>

模板语言标记:

  1. 使用{ {}} 用于接收render传来的字典数据,在html模板中先渲染在返回给浏览器
  2. 接收来的数据名字要和后台视图传来的要一致,不然拿不到。
  3. 如果在视图传来的数据是一个字典里嵌套这列表或字典,字典的可以通过{ {msg.keys}}来获取value值,而列表的可以通过{ {msg.0(列表下标从0开始)}}来获取。

csrf_token:相当于一个登陆验证(留着以后再讲,明白是个登陆验证,不写是使用不了post请求的)


示意如下:

python Django框架之URL与视图(3)_第4张图片
显然,可以正常访问。

可以看到,此时访问每个视图函数,必须加上在主目录/urls.py中path方法中传入的第一个参数,如访问app01下的app就必须在url中加入app01前缀

include方法的作用是拼接主目录/urls.py中path方法的第一个参数与每个app中urls.py中定义的路由,以形成完整的路由。


2.URL反向解析

随着功能的增加,路由层的 url 发生变化,就需要去更改对应的视图层和模板层的 url,非常麻烦,不便维护。

这时我们可以利用反向解析,当路由层 url 发生改变,在视图层和模板层动态反向解析出更改后的 url,免去修改的操作。

反向解析一般用在模板中的超链接及视图中的重定向。

拿上面的例子做比方


app02/views.py如下:
from django.shortcuts import render, HttpResponse, redirect

def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        user = request.POST.get('username')
        pwd = request.POST.get('password')
        if user == 'root' and pwd == '123':
        	# 重定向到app01下的index
            return redirect('/app01/index/')
        else:
            return render(request, 'login.html', {
     'msg': '用户名或密码错误'})

这里我们用了重定向的方法,访问到了app01下的index,但是如果在重定向较多,而代码的路由发生了更改,那么就要一个一个的更改,显然,这是让人头大的,稍有不慎,就会显入万劫不复…

所以django的路由模块中,还有一个方式。


方式是在url.py中的path方法中传入name参数即可,app01/urls.py如下:
from django.urls import path
from . import views


urlpatterns = [
    path('index/', views.index,name='index'),
    path('index_detail//', views.index_detail),
    path('index_list/', views.index_list),
]

此时app01下的index被我们取了一个index的别名。

但是要怎么样可以告诉redirect呢,此时name的好兄弟reverse来了,它不仅可以给redirect渲染,还可以给html模板的post请求的


在模板 templates 中的 HTML 文件中,利用 {% url "路由别名" %} 反向解析。
<form action="{% url 'index' %}" method="post"> 

app02/views.py如下:
from django.shortcuts import render, HttpResponse, redirect, reverse


def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        user = request.POST.get('username')
        pwd = request.POST.get('password')
        if user == 'root' and pwd == '123':
            return redirect(reverse('index'))
        else:
            return render(request, 'login.html', {
     'msg': '用户名或密码错误'})

效果还是一样的。


3.URL正则路径

有时候定义url匹配需要使用正则表达式来实现一些复杂的需求,这时候我们可以使用re_path来实现,其参数和path一致,只不过第一个参数即route参数可以是一个正则表达式。

还有django.conf.urls.url也支持正则表达式匹配路由,其底层也是调用的re_path(regex, view, kwargs, name)方法。

显然,定义路由时path(’’, views.music),等价于re_path(r’^$’, views.music)


此时我们在主目录/urls.py中定义视图如下:
from django.contrib import admin
from django.urls import path, re_path, reverse, include
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include('app01.urls', namespace='app01')),
    path('app02/', include('app02.urls', namespace='app02')),
    # 首页
    re_path(r'music.html$', views.music),
     # 匹配默认参数
    path('page', views.page),
    # 匹配一般参数
    path('page/', views.page),
    # 正则表达式
    # re_path(r'^$', views.music),
    re_path(r'retest/(?P\d{4})$', views.year_re),
    re_path(r'retest/(?P\d{2})$', views.month_re),
]

其中,(?P\d{4})是给正则表达式分组\d{4}取名,即通过?P<>给正则表达式分组取名,来与视图函数中的参数名匹配。


在app01/views如下:
from django.shortcuts import render, HttpResponse


def index(request):
    return HttpResponse('我是字符串,我后面的是一个html标签')


def index_detail(request, index_id, index_name):
    return HttpResponse('我的id是%s我的名字是%s' % (index_id, index_name))


def index_list(request):
    index_id = request.GET.get('id')
    index_name = request.GET.get('name')
    return HttpResponse('id:%s 名字:%s' % (index_id, index_name))


def login(request):
    return render(request, 'login.html')


def music(request):
    return HttpResponse('音乐网站首页')


def page(request, page_num=1):
    return HttpResponse('音乐 第%s页' % page_num)


def year_re(request, year):
    return HttpResponse('年份为:%s' % year)


def month_re(request, month):
    return HttpResponse('月份为:%s' % month)

此时访问127.0.0.1:8000/music.html显示:
python Django框架之URL与视图(3)_第5张图片
最后实现了自动匹配了。


4.URL命名空间

命名空间(英语:Namespace)是表示标识符的可见范围:

  • 一个标识符可在多个命名空间中定义,它在不同命名空间中的含义是互不相干的。
  • 一个新的命名空间中可定义任何标识符,它们不会与任何重复的标识符发生冲突,因为重复的定义都处于其它命名空间中。

存在问题:路由别名 name 没有作用域,Django 在反向解析 URL 时,会在项目全局顺序搜索,当查找到第一个路由别名 name 指定 URL 时,立即返回。当在不同的 app 目录下的urls 中定义相同的路由别名 name 时,可能会导致 URL 反向解析错误。

解决:使用命名空间

比如app01/urls.py和app02/urls.py修改如下:


app01/urls.py如下:
from django.urls import path
from . import views


urlpatterns = [
    path('index/', views.index,name='index'),
    path('index_detail//', views.index_detail),
    path('index_list/', views.index_list),
]

app02/urls.py如下:
from django.urls import path
from . import views

urlpatterns = [
    path('login/', views.login),
    path('index/', views.index,name='index'),

]

添加一个app02/urls.py下的index函数:
from django.shortcuts import render, HttpResponse, redirect, reverse


def index(request):
    return HttpResponse('app02主页面')

此时访问127.0.0.1:8000/app02/login/显示:
python Django框架之URL与视图(3)_第6张图片
很显然,reverse已经不知道是哪个index别名了,所以我们需要在用namespace='实例命名空间’来实例命名空间。


主目录/urls.py如下:
from django.contrib import admin
from django.urls import path reverse, include,re_path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include('app01.urls', namespace='app01')),
    path('app02/', include('app02.urls', namespace='app02')),
    re_path(r'login.html$', views.music),
]

app01/urls如下:
from django.urls import path
from . import views

# 设定命名空间
app_name = 'app01'

urlpatterns = [
    path('index/', views.index,name='index'),
    path('index_detail//', views.index_detail),
    path('index_list/', views.index_list),
]


app02/urls如下:
from django.urls import path
from . import views

# 设定命名空间
app_name = 'app02'

urlpatterns = [
    path('login/', views.login),
    path('index/', views.index,name='index'),

]


app02/views如下:
from django.shortcuts import render, HttpResponse, redirect, reverse


def index(request):
    return HttpResponse('app02主页面')


def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        user = request.POST.get('username')
        pwd = request.POST.get('password')
        if user == 'root' and pwd == '123':
            return redirect(reverse('app01:index'))
        else:
            return render(request, 'login.html', {
     'msg': '用户名或密码错误'})

此时访问127.0.0.1:8000/app02/login/如下:
python Django框架之URL与视图(3)_第7张图片
此时两者相同的命名就已经分开了,不再覆盖彼此。


假设访问注册首页重定向到文章页,app02/views.py如下:
from django.shortcuts import render, HttpResponse, redirect, reverse


def index(request):
    return HttpResponse('app02主页面')


def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        user = request.POST.get('username')
        pwd = request.POST.get('password')
        if user == 'root' and pwd == '123':
            return redirect(reverse('app01:index_detail'))
        else:
            return render(request, 'login.html', {
     'msg': '用户名或密码错误'})

app01/urls如下:
from django.urls import path
from . import views

app_name = 'app01'

urlpatterns = [
    path('index/', views.index, name='index'),
    path('index_detail//', views.index_detail, name='index_detail'),
    path('index_list/', views.index_list),
]

此时访问127.0.0.1:8000/app02/login/并登陆成功会报错。

NoReverseMatch at /app02/login/
Reverse for 'index_detail' with no arguments not found. 1 pattern(s) tried: ['app01/index_detail/(?P[0-9]+)/(?P[^/]+)$']

很明显,reverse()方法中没有传入index_id,index_name参数,路径参数是通过kwargs以字典形式来传递的。


现传入参数app02/views.py如下:
from django.shortcuts import render, HttpResponse, redirect, reverse


def index(request):
    return HttpResponse('app02主页面')


def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        user = request.POST.get('username')
        pwd = request.POST.get('password')
        if user == 'root' and pwd == '123':
            return redirect(reverse('app01:index_detail', kwargs={
     'index_id': 2, 'index_name': 'sehun'}))
        else:
            return render(request, 'login.html', {
     'msg': '用户名或密码错误'})

此时访问127.0.0.1:8000/app02/login/如下:
python Django框架之URL与视图(3)_第8张图片

显然,也获取到了参数。

你可能感兴趣的:(django,URL与视图,Django框架)