文中涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库
Web 服务简单的说就是处理请求,每一个请求就像是一个“顾客”。首先热情地把顾客迎接进来,而后知足用户的个性化需求,最后让顾客心满意足的离开。Django 做为一个 web 框架,可以让开发者有更多的精力和时间去应付复杂多变的需求,而不是把时间花费在招店小2、作饭的厨子、服务员等。那么下面咱们就来看看 Django 的接客之道吧。html
Web 应用的交互过程其实就是 HTTP 请求与响应的过程。不管是在 PC 端仍是移动端,咱们一般使用浏览器来上网,上网流程大体来讲是这样的:python
所以,django 做为一个 Web 框架,它的使命就是处理流程中的第二步。即接收浏览器发来的 HTTP 请求,返回相应的 HTTP 响应。因而引出这么几个问题:git
对于如何处理这些问题,django 有其一套规定的机制。咱们按照 django 的规定,就能开发出所需的功能。github
咱们先以一个最简单的 Hello World 为例来看看 django 处理上述问题的机制是怎么样的。web
首先 django 须要知道当用户访问不一样的网址时,应该如何处理这些不一样的网址(即所说的路由)。django 的作法是把不一样的网址对应的处理函数写在一个 urls.py 文件里,当用户访问某个网址时,django 就去会这个文件里找,若是找到这个网址,就会调用和它绑定在一块儿的处理函数(叫作视图函数)。数据库
下面是具体的作法,首先在 blog 应用的目录下建立一个 urls.py 文件,这时你的目录看起来是这样:django
blog\ __init__.py admin.py apps.py migrations\ 0001_initial.py __init__.py models.py tests.py views.py urls.py
在 blogurls.py 中写入这些代码:浏览器
from django.urls import path from . import views urlpatterns = [ path('', views.index, name='index'), ]
咱们首先从 django.urls 导入了 path
函数,又从当前目录下导入了 views 模块。而后咱们把网址和处理函数的关系写在了 urlpatterns
列表里。服务器
绑定关系的写法是把网址和对应的处理函数做为参数传给 path
函数(第一个参数是网址,第二个参数是处理函数),另外咱们还传递了另一个参数 name
,这个参数的值将做为处理函数 index
的别名,这在之后会用到。app
注意这里咱们的网址其实是一个规则,django 会用这个规则去匹配用户实际输入的网址,若是匹配成功,就会调用其后面的视图函数作相应的处理。
好比说咱们本地开发服务器的域名是 http://127.0.0.1:8000,那么当用户输入网址 http://127.0.0.1:8000 后,django 首先会把协议 http、域名 127.0.0.1 和端口号 8000 去掉,此时只剩下一个空字符串,而 ''
的模式正是匹配一个空字符串,因而两者匹配,django 便会调用其对应的 views.index
函数。
注意在 blogproject 目录下(即 settings.py 所在的目录),本来就有一个 urls.py 文件,这是整个工程项目的 URL 配置文件。而咱们这里新建了一个 urls.py 文件,且位于 blog 应用下。这个文件将用于 blog 应用相关的 URL 配置,这样便于模块化管理。不要把两个文件搞混了。
第二步就是要实际编写咱们的 views.index
视图函数了,按照惯例视图函数定义在 views.py 文件里:
blog/views.py from django.http import HttpResponse def index(request): return HttpResponse("欢迎访问个人博客首页!")
咱们前面说过,Web 服务器的做用就是接收来自用户的 HTTP 请求,根据请求内容做出相应的处理,并把处理结果包装成 HTTP 响应返回给用户。
这个两行的函数体现了这个过程。它首先接受了一个名为 request
的参数,这个 request
就是 django 为咱们封装好的 HTTP 请求,它是类 HttpRequest
的一个实例。而后咱们便直接返回了一个 HTTP 响应给用户,这个 HTTP 响应也是 django 帮咱们封装好的,它是类 HttpResponse
的一个实例,只是咱们给它传了一个自定义的字符串参数。
浏览器接收到这个响应后就会在页面上显示出咱们传递的内容 :欢迎访问个人博客首页!
还差最后一步了,咱们前面创建了一个 urls.py 文件,而且绑定了 URL 和视图函数 index
,可是 django 并不知道。django 匹配 URL 模式是在 blogproject 目录(即 settings.py 文件所在的目录)的 urls.py 下的,因此咱们要把 blog 应用下的 urls.py 文件包含到 blogprojecturls.py 里去,打开这个文件看到以下内容:
blogproject/urls.py """ 一大段注释 """ from django.contrib import admin from django.urls import path urlpatterns = [ path('admin/', admin.site.urls), ]
修改为以下的形式:
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('', include('blog.urls')), ]
咱们这里导入了一个 include
函数,而后利用这个函数把 blog 应用下的 urls.py 文件包含了进来。此外 include 前还有一个 ''
,这是一个空字符串。这里也能够写其它字符串,django 会把这个字符串和后面 include 的 urls.py 文件中的 URL 拼接。好比说若是咱们这里把 ''
改为 'blog/'
,而咱们在 blogurls.py 中写的 URL 是 ''
,即一个空字符串。那么 django 最终匹配的就是 blog/ 加上一个空字符串,即 blog/。
运行 pipenv run python manage.py runserver
打开开发服务器,在浏览器输入开发服务器的地址 http://127.0.0.1:8000/,能够看到 django 返回的内容了。
欢迎访问个人博客首页!
这基本上就上 django 的开发流程了,写好处理 HTTP 请求和返回 HTTP 响应的视图函数,而后把视图函数绑定到相应的 URL 上。
可是等一等!咱们看到在视图函数里返回的是一个 HttpResponse
类的实例,咱们给它传入了一个但愿显示在用户浏览器上的字符串。可是咱们的博客不可能只显示这么一句话,它有可能会显示很长很长的内容。好比咱们发布的博客文章列表,或者一大段的博客文章。咱们不能每次都把这些大段大段的内容传给 HttpResponse
。
django 对这个问题给咱们提供了一个很好的解决方案,叫作模板系统。django 要咱们把大段的文本写到一个文件里,而后 django 本身会去读取这个文件,再把读取到的内容传给 HttpResponse
。让咱们用模板系统来改造一下上面的例子。
首先在咱们的项目根目录(即 manage.py 文件所在目录)下创建一个名为 templates 的文件夹,用来存放咱们的模板。而后在 templates 目录下创建一个名为 blog 的文件夹,用来存放和 blog 应用相关的模板。
固然模板存放在哪里是可有可无的,只要 django 可以找到的就好。可是咱们创建这样的文件夹结构的目的是把不一样应用用到的模板隔离开来,这样方便之后维护。咱们在 templatesblog 目录下创建一个名为 index.html 的文件,此时你的目录结构应该是这样的:
HelloDjango-blog-tutorial\ manage.py ... templates\ blog\ index.html
注意再一次强调 templates 目录位于项目根目录,而 index.html 位于 templatesblog 目录下,而不是 blog 应用下,若是弄错了你可能会获得一个
TemplateDoesNotExist
异常。若是遇到这个异常,请回来检查一下模板目录结构是否正确。
在 templatesblogindex.html 文件里写入下面的代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{ title }}</title> </head> <body> <h1>{{ welcome }}</h1> </body> </html>
这是一个标准的 HTML 文档,只是里面有两个比较奇怪的地方:{{ title }}
,{{ welcome }}
。这是 django 规定的语法。用 {{ }} 包起来的变量叫作模板变量。django 在渲染这个模板的时候会根据咱们传递给模板的变量替换掉这些变量。最终在模板中显示的将会是咱们传递的值。
注意:index.html 必须以 UTF-8 的编码格式保存,且当心不要往里面添加一些特殊字符,不然极有可能获得一个
UnicodeDecodeError
这样的错误。
模板写好了,还得告诉 django 去哪里找模板,在 settings.py 文件里设置一下模板文件所在的路径。在 settings.py 找到 TEMPLATES
选项,它的内容是这样的:
blogproject/settings.py TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.djangoTemplates', 'DIRS': [], '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', ], }, }, ]
其中 DIRS
就是设置模板的路径,在 [] 中写入 os.path.join(BASE_DIR, 'templates')
,即像下面这样:
blogproject/settings.py TEMPLATES = [ { ... 'DIRS': [os.path.join(BASE_DIR, 'templates')], ... }, ]
这里 BASE_DIR
是 settings.py 在配置开头前面定义的变量,记录的是工程根目录 HelloDjango-blog-tutorial 的值。在这个目录下有模板文件所在的目录 templates,因而利用os.path.join
把这两个路径连起来,构成完整的模板路径,django 就知道去这个路径下面找咱们的模板了。
视图函数能够改一下了:
blog/views.py from django.shortcuts import render def index(request): return render(request, 'blog/index.html', context={ 'title': '个人博客首页', 'welcome': '欢迎访问个人博客首页' })
这里咱们再也不是直接把字符串传给 HttpResponse
了,而是调用 django 提供的 render
函数。这个函数根据咱们传入的参数来构造 HttpResponse
。
咱们首先把 HTTP 请求传了进去,而后 render
根据第二个参数的值 blog/index.html 找到这个模板文件并读取模板中的内容。以后 render
根据咱们传入的 context
参数的值把模板中的变量替换为咱们传递的变量的值,{{ title }}
被替换成了 context
字典中 title
对应的值,同理 {{ welcome }}
也被替换成相应的值。
最终,咱们的 HTML 模板中的内容字符串被传递给 HttpResponse
对象并返回给浏览器(django 在 render
函数里隐式地帮咱们完成了这个过程),这样用户的浏览器上便显示出了咱们写的 HTML 模板的内容了。