Django模块web_app开发

创建超级管理员

打开pycharm命令行,在manage.py同级的目录路径下执行,如在这里插入图片描述
打开后如下图,
在这里插入图片描述
命令行执行 python manage.py migrate ,生成迁移表(不生成会报错 django.db.utils.OperationalError: no such table: auth_user)

在这里插入图片描述
执行命令 python manage.py createsuperuser ,创建用户名 邮箱 密码(密码输入是看不见的,正常现象)
在这里插入图片描述
浏览器打开 127.0.0.1\admin 验证即可在这里插入图片描述

一、Django简介

百度百科:一个开放源代码的Web框架,由Python语言编写…

重点:一个大而全的框架,啥都替你考虑好了。

1. web框架介绍
具体介绍Django之前,必须先介绍Web框架的概念。

Web框架: 别人已经设定好的一个Web网站模板,你学习它的规则,然后“填空”或“修改”成你自己需要的样子。

一般Web框架的架构是这样的:

在这里插入图片描述

其它基于Python的Web框架,如Tornado、Flask、Webpy都是在这个范围内进行增删裁剪的。例如Tornado用的是自己的异步非阻塞通信协议,Flask则只提供了最精简和基本的框架,Django直接使用WSGI通信协议,并实现了大部分Web应用相关的功能。

2. MVC/MTV介绍
MVC:

百度百科:全名Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件工程典范,用业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

通俗解释:一种代码和文件的组织和管理形式!不要被缩写吓到了,这其实就是把代码分散到不同的文件中,把不同类型的文件又放到不同目录下的一种做法,然后取了个高大上的名字。当然,它带来的好处有很多,比如前后端分离,松耦合等等,在使用中你慢慢体会就会逐渐明白它。

其中:

  • 模型(model):定义数据库相关的内容,一般放在models.py文件中。

  • 视图(view):定义HTML等静态网页文件相关,也就是那些HTML、CSS、JS等前端的东西。

  • 控制器(controller):定义业务逻辑相关,就是你的主要代码。

MTV:

Django觉得MVC的字面意思很别扭,不太符合它的理念,就给它改了一下。view不再是HTML相关,而是主业务逻辑V了,相当于控制器。HTML被放在Templates中,称作模板T,于是MVC就变成了MTV。这其实就是一个文字游戏,和MVC本质上是一样的,换了个名字和叫法而已,换汤不换药。

3. Django的MTV模型组织
目录分开,就必须有机制将他们在内里进行耦合。在Django中,典型的业务流程如下图所示:

在这里插入图片描述

二、Django项目实例

  1. 程序安装
    Python3.6、pip3及Pycharm请自行安装。

(1)安装Django:

这里只介绍较为简单的pip3命令安装方式。

win+r,调出cmd,运行命令:

pip3 install django

自动安装Pypi提供的最新的Django版本。

在这里插入图片描述

注意:

  • 建议升级一下pip3,命令python -m pip install --upgrade pip
  • 如果你以前安装过django,则会使用先前缓存的安装文件
  • 使用cmd,请以管理员身份运行,否则可能出现权限问题
    (2)配置系统环境

成功安装Django后,在下图中的路径可找到django-admin.exe文件,将它加入操作系统环境变量中。这样以后调用会比较方便。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

运行django-admin help,能看到下面的内容表示安装过程OK。

在这里插入图片描述

或者进入Python交互式环境(注意一定要进入刚才安装了Django的Python解释器),按下面所示查看安装版本:

>>> import django
>>> django.get_version()
2.2

再或者使用pip list命令,查看是否存在Django模块。

2. 创建Django项目
在Linux等命令行界面下,使用Django提供的命令和vim也能进行项目开发。但是这里推荐使用Pycharm这个目前最好的Python开发IDE,它功能强大,界面友好。(下面所有的操作都在Pycharm中进行。)

点击file–>new project,出现下面的对话框。

在这里插入图片描述

选择Django栏目,输入项目名称,这里采用国际惯例的mysite。选择先前安装好Django2.2的Python解释器版本,点击create创建。(注:这里暂不考虑虚拟环境问题)

选择open in current window,在当前窗口打开。

Django将自动生成下面的目录结构:

在这里插入图片描述

与项目同名的mysite目录中是项目核心文件。templates目录是HTML文件存放处(这是Pycharm安利给我们的),也就是MTV中的T。manage.py是Django项目管理文件。

image.png-36kB

3. 创建APP
在每个Django项目中可以包含多个APP,相当于一个大型项目中的分系统、子模块、功能部件等等,相互之间比较独立,但也可以有联系。所有的APP共享项目资源。

在Pycharm下方的Terminal终端中输入命令:

python manage.py startapp login

这样就创建了一个叫做login的APP,django自动生成“login”文件夹,及一系列文件:

在这里插入图片描述
在这里插入图片描述
4. 编写路由
路由是浏览器输入url,在Django服务器响应url的转发中心。路由都写在urls文件里,它将浏览器输入的url映射到相应的业务处理逻辑也就是视图。简单的urls编写方法如下图:

在这里插入图片描述

5. 编写视图函数
路由转发用户请求到视图函数。视图函数处理用户请求,也就是编写业务处理逻辑,一般都在views.py文件里。我们下面写一个简单的视图函数:

在这里插入图片描述

通过上面两个步骤,我们将index这个url指向了views里的index()视图函数,它接收用户请求,并返回一个“hello world”字符串。

6. 运行web服务
现在我们已经可以将web服务运行起来了。

命令行的方式是:

python manage.py runserver 127.0.0.1:8000

但在Pycharm中,你可以这么干,在上部工具栏中找到下面图示的图标。

在这里插入图片描述

点击下拉箭头:

在这里插入图片描述

点击edit configurations:

在这里插入图片描述

在host中填入127.0.0.1,port中填入8000。

OK确定之后,点击绿色的三角,web服务就运行起来了。

然后按下图所示点击链接:
在这里插入图片描述

自动跳转到浏览器程序界面。显示的却是下图的404页面:

在这里插入图片描述

修改一下url,添加“/index/”,就一切ok了!

可以看到我们的’Hello World!'欢迎词了!

至此,一个最简单的Django编写的web服务就启动成功了。

7. 返回HTML文件
上面我们返回给用户浏览器的是什么?一个字符串!实际上这肯定不行,通常我们都是将HTML文件返回给用户。

下面,我们在templates目录中新建一个index.html文件:

在这里插入图片描述
代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test</title>
</head>
<body>
    <h1 style="color: black">Hello World!</h1>
</body>
</html>

在这里插入图片描述

再回到views.py文件,编辑index视图,注释当前的return语句,导入render方法,换上新的return语句:

在这里插入图片描述

为了让django知道我们的HTML文件在哪里,需要修改settings文件的相应内容。但默认情况下,它正好适用,你无需修改。

在这里插入图片描述

接下来,我们可以重新启动web服务。在浏览器刷新一下,你会看到带有样式的“Hello World”。
在这里插入图片描述

注:这里有个小技巧,在多次频繁重启服务时,可能会不能及时释放端口,容易启动不了服务,修改一下端口就OK了。

8. 使用静态文件
我们已经可以将HTML文件返还给用户了,但是这还不够,前端三大块HTML、CSS、JavaScript,还有各种插件,它们齐全才是一个完整的页面。在Django中,一般将这些静态文件放在static目录中。

接下来,在mysite中新建一个static目录。(下面导入的静态文件仅用于流程展示,并不实际使用它做点什么。)

在这里插入图片描述

你的CSS、JS和各种插件都可以放置在这个目录里。比如这里,我们又在static下新建了一个js目录,然后拷贝了一个jquery-3.2.1.min.js进来:

在这里插入图片描述

为了让Django知道这个static目录的存在,并能够找到这个目录,需要对settings进行配置:

在这里插入图片描述

现在,我们就可以在index.html中引入js文件了:

在这里插入图片描述

重新启动web服务,刷新浏览器,查看结果。当然,你啥都看不出来,因为仅仅引入了一个jqurey而已,-.

9. 接收用户发送的数据
前面,我们将一个要素齐全的HTML文件返还给了用户浏览器。但这还不够,因为web服务器和用户之间没有动态交互。

下面我们设计一个表单,让用户输入用户名和密码,提交给index这个url,服务器将接收到这些数据。

先修改index.html文件。删除原来的内容,写入下面的内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
    <h1>用户输入:</h1>
    <form action="/index/" method="post">
        用户名:<input type="text" name="username" /><br />
        密码:<input type="password" name="password" /><br />
        <input type="submit" value="提交" />
    </form>
</body>
</html>

在这里插入图片描述

重启web服务,刷新页面,如下图所示:

在这里插入图片描述

这时候我们先不要往输入框内输入信息。我们先修改views.py文件:

from django.shortcuts import render
from django.shortcuts import HttpResponse

# Create your views here.


def index(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        print(username, password)
    return render(request, 'index.html')

在这里插入图片描述

重启web服务,刷新index页面,然后我们随便输入点什么东西,点击提交,结果出现了下面的403页面。

在这里插入图片描述

这是因为django有一个跨站请求保护机制,这需要我们在index.html文件中加入一行{% csrf_token %}。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
    <h1>用户输入:</h1>
    <form action="/index/" method="post">
        {% csrf_token %}   <!--加入这行 --> 
        用户名:<input type="text" name="username" /><br />
        密码:<input type="password" name="password" /><br />
        <input type="submit" value="提交" />
    </form>
</body>
</html>

在这里插入图片描述

再次进入浏览器,刷新index页面,输入点东西,这次就能成功提交了,然后我们在Pycharm中可以看到print语句打印出来的相应数据了。

在这里插入图片描述

10. 返回动态页面
现在,我们收到了用户的数据,但返回给用户的依然是个静态页面。通常我们会根据用户的数据,进行处理后再返回给用户。

先改造views.py文件:

在这里插入图片描述

from django.shortcuts import render
from django.shortcuts import HttpResponse

# Create your views here.

user_list = []


def index(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        print(username, password)
        temp = {'user': username, 'pwd': password}
        user_list.append(temp)
    return render(request, 'index.html', {'data': user_list})

再改造index.HTML文件:

image.png-77.8kB

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
    <h1>用户输入:</h1>
    <form action="/index/" method="post">
        {% csrf_token %}   <!--加入这行 -->
        用户名:<input type="text" name="username" /><br />
        密码:<input type="password" name="password" /><br />
        <input type="submit" value="提交" />
    </form>

    <h1>用户展示:</h1>
        <table border="1">
            <thead>
                <tr>用户名</tr>
                <tr>密码</tr>
            </thead>
            <tbody>
                {% for item in data %}
                <tr>
                    <td>{{ item.user }}</td>
                    <td>{{ item.pwd }}</td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
</body>
</html>

说明:Django采用自己的模板语言,类似jinja2,根据提供的数据,替换掉HTML中的相应部分,详细语法入门后再深入学习。

接下来,重启服务,刷新浏览器,多输入几次。

在这里插入图片描述

可以看到,我们获得了用户实时输入的数据,并将它实时展示在了用户页面上,这是个不错的交互过程。在Pycharm中,我们也能看到每次输入的数据。

11. 使用数据库
流程走到这里,django的MTV框架基本已经浮出水面了,只剩下最后的数据库部分了。

上面我们虽然和用户交互得很好,但并没有保存任何数据,页面一旦关闭,或服务器重启,一切都将回到原始状态。

使用数据库的需求是毫无疑问的,Django通过自带的ORM框架操作数据库,并且原生支持轻量级的sqlite3数据库。下面我们来看一看:

使用数据库前,我们需要注册app:

在这里插入图片描述

不注册它,你的数据库就不知道该给哪个app创建表。

然后我们在settings中,配置数据库相关的参数,如果使用sqlite3,则不需要做任何修改。

image.png-55.9kB

再编辑models.py文件,也就是MTV中的M。

在这里插入图片描述

from django.db import models

# Create your models here.


class UserInfo(models.Model):
    user = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)

这里我们创建了2个字段,分别保存用户的名字和密码。

接下来要在Pycharm的Teminal中通过命令创建数据库的表了。有2条命令,分别是:

python manage.py makemigrations

在这里插入图片描述

这会在login目录中的migrations目录中生成一个0001_initial.py迁移记录文件。

image.png-32.1kB

再输入命令:

python manage.py migrate

运行结果如下:

D:\work\for_test\mysite>python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, login, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying login.0001_initial... OK
  Applying sessions.0001_initial... OK
 

这样,我们就在数据库中将所有app的数据表都创建好了。我们可以看到项目根目录下出现了一个db.sqlite3文件:

在这里插入图片描述

现在,我们来修改views.py中的业务逻辑

![在这里插入图在这里插入图片描述

from django.shortcuts import render
from login import models  # 导入models文件

# Create your views here.


def index(request):

    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        # 将数据保存到数据库
        models.UserInfo.objects.create(user=username, pwd=password)

    # 从数据库中读取所有数据,注意缩进
    user_list = models.UserInfo.objects.all()
    return render(request, 'index.html', {'data': user_list})

重启web服务后,刷新浏览器页面,之后和用户交互的数据都能保存到数据库中。任何时候都可以从数据库中读取数据,展示到页面上,不会因为服务器中途关闭,丢失先前的数据了。

至此,一个要素齐全,主体框架展示清晰的Django项目完成了,其实很简单是不是?

DEBUG 模式
又名开发者模式,默认打开,用来调试和检测Django应用,True = 打开,False=关闭(只有赋予了ALLOWED_HOSTS中IP地址或域名才能正常关闭)。

ALLOWED_HOSTS它是用来设置只有它的列表之中的IP地址或是域名才能访问到本APP。

url传递变量或参数
1、采用在path的第一个参数中加入<参数名>的方式实现参数传递,然后在视图函数中也要加入这个参数名(函数引入名必须和path中的参数名保持一致),可以同时传递多个参数。
2、查询字符串的方式:path参数不做任何设置,只需在视图函数中使用request.GET.get("参数名")或者request.GET["参数名"]获取参数,无需引入任何数据,URL末尾加上?参数名=xxx 实现参数传递。注:request.GET返回的是一个dict(字典)的数据类型

def index(request):
author = request.GET.get('id')  #等同于request.GET['id']
    tmp = '作者是:%s'%author
    return HttpResponse(tmp)

path转化器

Django默认支持以下5个转化器:

  • str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
  • int,匹配正整数,包含0。
  • slug,匹配字母、数字以及横杠、下划线组成的字符串。
  • uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
  • path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)

注册自定义转化器

对于一些复杂或者复用的需要,可以定义自己的转化器。转化器是一个类或接口,它的要求有三点:

  • regex 类属性,字符串类型

  • to_python(self, value) 方法,value是由类属性 regex 所匹配到的字符串,返回具体的Python变量值,以供Django传递到对应的视图函数中。

  • to_url(self, value) 方法,和 to_python 相反,value是一个具体的Python变量值,返回其字符串,通常用于url反向引用。

例子:

class FourDigitYearConverter:  
    regex = '[0-9]{4}'  
    def to_python(self, value):  
        return int(value)  
    def to_url(self, value):  
        return '%04d' % value  

使用register_converter 将其注册到URL配置中:

from django.urls import register_converter, path  
from . import converters, views  
register_converter(converters.FourDigitYearConverter, 'yyyy')  
urlpatterns = [  
    path('articles/2003/', views.special_case_2003),  
    path('articles/<yyyy:year>/', views.year_archive),  

urls模块化:

如果项目变得越来越大。那么url会变得越来越多。如果都放在主urls.py文件中,那么将不太好管理。因此我们可以将每个app自己的urls放到自己的app中进行管理。一般我们会在app中新建一个urls.py文件用来存储所有和这个app相关的子url。
需要注意的地方:

  1. 应该使用include函数包含子urls.py,并且这个urls.py的路径是相对于项目的路径。示例代码如下:
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('book',include('book.urls'))
    ]
    
  2. appurls.py中,所有的url匹配也要放在一个叫做urlpatterns的变量中,否则找不到。
  3. url是会根据主urls.py和app中的urls.py进行拼接的,因此注意不要多加斜杠。

include函数的用法:

  1. include(module,namespace=None):
    • module:子url的模块字符串。
    • namespace:实例命名空间。这个地方需要注意一点。如果指定实例命名空间,那么前提必须要先指定应用命名空间。也就是在子urls.py中添加app_name变量。
  2. include((pattern_list, app_namespace), namespace=None):include函数的第一个参数既可以为一个字符串,也可以为一个元组,如果是元组,那么元组的第一个参数是子urls.py模块的字符串,元组的第二个参数是应用命名空间。也就是说,应用命名空间既可以在子urls.py中通过app_name指定,也可以在include函数中指定。
  3. include(pattern_list):pattern_list是一个列表。这个列表中装的是path或者re_path函数。实例代码如下:
    path('movie/',include([
        path('',views.movie),
        path('list/',views.movie_list),
    ]))
    

url命名:

为什么需要url命名?

因为url是经常变化的。如果在代码中写死可能会经常改代码。给url取个名字,以后使用url的时候就使用他的名字进行反转就可以了,就不需要写死url了。

如何给一个url指定名称?

path函数中,传递一个name参数就可以指定。示例代码如下:

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

应用命名空间:

在多个app之间,有可能产生同名的url。这时候为了避免反转url的时候产生混淆,可以使用应用命名空间,来做区分。定义应用命名空间非常简单,只要在appurls.py中定义一个叫做app_name的变量,来指定这个应用的命名空间即可。示例代码如下:

# 应用命名空间
app_name = 'front'

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

以后在做反转的时候就可以使用应用命名空间:url名称的方式进行反转。示例代码如下:

login_url = reverse('front:login')

应用(app)命名空间和实例命名空间:

一个app,可以创建多个实例。可以使用多个url映射同一个app。所以这就会产生一个问题。以后在做反转的时候,如果使用应用命名空间,那么就会发生混淆。为了避免这个问题。我们可以使用实例命名空间。实例命名空间也是非常简单,只要在include函数中传递一个namespace变量即可。示例代码如下:

urlpatterns = [
    path('',include('front.urls')),
    # 同一个app下有两个实例
    path('cms1/',include('cms.urls',namespace='cms1')),
    path('cms2/',include('cms.urls',namespace='cms2')),
]

以后在做反转的时候,就可以根据实例命名空间来指定具体的url。示例代码如下:

def index(request):
    username = request.GET.get("username")
    if username:
        return HttpResponse('CMS首页')
    else:
        # 获取当前的命名空间
        current_namespace = request.resolver_match.namespace
        return redirect(reverse("%s:login"%current_namespace))

re_path笔记:

  1. re_path和path的作用都是一样的。只不过re_path是在写url的时候可以用正则表达式,功能更加强大。
  2. 写正则表达式都推荐使用原生字符串。也就是以r开头的字符串。
  3. 在正则表达式中定义变量,需要使用圆括号括起来。这个参数是有名字的,那么需要使用?P<参数的名字>。然后在后面添加正则表达式的规则。示例代码如下:
    from django.urls import re_path
    from . import views
    
    urlpatterns = [
        # r"":代表的是原生字符串(raw)
        re_path(r'^$',views.article),
        # /article/list/<year>/
        re_path(r"^list/(?P<year>\d{4})/$",views.article_list),
        re_path(r"^list/(?P<month>\d{2})/$",views.article_list_month)
    ]
    
  4. 如果不是特别要求。直接使用path就够了,省的把代码搞的很麻烦(因为正则表达式其实是非常晦涩的,特别是一些比较复杂的正则表达式,今天写的明天可能就不记得了)。除非是url中确实是需要使用正则表达式来解决才使用re_path

reverse笔记:

  1. 如果在反转url的时候,需要添加参数,那么可以传递kwargs参数到revers函数中。示例代码如下:
    detail_url = reverse('detail',kwargs={"article_id":1,'page':2})
    
  2. 如果想要添加查询字符串的参数,则必须手动的进行拼接。示例代码如下:
    login_url = reverse('login') + "?next=/"
    
    

自定义URL(PATH)转换器笔记:

需求:

实现一个获取文章列表的demo,用户可以根据/articles/文章分类/的方式来获取文章。其中文章分类采用的是分类1+分类2+分类3...的方式拼接的,并且如果只有一个分类,那就不需要加号。示例如下:

# 1. 第一种:获取python分类下的文章
/articles/python/
# 2. 第二种:获取python和django分类下的文章
/articles/python+django/
# 3. 第三种:获取python和django和flask分类下的文章
/articles/python+django+flask/
以此类推...

在“文章分类”参数传到视图函数之前要把这些分类分开来存储到列表中。
比如参数是python+django,那么传到视图函数的时候就要变成['python','django']

以后在使用reverse反转的时候,限制传递“文章分类”的参数应该是一个列表,并且要将这个列表变成python+django的形式。

自定义URL转换器:

之前已经学到过一些django内置的url转换器,包括有int、uuid等。有时候这些内置的url转换器并不能满足我们的需求,因此django给我们提供了一个接口可以让我们自己定义自己的url转换器。

自定义url转换器按照以下五个步骤来走就可以了:

  1. 定义一个类,直接继承自object就可以了。
  2. 在类中定义一个属性regex,这个属性是用来限制url转换器规则的正则表达式。
  3. 实现to_python(self,value)方法,这个方法是将url中的值转换一下,然后传给视图函数的。
  4. 实现to_url(self,value)方法,这个方法是在做url反转的时候,将传进来的参数转换后拼接成一个正确的url。
  5. 将定义好的转换器,使用django.urls.converters.register_converter方法注册到django中。

示例代码如下:

from django.urls import register_converter

class CategoryConverter(object):
    regex = r'\w+|(\w+\+\w+)+'

    def to_python(self,value):
        # python+django+flask
        # ['python','django','flask']
        result = value.split("+")
        return result

    def to_url(self,value):
        # value:['python','django','flask']
        # python+django+flask
        if isinstance(value,list):
            result = "+".join(value)
            return result
        else:
            raise RuntimeError("转换url的时候,分类参数必须为列表!")

register_converter(CategoryConverter,'cate')

URL映射的时候指定默认参数:

使用path或者是re_path的后,在route中都可以包含参数,而有时候想指定默认的参数,这时候可以通过以下方式来完成。示例代码如下:

from django.urls import path

from . import views

urlpatterns = [
    path('blog/', views.page),
    path('blog/page<int:num>/', views.page),
]

# View (in blog/views.py)
def page(request, num=1):
    # Output the appropriate page of blog entries, according to num.
    ...

当在访问blog/的时候,因为没有传递num参数,所以会匹配到第一个url,这时候就执行view.page这个视图函数,而在page函数中,又有num=1这个默认参数。因此这时候就可以不用传递参数。而如果访问blog/1的时候,因为在传递参数的时候传递了num,因此会匹配到第二个url,这时候也会执行views.page,然后把传递进来的参数传给page函数中的num。

©️2020 CSDN 皮肤主题: 技术工厂 设计师:CSDN官方博客 返回首页