本教程内容已过时,更新版教程请访问: Django 博客开发入门教程。
这是 Django 博客教程的第 6 篇,在阅读此篇教程以前,请确保你已阅读 Django 博客教程的前 5 篇:
1. Django 博客教程:前言
2. 搭建开发环境
3. 建立我们的 django 博客应用
4. 创建 django 博客的数据库模型
5. 让 django 完成翻译——迁移数据库模型
django 是如何处理 http 请求的
Web 应用的交互过程其实就是 http 请求与响应的过程。无论是在 PC 端还是移动端,我们通常使用浏览器来上网,我们的上网流程大致来说是这样的:
- 我们打开浏览器,在地址栏输入我们想访问的网址,比如 www.djangoproject.com(当然你也可能从收藏夹里直接打开网站,但本质上都是一样的。)
- 浏览器知道我们想要访问那个网址后,它在后台帮我们做了很多事情,主要就是把我们的访问意图包装成一个 http 请求,发给我们想要访问的网址所对应的服务器。通俗点说就是浏览器帮我们通知网站的服务器,说有人来访问你啦,访问的请求都写在 http 里了,你按照要求处理后告诉我,我再帮你回应他!
- 网站服务器处理了 http 请求,然后生成一段 http 响应给浏览器,浏览器解读这个响应,把相关的内容在浏览器里显示出来,于是我们就看到了网站的内容。比如你访问了社区主页 www.pythonzh.cn,服务器接收到这个请求后他就知道用户访问的是首页,首页显示的是全部帖子列表,于是它从数据库里把帖子数据取出来,生成一个写着这些数据的 html 文档,包装到 http 响应里发给浏览器,浏览器解读这个响应,把 html 文档显示出来,我们就看到了帖子列表的内容。
因此,django 作为一个 web 框架,它的使命就是处理流程中的第二步,接收浏览器发来的 http 请求,返回相应的 http 响应。于是引出这么几个问题:
- django 如何接收 http 请求?
- django 如何处理这个 http 请求?
- django 如何生成 http 响应?
对于如何处理这些问题,django 有其自身的一套规定的机制。我们按照 django 的规定,就能开发出我们所需的功能。我们先以一个最简单的 hello world 为例来看看 django 处理上述问题的机制是怎么样的。
首先 django 需要知道当用户访问不同的网址时,应该如何处理不同的网址。django 的做法是把不同的网址对应的处理函数写在一个 urls.py 文件里,当用户访问某个网址时,django 就去会这个文件里找,如果找到这个网址,就会调用和它绑定在一起的处理函数(叫做视图函数),下面是具体的做法,首先在 blog 应用的目录下创建一个 urls.py 文件,这时你的目录看起来是这样:
blog/
__init__.py
admin.py
apps.py
migrations/
0001_initial.py
__init__.py
models.py
tests.py
views.py
urls.py # 新建的文件
在 urls.py 中写入这些代码:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index, name='index'),
]
我们首先从 django.conf.urls 导入了 url 函数,又从当前目录下导入了 views 模块。然后我们把网址和处理函数的关系写在了 urlpatterns 列表里。绑定关系的写法是把网址和对应的处理函数作为参数传给 url 函数(第一个参数是网址,第二个参数是处理函数),另外我们还传递了另外一个参数 name,这个参数的值将作为处理函数 index 的别名,这在以后会用到。
注意这里我们的网址是用正则表达式写的,django 会用这个正则表达式去匹配用户实际输入的网址,如果匹配成功,就会调用其后面的视图函数做相应的处理。比如说我们本地开发服务器的域名是 127.0.0.1:8000,那么当用户输入网址:127.0.0.1:8000 后,django 首先会把域名(即 127.0.0.1)和端口号(8000)去掉,此时只剩下一个空字符串,而 r'^$' 的模式正是匹配一个空字符串(这个正则表达式的意思是以空字符串开头且以空字符串结尾),于是二者匹配,django 便会调用其对应的 views.index 函数。
第二步就是要实际编写我们的 views.index 视图函数了,按照惯例视图函数定义在 views.py 文件里:
blog/views.py
from django.http import HttpResponse
def index(request):
return HttpResponse("欢迎访问我的博客首页!")
我们前面说过,Web 服务器的作用就是接收来自用户的 http 请求,根据请求内容作出相应的处理,并把处理结果包装成 http 响应返回给用户。这个两行的函数体现了这个过程。它首先接受了一个名为 request 的参数,这个 request 就是 django 为我们封装好的 http 请求,它是类 HttpResponse 的一个实例。然后我们便直接返回了一个 http 响应给用户,这个 http 响应也是 django 帮我们封装好的,它是类 HttpResponse 的一个实例,只是我们给它传了一个自定义的字符串,用户接受到这个响应后就会在浏览器显示出我们传递的内容:“欢迎访问我的博客首页!”
还差最后一步了,我们前面建立了一个 urls.py 文件,并且绑定了 URL 和视图函数 index,但是 django 并不知道。django 匹配 url 是在 blogproject 的 urls.py 下的,所以我们要把我们自己写的 urls.py 文件包含到这个文件里去,打开这个文件看到如下内容:
"""
一大段注释
"""
from django.conf.urls import url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
]
修改成如下的形式:
- from django.conf.urls import url
+ from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
+ url(r'', include('blog.urls')),
]
这里 - 表示删掉这一行,+ 表示添加这一行。我们这里导入了一个 include 函数,然后利用这个函数把 blog 应用下的 urls.py 包含了进来。此外 include 前还有一个 r'',这是一个空字符串,这里也可以写其他字符串,django 会把这个字符串和后面 include 的 urls.py 文件中的 url 拼接。假如我们这里把 r'' 改成 r'blog/',而我们在 blog.urls 中写的url 是 r'^$',一个空字符串,那么 django 最终匹配的就是 blog/ 加上一个空字符串,即 blog/。
运行 python manage.py runserver 打开开发服务器,在浏览器输入开发服务器的地址 http://127.0.0.1:8000/,可以看到我们返回的内容了:欢迎访问我的博客首页!
这基本上就上 django 的开发流程了,写好处理 http 请求和返回 http 响应的视图函数,然后把视图函数绑定到相应的 URL 上。但是等一等!我们看到在我们的视图函数里返回的是一个 HttpResponse 类的实例,我们给他传入了一个我们希望显示在用户浏览器上的字符串。但是我们的博客不可能只显示这么一句话,它有可能会显示很长很长的内容,比如我们发布的博客文章列表,或者一大段的博客文章,我们不能每次都把这些大段大段的内容传给 HttpResponse 。于是 django 对这个问题给我们提供了一个好的方法,叫做模板系统。django 要我们把大段的文本写到一个文件里,然后 django 自己会去读取这个文件,django 再把读取到的内容传给 HttpResponse。我们用模板系统来改造一下上面的例子。首先在我们的项目根目录下建立一个名为 templates 的文件夹,用来存放我们的模板。然后再建立一个名为 blog 的文件夹,用来存放和 blog 应用相关的模板。当然模板存放在哪里是无关紧要的,只要 django 能够找到的就好。但是我们建立这样的文件夹结构的目的是把不同应用用到的模板隔离开来,这样方便以后维护,养成良好的习惯。然后我们在 blog 目录下建立一个名为 index.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 在读取这个模板的时候回根据我们传来值替换这些变量。最终在模板中显示的将会是我们传递的值。
模板写好了,还得告诉 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 前面定义的变量,记录的是工程根目录 blogproject 的值,在这个目录下有我们的模板目录 templates,于是利用os.path.join 把这两个路径连起来,构成完整的模板路径,django 就知道去这个路径下面找我们的模板了。
视图函数可以改一下了:
blog/views.py
from django.http import HttpResponse
from django.shortcuts import render
def index(request):
return render(request, 'blog/index.html', context={
'title': '我的博客首页',
'welcome': '欢迎访问我的博客首页'
})
这里我们在是直接把字符串传给 HttpResponse 了,而是调用 django 提供了 render 函数,这个函数根据我们传入的参数来构造 HttpResponse。我们首先把 http 请求传了进去,然后它根据第二个参数的值 blog/index.html 找到我们的模板,然后读取模板中的内容,并且根据我们传入的 context 把模板中的变量替换为我们传递的值,{{ title }} 被替换成了 context 字典中 title 对应的值,同理 {{ welcome }} 也被替换成相应的值。最终,我们的 html 模板中的内容字符串被传递给 HttpResponse 对象并返回给浏览器,这样用户的浏览器上便显示出了我们写的 html 模板的内容。
网友评论
from django.http import HttpResponse
def index(request):
return render(request,'blog/index.html',context={
'title': '我的博客首页',
'welocme': 'Hello World'
})
视图函数
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
</head>
<body>
<h1>{{ welcome }}</h1>
</body>
</html> 这是我模板代码 但是我在浏览器中打开显示源码时内容是这个
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>我的博客首页</title>
</head>
<body>
<h1></h1>
</body>
</html>
但是实际上应该在根目录下面建立一个templates文件夹,然后在templates文件夹里面新建的blog里面创建index.html
TemplateDoesNotExist at /
blog/index.html
Request Method: GET
Request URL: http://127.0.0.1:8000/
Django Version: 1.10.6
Exception Type: TemplateDoesNotExist
Exception Value:
blog/index.html
Exception Location: c:\Python34\Scripts\venv\lib\site-packages\django\template\loader.py in get_template, line 25
Python Executable: c:\Python34\Scripts\venv\Scripts\python.exe
Python Version: 3.4.2
Python Path:
['D:\\PythonWorkspace\\blogproject',
'C:\\windows\\SYSTEM32\\python34.zip',
'c:\\Python34\\Scripts\\venv\\DLLs',
'c:\\Python34\\Scripts\\venv\\lib',
'c:\\Python34\\Scripts\\venv\\Scripts',
'C:\\Python34\\Lib',
'C:\\Python34\\DLLs',
'c:\\Python34\\Scripts\\venv',
'c:\\Python34\\Scripts\\venv\\lib\\site-packages']
Server time: 星期二, 11 四月 2017 19:00:24 +0800
在网上找了更改方法,但都不行,能帮忙看一下是哪里出错了吗?
UnicodeDecodeError at /
'utf-8' codec can't decode byte 0xff in position 0: invalid start byte
我的python是3.4.2版本,不知道是不是版本的问题?
2. 正确设置了: 'DIRS': [os.path.join(BASE_DIR, 'templates')],
3. 视图函数正确渲染了模板:
return render(request, 'blog/index.html', context={
'title': '我的博客首页',
'welcome': '欢迎访问我的博客首页'
})
检查一下是哪一步出了问题。
from django.shortcuts import render
# Create your views here.
#blog/views.py
#from django.http import HttpResponse
#def index(request):
# return HttpResponse("欢迎访问我的博客首页!")
from django.http import HttpResponse
#from django.shotcuts import render
def index(request):
return render(request, 'blog/index.html', context={
'title': '我的博客首页',
'welcome': '欢迎访问我的博客首页'
})
原先的HTTPResponse大写了,还有就是from django.shotcuts import render
中的shotcuts不对,应该是shortcuts,我也不知道我说对没有,但是我改了之后能够运行