Django学习路线之URL与视图

经过前一天得学习(Django学习路线之从虚拟环境开始Django初体验),相信你已经对Django已经有了基本的认识,今天来具体学习一下MVC思想的V(视图)

  1. Django视图
  2. URL映射
  3. URL中添加参数
  4. URL模块化
  5. URL命名与反转
  6. 指定默认的参数
  7. re_path函数

在这里插入图片描述

Django视图

新建一个名为django_view的项目:
在这里插入图片描述
并新建一个名为news的app:
在这里插入图片描述
在新建的app里的 views.py文件里补充如下代码:

from django.http import HttpResponse

# Create your views here.

def news(request):
    return HttpResponse("新闻首页")

回到项目的urls.py文件做映射:

from news import views

刚导入的时候可能会有如下报错:
在这里插入图片描述
这时候只需要对项目文件夹进行如下操作即可:
在这里插入图片描述
把映射写完后:

from news import views

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

进入网站界面:
在这里插入图片描述
因为没有定义首页,所以首页会报错,但是进入news的界面已经定义了,所以直接进入即可:
在这里插入图片描述

方法里的request参数

接下来我们回到news的views.py文件,看一下request的类型:

def news(request):
    print(type(request))
    return HttpResponse("新闻首页")

看一下request的类型:

<class ‘django.core.handlers.wsgi.WSGIRequest’>

可以看出这是一个类,下面看一下这个类的源码:

from django.core.handlers.wsgi import WSGIRequest

点进去看一下:
在这里插入图片描述
看一下这个类里的方法:
在这里插入图片描述
既然是类,我们就可以调用里面的方法,下面来试一下调用method方法:
在这里插入图片描述
清空输出再刷新一下页面:
在这里插入图片描述
发现这是一个GET请求

注意事项

  • request参数是必须的
  • 返回的HttpResponse()也是必须的

如果直接返回字符串会报错,如下图所示:
在这里插入图片描述
‘str’ object has no attribute ‘get’

视图的总结

视图一般都写在app的views.py中

并且视图的第一个参数永远都是request(一个HttpRequest)对象。这个对象存储了请求过来的所有信息,包括携带的参数以及一些头部信息等。在视图中,一般是完成逻辑相关的操作。

比如这个请求是添加一篇博客,那么可以通过request来接收到这些数据,然后存储到数据库中,最后再把执行的结果返回给浏览器。

视图函数的返回结果必须是HttpResponseBase对象或者子类的对象。示例代码如下:

news/views.py:

from django.http import HttpResponse
def news(request):
    return HttpResponse("这是新闻首页")

urls.py:

from news import views

urlpatterns = [
    path("news",views.news)
]

URL映射

基本原理

视图写完后,要与URL进行映射,也即用户在浏览器中输入什么url的时候可以请求到这个视图函数

向浏览器输入:http://127.0.0.1:8000/news/

且urls.py里添加了映射:

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

因此才能访问到如下界面:
在这里插入图片描述
换句话说,在用户输入了某个url,请求网站的时候,django会从项目的urls.py文件中寻找对应的视图

另外,打开settings.py文件,找到ROOT_URLCONF:
在这里插入图片描述
如果把这一行注释,那么就无法找到对应的视图了

问题的引入

假设我多了一个book的界面:

from django.contrib import admin
from django.urls import path
from news import views
from book import views

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

这时会出现两个views,这时因为不知道使用哪一个views,所以会报错

要解决这一问题,请继续往下看

URL中添加参数

有时候,url中包含了一些参数需要动态调整。

比如我的某篇CSDN文章的url:
https://blog.csdn.net/zbp_12138/article/details/105962054

后面的105962054就是我这篇文章的id

那么主页的url就可以写成:
https://blog.csdn.net/zbp_12138/article/details/

那么如何在django中实现这种需求呢。这时候我们可以在path函数中,使用尖括号的形式来定义一个参数

指定参数

urls.py:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('news/', views.news),
    # news_id 必须和视图函数中的参数保持一致
    path('news/<news_id>', views.news_detail)
]

news_id 必须和视图函数中的参数保持一致

在views.py里补充对应的方法:

def news_detail(request,news_id):
    return HttpResponse("新闻详情页-{}".format(news_id))

效果如下:
在这里插入图片描述
但是这样的写法不够安全,我们随意打开百度搜索一个页面:
在这里插入图片描述
可以看到后面的id都是乱码,那么怎么实现这一功能呢?

查询字符串的方式传递参数

下面的部分把刚刚的代码注释,便于大家比对

urls.py:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('news/', views.news),
    # news_id 必须和视图函数中的参数保持一致
    # path('news/<news_id>', views.news_detail)
    # 查询字符串的方式传递参数
    path('news/news_detail/', views.news_detail)
]

里面就不使用尖括号了,改动主要在views.py里:

# def news_detail(request,news_id):
#     return HttpResponse("新闻详情页-{}".format(news_id))

def news_detail(request):
    news_id = request.GET.get("news_id")
    return HttpResponse("新闻详情页-{}".format(news_id))

这次把参数放在方法里

来看一下效果:http://127.0.0.1:8000/news/news_detail/?news_id=2
在这里插入图片描述

URL模块化

回到URL映射里提到的那个问题,这里我们新建一个名为book的app:
在这里插入图片描述
在urls.py里添加如下代码:

from news import views
from book import views

直接运行会发生报错:
在这里插入图片描述
AttributeError: module ‘book.views’ has no attribute ‘news’

也就是说当前有两个views,程序不知道使用哪个views,而且现在只有两个app,到后期app更多时,这样的写法肯定会有问题

因此我们可以用as语句来优化一下:

from news import views
from book import views as bviews

不过这也有个问题,虽然不报错了,但是如果把所有的app的views中的视图都放在urls.py中进行映射,肯定会让代码显得非常乱

因此django给我们提供了一个方法,可以在app内部包含自己的url匹配规则,而在项目的urls.py中再统一包含这个app的urls。使用这个技术需要借助include函数。下面是示例代码

django_views/urls.py文件:
在这里插入图片描述

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path("news/",include("news.urls"))
]

在news里新建一个urls.py:

from django.urls import path
from . import views #从当前目录导入views

urlpatterns = [
    path("",views.news),
    path('news_detail/', views.news_detail)
]

如此一来,在django_views/urls.py文件中把所有的和news这个app相关的url都移动到news/urls.py中了,django_first/urls.py中,通过include函数包含news.urls,以后在请求news相关的url的时候都需要加一个news的前缀

那么在book这个app里也是同样的做法

在django_view\urls.py里补充book的url映射:

urlpatterns = [
    path("news/",include("news.urls")),
    path("book/",include("book.urls"))
]

在book\views里补充方法:

from django.http import HttpResponse

# Create your views here.

def book(request):
    return HttpResponse("图书首页")

def book_detail(request, book_id):
    return HttpResponse("图书详情页-{}".format(book_id))

在book里新建urls.py:

from django.urls import path
from . import views

urlpatterns = [
    path("",views.book),
    path("book_detail/<book_id>",views.book_detail)
]

效果是一样的:
在这里插入图片描述
这里不知道大家有没有注意到,book_id应该为int类型:

def book_detail(request, book_id):
    print(type(book_id))
    return HttpResponse("图书详情页-{}".format(book_id))

打印一下:
在这里插入图片描述
很明显,并不是int类型,传一个字符串也是可以的:
在这里插入图片描述
那么这里就出现一个问题,我无法用这里的id做SQL查询,或者是在列表里根据id取值

这时试一下强制类型转换:

def book_detail(request, book_id):
    print(type(book_id))
    new_book_id = int(book_id)
    print(type(new_book_id))
    return HttpResponse("图书详情页-{}".format(new_book_id))

保存并运行一下:
在这里插入图片描述
直接就报错了: invalid literal for int() with base 10: ‘zbp’

Django内置转换器

这里有一个优化的方法,在book\urls.py里:

path("book_detail/<int:book_id>",views.book_detail

尖括号里由<book_id>改成 <int: book_id>

这时再试一下输入字符串:
在这里插入图片描述
报错信息变成了:“没有找到页面”,这样的效果是比直接报错要好的

另外,输入一个数字,输出一下id的类型:
在这里插入图片描述
可以看到已经变成了int类型

除了int和str,还有其他的类型可以输出:

from django.urls import converters

来看一下Django内置的5个类型:
在这里插入图片描述
UUID:https://www.cnblogs.com/franknihao/p/7307224.html

URL命名与反转

  1. 为什么需要URL命名
    因为在项目开发的过程中URL地址可能经常变动,如果写死会经常去修改

  2. 如何给一个URL指定名称
    path("",views.index,name=“index”)

  3. 应用命名空间
    在多个app之间可能产生同名的URL,这时候为了避免这种情况,可以使用命名空间来加以区分。在urls.py中添加app_name即可。

下面新建两个app,分别是前台和后台:
在这里插入图片描述
接下来是代码补充,跟前面讲到的方法是一样的,我们再一起来过一遍,加深印象:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
来测试一下:
在这里插入图片描述
在这里插入图片描述

重定向

接下来要实现的功能是判断用户是否登陆,如果没有登陆,就跳转到登陆界面

拿知乎的官网举例:https://www.zhihu.com/

直接访问时,会直接跳转到登陆界面:https://www.zhihu.com/signin?next=%2F
在这里插入图片描述
下面来到front\views.py实现这一功能,我们一步步来:

def index(request):
    # url是否传递username
    username = request.GET.get("username")
    if username:
        return HttpResponse("前台首页")
    else:
        # 跳转到登陆界面
        pass

下面我们需要redirect(),进行重定向,完整代码如下:

from django.shortcuts import render,redirect
from django.http import HttpResponse

# Create your views here.

def index(request):
    # url是否传递username
    username = request.GET.get("username")
    if username:
        return HttpResponse("前台首页")
    else:
        # 跳转到登陆界面
        return redirect("login/")

def login(request):
    return HttpResponse("前台登录界面")

下面来看一下效果: http://127.0.0.1:8000/front/
在这里插入图片描述
自动跳转到了: http://127.0.0.1:8000/front/login/
在这里插入图片描述
但这时有个问题,这里的映射都必须对应:
在这里插入图片描述
在这里插入图片描述
这样一来,改起来就会很麻烦,app一多就更难改了,这时就要用到反转

反转

在front\urls.py里增加一个参数name:

from django.urls import path
from . import views

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

这样一来,前面的映射写什么地址都行,都不影响页面的跳转

在front\views.py里多导入一个reverse库:

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

# Create your views here.

def index(request):
    # url是否传递username
    username = request.GET.get("username")
    if username:
        return HttpResponse("前台首页")
    else:
        # 跳转到登陆界面
        return redirect(reverse("login"))

def login(request):
    return HttpResponse("前台登录界面")

跳转时使用反转,参数对应于front\urls.py里参数name的值

而这时也有一个问题,前台和后台的name一样的话:
在这里插入图片描述
在这里插入图片描述
在多个app之间可能产生同名的URL,这时候为了避免这种情况,可以使用命名空间来加以区分

在cms\urls.py里加上app_name:

app_name = "cms"

在这里插入图片描述
在cms\views.py里:
在这里插入图片描述
这时来看一下效果,输入http://127.0.0.1:8000/cms/:
在这里插入图片描述
得到的返回:http://127.0.0.1:8000/cms/login/
在这里插入图片描述

应用命名空间和实例命名空间

一个app,可以创建多个实例。可以使用多个URL映射同一个App。在做反转的时候,如果使用应用命名空间,就会发生混淆,为了避免这个问题,可以使用实例命名空间,实例命名空间使用,namespace=‘实例命名空间’

urls.py:

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('cms1/', include("cms.urls",namespace='cms1')),
    path('cms2/', include("cms.urls",namespace='cms2')),
    path('front/', include("front.urls")),
]

URL反转传递参数

如果这个url中需要传递参数,那么可以通过kwargs来传递参数

front\views.py里的reverse加入kwargs:

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

# Create your views here.

def index(request):
    # url是否传递username
    username = request.GET.get("username")
    if username:
        return HttpResponse("前台首页")
    else:
        # 跳转到登陆界面
        return redirect(reverse("front:login", kwargs={"uid":1}))

def login(request, uid):
    return HttpResponse("前台登录界面-接收到的uid为{}".format(uid))

front\views.py:

from django.urls import path
from . import views

app_name = "front"

urlpatterns = [
    path("",views.index),
    path("login/<uid>",views.login, name = "login")
]

下面是效果:

访问http://127.0.0.1:8000/front/
在这里插入图片描述
如果要传入字符串:

把front\urls.py里的path改成path(“login/”,views.login, name = “login”),即去掉< uid >

然后到front\views.py,把代码改成:

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

# Create your views here.

def index(request):
    # url是否传递username
    username = request.GET.get("username")
    if username:
        return HttpResponse("前台首页")
    else:
        # 跳转到登陆界面
        login_url = reverse("front:login") + "?name=zbp"
        return redirect(login_url)

def login(request):
    uid = request.GET.get("name")
    return HttpResponse("前台登录界面-接收到的uid为{}".format(uid))

因为django中的reverse反转url的时候不区分GET请求和POST请求,因此不能在反转的时候添加查询字符串的参数

效果如下:
在这里插入图片描述

指定默认的参数

在book\views.py里添加图书列表以及对应方法:


book_list = ["a", "b", "c", "d"]

def book_lists(request):
    return HttpResponse(book_list[0])

在book\urls.py补充映射:

urlpatterns = [
    path("",views.book),
    path("book_detail/<int:book_id>",views.book_detail),
    path("book_lists/",views.book_lists)
]

效果如下:
在这里插入图片描述
图书详情页的功能也可以进行完善:

book\views.py:

def book_detail(request, book_id = 1):
    print(type(book_id))
    return HttpResponse("图书详情页-{}".format(book_list[book_id]))

给book_id指定一个默认参数1

book\urls.py:

urlpatterns = [
    path("",views.book),
    path("book_detail/<int:book_id>",views.book_detail),
    path("book_detail/",views.book_detail),
    path("book_lists/",views.book_lists)
]

看一下效果:
在这里插入图片描述
默认的参数是1,传一个其他的参数:
在这里插入图片描述

re_path函数

有时候在写url匹配的时候,想要写使用正则表达式来实现一些复杂的需求,那么这时候我们可以使用re_path来实现

re_path的参数和path参数一模一样,只不过第一个参数也就是route参数可以为一个正则表达式

在news\urls.py里添加re_path:

urlpatterns = [
    path("",views.news),
    path('news_detail/', views.news_detail),
    # r是原生字符串
    re_path(r'news_list/(?P<year>\d{4})', views.news_list)
]

年份是4位的,问号大P用于命名< year >, 匹配4个长度

在news\viewss.py里补充news_list方法:

def news_list(request, year):
    return HttpResponse("新闻列表页面-{}年".format(year))

来看一下效果:
在这里插入图片描述
这里需要注意的是,如果传入5位的参数,也只会匹配前4位:
在这里插入图片描述

如果要匹配月份,写法如下:

re_path(r"^article_list/(?P<mouth>\d{2})/",views.article_mouth)

猜你喜欢

转载自blog.csdn.net/zbp_12138/article/details/105991996