Django2实战01.md 18.9 KB
Newer Older
1
## Django 2.x实战(01) - 快速上手
2

3
Web开发的早期阶段,开发者需要手动编写每个页面,例如一个新闻门户网站,每天都要修改它的HTML页面,这样随着网站规模和体量的增大,这种方式就变得极度糟糕。为了解决这个问题,开发人员想到了用外部程序来为Web服务器生成动态内容,也就是说HTML页面以及页面中的动态内容不再通过手动编写而是通过程序自动生成。最早的时候,这项技术被称为CGI(公共网关接口),当然随着时间的推移,CGI暴露出的问题也越来越多,例如大量重复的样板代码,总体性能较为低下等,因此在时代呼唤新英雄的背景下,PHP、ASP、JSP这类Web应用开发技术在上世纪90年代中后期如雨后春笋般涌现。通常我们说的Web应用是指通过浏览器来访问网络资源的应用程序,因为浏览器的普及性以及易用性,Web应用使用起来方便简单,免除了安装和更新应用程序带来的麻烦,而且也不用关心用户到底用的是什么操作系统,甚至不用区分是PC端还是移动端。
4 5 6 7 8 9 10

### Web应用机制和术语

下图向我们展示了Web应用的工作流程,其中涉及到的术语如下表所示。

![](./res/web-application.png)

11 12
> 说明:相信有经验的读者会发现,这张图中其实还少了很多东西,例如反向代理服务器、数据库服务器、防火墙等,而且图中的每个节点在实际项目部署时可能是一组节点组成的集群。当然,如果你对这些没有什么概念也不要紧,后续的课程中我们会为大家进行讲解。

13 14 15 16 17 18
| 术语          | 解释                                                         |
| ------------- | ------------------------------------------------------------ |
| **URL/URI**   | 统一资源定位符/统一资源标识符,网络资源的唯一标识            |
| **域名**      | 与Web服务器地址对应的一个易于记忆的字符串名字                |
| **DNS**       | 域名解析服务,可以将域名转换成对应的IP地址                   |
| **IP地址**    | 网络上的主机的身份标识,通过IP地址可以区分不同的主机         |
19
| **HTTP**      | 超文本传输协议,构建在TCP之上的应用级协议,万维网数据通信的基础 |
20 21 22 23
| **反向代理**  | 代理客户端向服务器发出请求,然后将服务器返回的资源返回给客户端 |
| **Web服务器** | 接受HTTP请求,然后返回HTML文件、纯文本文件、图像等资源给请求者 |
| **Nginx**     | 高性能的Web服务器,也可以用作[反向代理](https://zh.wikipedia.org/wiki/%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86)[负载均衡](https://zh.wikipedia.org/wiki/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1) 和 [HTTP缓存](https://zh.wikipedia.org/wiki/HTTP%E7%BC%93%E5%AD%98) |

24
#### HTTP协议
25

26
这里我们稍微费一些笔墨来谈谈上面提到的HTTP。HTTP(超文本传输协议)是构建于TCP(传输控制协议)之上应用级协议,它利用了TCP提供的可靠的传输服务实现了Web应用中的数据交换。按照维基百科上的介绍,设计HTTP最初的目的是为了提供一种发布和接收[HTML](https://zh.wikipedia.org/wiki/HTML)页面的方法,也就是说这个协议是浏览器和Web服务器之间传输的数据的载体。关于这个协议的详细信息以及目前的发展状况,大家可以阅读阮一峰老师的[《HTTP 协议入门》](http://www.ruanyifeng.com/blog/2016/08/http.html)[《互联网协议入门》](http://www.ruanyifeng.com/blog/2012/05/internet_protocol_suite_part_i.html)系列以及[《图解HTTPS协议》](http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html)进行了解,下图是我于2009年9月10日凌晨4点在四川省网络通信技术重点实验室用开源协议分析工具Ethereal(抓包工具WireShark的前身)截取的访问百度首页时的HTTP请求和响应的报文(协议数据),由于Ethereal截取的是经过网络适配器的数据,因此可以清晰的看到从物理链路层到应用层的协议数据。
27

28 29 30 31 32 33 34 35 36
HTTP请求(请求行+请求头+空行+[消息体]):

![](./res/http-request.png)

HTTP响应(响应行+响应头+空行+消息体):

![](./res/http-response.png)

>  说明:但愿这两张如同泛黄的照片般的截图帮助你大概的了解到HTTP是一个怎样的协议。
37 38 39

### Django概述

40 41
Python的Web框架有上百个,比它的关键字还要多。所谓Web框架,就是用于开发Web服务器端应用的基础设施(通常指封装好的模块和一系列的工具)。事实上,即便没有Web框架,我们仍然可以通过socket或[CGI](https://zh.wikipedia.org/wiki/%E9%80%9A%E7%94%A8%E7%BD%91%E5%85%B3%E6%8E%A5%E5%8F%A3)来开发Web服务器端应用,但是这样做的成本和代价在实际开发中通常是不能接受的。通过Web框架,我们可以化繁为简,同时降低创建、更新、扩展应用程序的工作量。Python的Web框架中比较有名的有:Flask、Django、Tornado、Pyramid、Bottle、Web2py、web.py等。

42 43 44 45 46 47 48 49 50 51 52 53 54
在基于Python的Web框架中,Django是所有重量级选手中最有代表性的一位,开发者可以基于Django快速的开发可靠的Web应用程序,因为它减少了Web开发中不必要的开销,对常用的设计和开发模式进行了封装,并对MVC架构提供了支持(MTV)。许多成功的网站和App都是基于Django框架构建的,国内比较有代表性的网站包括:知乎、豆瓣网、果壳网、搜狐闪电邮箱、101围棋网、海报时尚网、背书吧、堆糖、手机搜狐网、咕咚、爱福窝、果库等。

![](./res/mvc.png)

Django诞生于2003年,它是一个在真正的应用中成长起来的项目,由劳伦斯出版集团旗下在线新闻网站的内容管理系统(CMS)研发团队编写(主要是Adrian Holovaty和Simon Willison),以比利时的吉普赛爵士吉他手Django Reinhardt来命名,在2005年夏天作为开源框架发布。使用Django能用很短的时间构建出功能完备的网站,因为它代替程序员完成了所有乏味和重复的劳动,剩下真正有意义的核心业务给程序员,这一点就是对DRY(Don't Repeat Yourself)理念的最好践行。

### 快速上手

#### 准备工作

1. 检查Python环境:Django 1.11需要Python 2.7或Python 3.4以上的版本;Django 2.0需要Python 3.4以上的版本。

   ```Shell
55
   
56 57 58 59
   $ python3 --version
   ```

   ```Shell
60
   
61 62 63 64 65 66
   $ python3
   >>> import sys
   >>> sys.version
   >>> sys.version_info
   ```

67
2. 创建项目文件夹并切换到该目录,例如我们要实例一个OA(办公自动化)项目。
68 69

   ```Shell
70
   
71 72
   $ mkdir oa
   $ cd oa
73 74 75 76 77
   ```

3. 创建并激活虚拟环境。

   ```Shell
78
   
79 80 81 82 83 84 85 86
   $ python3 -m venv venv
   $ source venv/bin/activate
   ```
   > 注意:Windows系统下是执行`venv/Scripts/activate.bat`批处理文件。

4. 更新包管理工具pip。

   ```Shell
87
   
88 89
   (venv)$ python -m pip install --upgrade pip
   ```
90
   > 注意:请注意终端提示符发生的变化,前面的`(venv)`说明我们已经进入虚拟环境,而虚拟环境下的python和pip已经是Python 3的解释器和包管理工具了。
91 92 93 94

5. 安装Django。

   ```Shell
95
   
96 97 98
   (venv)$ pip install django
   ```

99
   或指定版本号来安装对应的Django的版本。
100 101

   ```Shell
102
   
103 104 105 106 107 108
   (venv)$ pip install django==1.11
   ```

6. 检查Django的版本。

   ```Shell
109
   
110 111 112 113 114
   (venv)$ python -m django --version
   (venv)$ django-admin --version
   ```

   ```Shell
115
   
116 117 118 119
   (venv)$ python
   >>> import django
   >>> django.get_version()
   ```
120 121 122 123 124 125 126 127 128
   下图展示了Django版本和Python版本的对应关系,在我们的项目中我们选择了最新的Django 2.0的版本。

   | Django版本 | Python版本              |
   | ---------- | ----------------------- |
   | 1.8        | 2.7、3.2、3.3、3.4、3.5 |
   | 1.9、1.10  | 2.7、3.4、3.5           |
   | 1.11       | 2.7、3.4、3.5、3.6      |
   | 2.0        | 3.4、3.5、3.6           |

129
   > 说明:在写这篇文章时Django 2.1版本尚未正式发布,因此我们的教程使用了2.0.5版本。
130

131
7. 使用`django-admin`创建项目,项目命名为oa。
132 133

   ```Shell
134
   
135
   (venv)$ django-admin startproject oa .
136 137
   ```

138
   > 注意:上面的命令最后的那个点,它表示在当前路径下创建项目。
139 140 141 142

   执行上面的命令后看看生成的文件和文件夹,它们的作用如下所示:

   - `manage.py`: 一个让你用各种方式管理 Django 项目的命令行工具。
143 144 145 146
   - `oa/__init__.py`:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。
   - `oa/settings.py`:Django 项目的配置文件。
   - `oa/urls.py`:Django 项目的 URL 声明,就像你网站的“目录”。
   - `oa/wsgi.py`:作为你的项目的运行在 WSGI 兼容的Web服务器上的入口。
147 148 149 150

8. 启动服务器运行项目。

   ```Shell
151
   
152 153 154
   (venv)$ python manage.py runserver
   ```

155 156 157 158 159
   在浏览器中输入<http://127.0.0.1:8000>访问我们的服务器,效果如下图所示。

   ![](./res/django-index-1.png)


160 161 162 163 164 165
   > 说明1:刚刚启动的是Django自带的用于开发和测试的服务器,它是一个用纯Python编写的轻量级Web服务器,但它并不是真正意义上的生产级别的服务器,千万不要将这个服务器用于和生产环境相关的任何地方。

   > 说明2:用于开发的服务器在需要的情况下会对每一次的访问请求重新载入一遍Python代码。所以你不需要为了让修改的代码生效而频繁的重新启动服务器。然而,一些动作,比如添加新文件,将不会触发自动重新加载,这时你得自己手动重启服务器。

   > 说明3:可以通过`python manage.py help`命令查看可用命令列表;在启动服务器时,也可以通过`python manage.py runserver 1.2.3.4:56789`来指定绑定的IP地址和端口。

166 167 168 169 170
   > 说明4:可以通过Ctrl+C来终止服务器的运行。

9. 接下来我们进入项目目录oa并修改配置文件settings.py,Django是一个支持国际化和本地化的框架,因此刚才我们看到的默认首页也是支持国际化的,我们将默认语言修改为中文,时区设置为东八区。

   ```Shell
171
   
172 173 174 175 176
   (venv)$ cd oa
   (venv)$ vim settings.py
   ```

   ```Python
177
   
178 179 180 181 182 183 184 185 186 187 188 189 190
   # 此处省略上面的内容
   
   # 设置语言代码
   LANGUAGE_CODE = 'zh-hans'
   # 设置时区
   TIME_ZONE = 'Asia/Chongqing'
   
   # 此处省略下面的内容
   ```

10. 回到manage.py所在的目录,刷新刚才的页面。

  ```Shell
191
  
192 193 194 195 196 197 198 199 200
  (venv)$ cd ..
  (venv)$ python manage.py runserver
  ```

  ![](./res/django-index-2.png)

#### 动态页面

1. 创建名为hrs(人力资源系统)的应用(注:一个项目可以包含多个应用)。
201 202

   ```Shell
203
   
204 205 206
   (venv)$ python manage.py startapp hrs
   ```

207
   执行上面的命令会在当前路径下创建hrs目录,其目录结构如下所示:
208 209 210 211

   - `__init__.py`:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。
   - `admin.py`:可以用来注册模型,让Django自动创建管理界面。
   -  `apps.py`:当前应用的配置。
212 213
   - `migrations`:存放与模型有关的数据库迁移信息。
     - `__init__.py`:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。
214
   - `models.py`:存放应用的数据模型,即实体类及其之间的关系(MVC/MVT中的M)。
215
   - `tests.py`:包含测试应用各项功能的测试类和测试函数。
216
   - `views.py`:处理请求并返回响应的函数(MVC中的C,MVT中的V)。
217

218
2. 进入应用目录修改视图文件views.py。
219 220

   ```Shell
221
   
222 223 224 225 226
   (venv)$ cd hrs
   (venv)$ vim views.py
   ```

   ```Python
227
   
228 229 230 231 232 233 234 235
   from django.http import HttpResponse
   
   
   def index(request):
       return HttpResponse('<h1>Hello, Django!</h1>')
   
   ```

236
3. 在应用目录创建一个urls.py文件并映射URL。
237 238

   ```Shell
239
   
240 241 242 243 244
   (venv)$ touch urls.py
   (venv)$ vim urls.py
   ```

   ```Python
245
   
246 247 248 249 250 251 252 253
   from django.urls import path
   
   from hrs import views
   
   urlpatterns = [
       path('', views.index, name='index'),
   ]
   ```
254
   > 说明:上面使用的path函数是Django 2.x中新添加的函数,除此之外还有re_path是支持正则表达式的URL映射函数;Django 1.x中是用url函数来设定URL映射。
255

256
4. 切换到项目目录,修改该目录下的urls.py文件,对应用中设定的URL进行合并。
257 258

   ```Shell
259
   
260
   (venv) $ cd ..
261
   (venv) $ cd oa
262 263 264 265
   (venv) $ vim urls.py
   ```

   ```Python
266
   
267 268 269 270 271 272 273 274 275
   from django.contrib import admin
   from django.urls import path, include
   
   urlpatterns = [
       path('admin/', admin.site.urls),
       path('hrs/', include('hrs.urls')),
   ]
   ```

276
5. 启动项目并访问应用。
277 278

   ```Shell
279
   
280
   (venv)$ cd ..
281 282 283 284 285 286 287
   (venv)$ python manage.py runserver
   ```

   在浏览器中访问<http://localhost:8000/hrs>

   > 说明:如果想实现远程访问,需要先确认防火墙是否已经打开了8000端口,而且需要在配置文件settings.py中修改ALLOWED_HOSTS的设置,添加一个'*'表示允许所有的客户端访问Web应用。

288
6. 修改views.py生成动态内容。
289 290

   ```Shell
291
   
292 293 294 295 296
   (venv)$ cd hrs
   (venv)$ vim views.py
   ```

   ```Python
297
   
298 299
   from io import StringIO
   
300 301 302 303 304 305 306
   from django.http import HttpResponse
   
   depts_list = [
       {'no': 10, 'name': '财务部', 'location': '北京'},
       {'no': 20, 'name': '研发部', 'location': '成都'},
       {'no': 30, 'name': '销售部', 'location': '上海'},
   ]
307 308 309 310 311 312 313 314 315 316
   
   
   def index(request):
       output = StringIO()
       output.write('<html>\n')
       output.write('<head>\n')
       output.write('\t<meta charset="utf-8">\n')
       output.write('\t<title>首页</title>')
       output.write('</head>\n')
       output.write('<body>\n')
317
       output.write('\t<h1>部门信息</h1>\n')
318
       output.write('\t<hr>\n')
319 320 321 322 323 324 325 326 327 328 329 330 331
       output.write('\t<table>\n')
       output.write('\t\t<tr>\n')
       output.write('\t\t\t<th>部门编号</th>\n')
       output.write('\t\t\t<th>部门名称</th>\n')
       output.write('\t\t\t<th>所在地</th>\n')
       output.write('\t\t</tr>\n')
       for dept in depts_list:
           output.write('\t\t<tr>\n')
           output.write(f'\t\t\t<td>{dept["no"]}</td>\n')
           output.write(f'\t\t\t<td>{dept["name"]}</td>\n')
           output.write(f'\t\t\t<td>{dept["location"]}</td>\n')
           output.write('\t\t</tr>\n')
       output.write('\t</table>\n')
332 333 334 335 336
       output.write('</body>\n')
       output.write('</html>\n')
       return HttpResponse(output.getvalue())
   ```

337 338 339 340 341 342 343
7. 再次使用下面的命令来启动服务器并查看程序的运行结果。

    ```Shell
    
    (venv)$ cd ..
    (venv)$ python manage.py runserver
    ```
344
    ![](./res/runserver01.png)
345

346 347
#### 使用视图模板

348
上面通过拼接HTML代码的方式生成动态视图的做法在实际开发中是无能接受的,这一点我相信是不言而喻的。为了解决这个问题,我们可以提前准备一个模板页,所谓模板页就是一个带占位符的HTML页面,当我们将程序中获得的数据替换掉页面中的占位符时,一个动态页面就产生了。
349

350
我们可以用Django框架中template模块的Template类创建模板对象,通过模板对象的render方法实现对模板的渲染。所谓的渲染就是用数据替换掉模板页中的占位符,Django框架通过shortcuts模块的快捷函数render简化了渲染模板的操作,具体的用法如下所示。
351 352 353 354

1. 先回到manage.py文件所在的目录创建一个templates文件夹。

   ```Shell
355
   
356 357 358 359 360 361 362 363
   (venv)$ cd ..
   (venv)$ mkdir templates
   (venv)$ cd templates
   ```

2. 创建模板页index.html。

   ```Shell
364
   
365 366 367 368
   (venv)$ touch index.html
   (venv)$ vim index.html
   ```
   ```HTML
369
   
370 371 372 373 374 375 376
   <!DOCTYPE html>
   <html lang="en">
   <head>
   	<meta charset="UTF-8">
   	<title>首页</title>
   </head>
   <body>
377
   	<h1>部门信息</h1>
378
   	<hr>
379 380 381 382 383 384 385 386 387 388 389 390 391 392
   	<table>
   	    <tr>
               <th>部门编号</th>
               <th>部门名称</th>
               <th>所在地</th>
           </tr>
           {% for dept in depts_list %}
           <tr>
               <td>{{ dept.no }}</td>
               <td>{{ dept.name }}</td>
               <td>{{ dept.location }}</td>
           <tr>
           {% endfor %}
       </table>
393 394 395
   </body>
   </html>
   ```
396
   注意在模板页中我们使用了`{{ greeting }}`这样的模板占位符语法,也使用了`{% for %}`这样的模板指令,如果对此不熟悉并不要紧,我们会在后续的内容中进一步的讲解模板的用法。
397 398 399 400

3. 回到应用目录,修改views.py文件。

   ```Shell
401
   
402 403 404 405 406 407
   (venv)$ cd ..
   (venv)$ cd hrs
   (venv)$ vim views.py
   ```

   ```Python
408
   
409 410
   from django.shortcuts import render
   
411 412 413 414 415
   depts_list = [
       {'no': 10, 'name': '财务部', 'location': '北京'},
       {'no': 20, 'name': '研发部', 'location': '成都'},
       {'no': 30, 'name': '销售部', 'location': '上海'},
   ]
416 417
   
   
418 419
   def index(request):
       return render(request, 'index.html', {'depts_list': depts_list})
420 421
   ```

422
   到此为止,我们还没有办法让views.py中的render函数找到模板文件index.html,为此我们需要修改settings.py文件,配置模板文件所在的路径。
423 424 425 426

4. 切换到项目目录修改settings.py文件。

   ```Shell
427
   
428
   (venv)$ cd ..
429
   (venv)$ cd oa
430 431 432 433
   (venv)$ vim settings.py
   ```

   ```Python
434
   
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
   # 此处省略上面的内容
   
   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',
               ],
           },
       },
   ]
   
   # 此处省略下面的内容
   ```

456
5. 重新运行项目并查看结果。
457 458

   ```Shell
459
   
460
   (venv)$ cd ..
461 462 463
   (venv)$ python manage.py runserver
   ```

464 465 466 467 468

### 总结

至此,我们已经利用Django框架完成了一个非常小的Web应用,虽然它并没有任何的实际价值,但是我们需要通过这个项目了解到Django框架的使用方式。当然,如果使用PyCharm的Professional版本,也可以通过PyCharm的创建项目的选项直接创建Django项目,使用PyCharm的好处在于编写代码时可以获得代码提示、错误修复、自动导入等功能,从而提升开发效率,但是代价是需要支付对应的费用才能使用专业版的PyCharm,社区版的PyCharm中并未包含对Web框架的支持。

骆昊的技术专栏's avatar
骆昊的技术专栏 已提交
469
此外,学习Django最好的资料肯定是它的[官方文档](https://docs.djangoproject.com/zh-hans/2.0/),除此之外图灵社区最近出版的[《Django基础教程》](http://www.ituring.com.cn/book/2630)也是非常适合初学者的读物。