Django 是 Python 社区的两大最受欢迎的 Web 框架之一(另外一个是 Flask)。凭借功能强大的脚手架和诸多开箱即用的组件,用 Django 搭建 Web 应用快速而又省力。然而,也正是由于过于强大,想要驾驭它须要花费很多的力气。本文将经过实现一个新闻发布网站带你快速熟悉 Django 框架,让你可以骑上这匹快马,在 Web 开发的战场上尽情驰骋。html
提示前端
这篇文章写做时用的是 Django 2.x 版本,发表时已经推出了 3.x 版本。不过通过笔者测试,这篇文章中的代码对于 2.x 和 3.x 都适用哦!python
Django 由 Adrian Holovaty 和 Simon Willison 在 2003 年的秋天写成,并在 2005 年正式发布。他们俩当时为一个新闻报社制做网站,对快速开发有着比较高的需求,而且但愿可以在开发的同时也可以让非技术人员为网站添加内容。因而这也使得 Django 具有了两项鲜明的特色:数据库
Django 的名字取自吉他手 Django Reinhardt,发音为 JANG-goh(谐音“尖狗”),但实际上 Django 的吉祥物是一只长着翅膀的小马。django
在这篇教程中,咱们也将向 Django 的起源致敬——手把手带你开发一个新闻发布网站,而且能够从后台管理系统中添加新闻,展现到网站首页上。浏览器
本教程假定你已经知道了:bash
读完这篇教程后,你将掌握 Django MTV 框架的精髓:服务器
虽然 Django 还有不少知识点,可是理解了 MTV,后面的知识点学习起来也就轻松多啦。session
本文假定你已经安装好了 Python 3 和 pip,那么能够直接用 pip 安装 Django:app
pip install django
复制代码
直接用 pip 在全局安装 Django 的确不是一个很好的作法,用虚拟环境更符合最佳实践。为了减小初学者们的认知负担,在这里就简化了安装过程。熟悉 pipenv 等虚拟环境工具的老司机固然能够自行使用哈。
安装好 Django 后,咱们用 Django 自带的脚手架工具 django-admin 建立项目:
django-admin startproject django_news
cd django_news
复制代码
生成的项目骨架及每一个文件的做用以下所示:
django_news
├── django_news // 项目全局文件目录
│ ├── __init__.py
│ ├── settings.py // 全局配置
│ ├── urls.py // 全局路由
│ └── wsgi.py // WSGI服务接口(暂时不用纠结这个是神马)
└── manage.py // 项目管理脚本
复制代码
咱们使用 manage.py 来运行开发服务器(Development Server):
python manage.py runserver
复制代码
提示
细心的你会发现出现了一行鲜红色的提示:You have 17 unapplied migration(s)...(省略 n 个字符)。不用担忧,咱们会在接下来的步骤中详细讲解前因后果。
按照提示,咱们经过浏览器访问 localhost:8000,能够看到欢迎界面:
提示
Django 开发服务器能够保持开启,而且后面修改代码会自动从新加载,很是方便。后面运行其余命令时,再打开一个终端(命令行)便可。
一切准备就绪,缰绳已在你手中!
在上一节中咱们讲到,Django 是一个高度模块化的框架。具体而言,一个 Django 应用由多个子应用组成,咱们通常称之为 App(注意不是咱们常说的移动应用 APP,而是 Application 的简写),以下图所示。
Django App 通常分为三大类(根据来源):
全部的 Django 应用都在 django_news/settings.py 的 INSTALLED_APPS
列表中定义:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
复制代码
话很少说,让咱们来建立第一个自定义 App,名称为 news:
python manage.py startapp news
复制代码
生成的 news 应用文件夹结构以下所示:
news // news 应用目录
├── __init__.py // 初始化模块
├── admin.py // 后台管理配置
├── apps.py // 应用配置
├── migrations // 数据库迁移文件目录
│ └── __init__.py // 数据库迁移初始化模块
├── models.py // 数据模型
├── tests.py // 单元测试
└── views.py // 视图
复制代码
这个子目录里面乍一看好多文件啊!这是由于 Django 始终坚持解耦的原则——尽可能减小代码之间的耦合,把不相关的代码拆成多个模块,让同一个模块具备内聚性。相信我,等到后面慢慢熟悉以后,你会对每个模块都了如指掌的。
实际上,每一个 Django App 的组织结构符合 Django 的 MTV 法则——Model(模型)+ Template(模板)+ View(视图)。MTV 与你们比较熟悉的 MVC 在思想上很是类似,可是命名有比较大的出入,以下表所示:
你们熟知的 View,在 Django 里面表明的是业务逻辑,也就是 MVC 中的控制器哦!
最后,咱们在 settings.py 中将 news 应用加入 INSTALLED_APPS
中:
# ...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'news',
]
复制代码
至此,咱们已经建立了第一个 Django 应用!可是如今这个应用尚未任何内容,咱们接下来将逐步完善这个应用。
也许你已经注意到,经过访问 localhost:8000/admin 已经能够访问后台管理系统了(虽然会跳转到登陆界面)。接下来,咱们也但愿可以访问到刚才建立的 news 应用。所以,这一步中咱们将:
Django 的路由系统是由全局路由和子应用路由组成。简单来讲,根据用户输入的 URL,全局路由表进行匹配并选择正确的子应用路由,再由所选择的子应用路由匹配并选择正确的视图(View)。整个流程以下图所示:
例如,用户访问 example.com/apple/buy,而后全局路由根据 /apple/buy 先选择 apple 的路由表,再从 apple 路由表中根据 /buy 选择 /buy 路由,而后执行 /buy 对应的 BuyView 视图,返回给用户结果。
对视图访问的流程大体了解以后,咱们就能够开始动手了。首先打开 news/views.py,写一个简单的视图函数,返回一串 Hello World!:
from django.http import HttpResponse
def index(request):
return HttpResponse('Hello World!')
复制代码
上面这个 index
函数能够说是一个最简单的视图函数了,实际大部分应用的视图要比这复杂得多。Django 同时支持基于函数的视图(FBV,Function-based View)和基于类的视图(CBV,Class-based View),这里显然是 FBV,接收一个 request
请求对象做为参数,返回了一个 HttpResponse
对象。
接着,咱们要让路由系统可以访问到刚才写好的视图函数。所以先实现子应用 news 的路由表,建立 news/urls.py 文件以下:
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
复制代码
每个 Django 路由表模块(urls.py)中都约定必须包含一个 urlpatterns
列表用来存放路由映射表。列表中每一个元素是一个用 django.urls.path
函数封装好的路由映射,一般接收如下三个参数:
route
:必须,即实际的访问路由,空字符串等于 /
,即空路由view
:必须,该路由将要访问的视图name
:可选,该路由的名称,方便后续在模板中使用咱们将刚刚写好的 news 路由表接入全局路由表。因为咱们但愿新闻可以展现在首页(即经过 /
就能访问,无需 /news
),所以 news 应用路由在全局路由中的 URL 是一个空字符串。在 django_news/urls.py 中修改以下:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('news.urls')),
]
复制代码
这里使用 django.urls.include
函数将 news 应用的路由表接入进来,而且 include
函数的参数是路由模块路径的字符串 news.urls
,省去了手动 import
的麻烦。
注意
添加路由规则时顺序是很重要的,由于在尝试匹配时会按照从上到下的顺序进行,所以应该把最模糊的路由(即空路由)放在最下面。
若是你开发服务器还在运行(若是没有能够再打开),访问 localhost:8000,就能够看到那一串熟悉的字符了:
上一步中,咱们学会了如何实现视图,并将其接入路由配置中,使其可以被用户访问。接下来,咱们将实现一个 Django 模板做为网页前端,从而给用户呈现更丰富的内容。
提示
若是你有过其余模板(或者相似技术)的开发经验,例如 Jinja、EJS 或是 JSP 等,对 Django 模板会有一种似曾相识的感受。若是你不了解什么是模板引擎,也不用担忧,简单的理解就是一个能够填充内容、甚至可以加入代码逻辑的相似 HTML 文档,最终会被转换成浏览器可以识别的 HTML 文档。
Django 模板本质上是一个 HTML 文档,只不过经过一些特殊的语法实现数据的填充。这里咱们讲解一下最经常使用的三个语法:
表达式插值
最经常使用的语法,没有之一。经过在一对花括号 {{}}
放入一个表达式,就可以在视图中传入表达式中变量的内容,并最终渲染成包含变量具体内容的 HTML 代码。须要注意的是,所支持的表达式仅支持如下形式(能够自由组合):
<!-- 单个变量 -->
{{ variable }}
<!-- 获取字典的键或对象的属性 -->
{{ dict.key }}
{{ object.attribute }}
<!-- 获取列表中的某个元素 -->
{{ list.0 }}
复制代码
例如,模板这样写:
<h1>{{ name }}</h1>
<p>{{ news.title }}</p>
<p>{{ news.visitors.0 }}</p>
复制代码
若是咱们在视图中传入如下上下文字典(Context Dictionary):
{
'name': 'Tuture',
'news': {
'title': 'Hello World',
'visitors': ['Tom', 'Marc'],
}
}
复制代码
那么最终渲染成的 HTML 代码就是:
<h1>Tuture</h1>
<p>Hello World</p>
<p>Tom</p>
复制代码
条件语句
条件语句的定义以下:
{% if is_true %}
<h1>It is true!</h1>
{% else %}
<h1>It is false!</h1>
{% endif %}
复制代码
若是变量 is_true
为真,那么最终渲染出来的就是 <h1>It is true!</h1>
,不然就是 <h1>It is false!</h1>
。注意:整个条件语句必须以 {% endif %}
结束,而且 {% else %}
是可选的。
循环语句
循环语句用来在模板上展现任意长的列表内容。其语法以下:
{% for elem in some_list %}
<p>{{ elem }}</p>
{% endfor %}
复制代码
若是传入的 some_list
为 ['Apple', 'Banana', 'Orange']
,那么渲染出的 HTML 代码就是:
<p>Apple</p>
<p>Banana</p>
<p>Orange</p>
复制代码
到了动手时间了,咱们先实现第一个 Django 模板。在 news 目录中建立一个 templates 目录,再在 templates 目录中建立一个 news 目录,并在内层的 news 目录中建立 index.html 文件:
mkdir -p news/templates/news
touch news/templates/news/index.html
复制代码
思考
听上去很麻烦,只建立
news/templates
,而后把模板放里面不就行了,为何还要再建立一个 news 目录?这是因为 Django 的模板查找机制会将全部应用里面的模板所有收集到一块儿,若是两个模板的名字冲突,就会致使其中一个模板不能被正确访问。若是放在 news 子文件夹里面,就可以经过news/index.html
访问,经过命名空间的机制避免了冲突。
模板的代码以下:
{% if news_list %}
<ul>
{% for elem in news_list %}
<li>
<h3>{{ elem.title }}</h3>
<p>{{ elem.content }}</p>
</li>
{% endfor %}
</ul>
{% else %}
<p>暂无新闻</p>
{% endif %}
复制代码
这短短几行模板代码却很好地覆盖了咱们刚刚讲述的三个模板语法:表达式插值、条件语句和循环语句。若是忘记其中某个地方是什么意思的话,翻上去看看吧!
完成模板的编写后,咱们要在视图中对其进行渲染。打开 news/views.py 文件,修改代码以下:
from django.shortcuts import render
def index(request):
context = {
'news_list': [
{
"title": "图雀写做工具推出了新的版本",
"content": "随随便便就能写出一篇好教程,真的很神奇",
},
{
"title": "图雀社区正式推出快速入门系列教程",
"content": "一杯茶的功夫,让你快速上手,绝无担心",
},
]
}
return render(request, 'news/index.html', context=context)
复制代码
这里咱们调用 django.shortcuts.render
函数来渲染模板,这个函数一般接受三个参数(有其余参数,可是这里咱们不关心):
request
:请求对象,直接把视图的参数 request
传进来就能够template_name
:模板名称,这里就是咱们刚刚建立的 news/index.html
context
:传入模板的上下文对象,必须是一个字典,字典中的每一个键对应模板中的变量。这里咱们弄了些假数据,伪装是从数据库里面取来的。再访问 localhost:8000,看一下咱们的首页是否是有内容了:
完美!
Django 的 MTV,咱们已经讲了 T(Template)和 V(View),如今来到了最后一关: M(Model)了。数据模型是 Django 入门最大的难点,消化这一步的内容须要花点力气,可是相信我,当你迈过 M 这最后一关,你便能真正上手 Django 开发了!下面咱们先介绍一下 Django 的数据模型设计。
Django 在数据模型方面的设计堪称典范,列举一些闪光点:
对于初学者而言,咱们暂且选择默认的 SQLite 数据库,省去了配置数据库的烦恼。在后面的进阶教程中,咱们会切换到其余适合生产环境的数据库。
简单来讲,ORM 可以将面向对象的代码转换成相应的 SQL 语句,从而对数据库进行操做。SQL 是用于访问和处理数据库的标准的计算机语言,可是直接写在代码里面显然难以维护,并且对使用者的要求也很是高,写的糟糕的 SQL 代码查询效率很是低下。所以,使用设计良好的 ORM 不只让代码可读性更好,也能帮助开发者进行查询优化,节省很多力气。
咱们来看一些简单的 Django ORM 例子:
# 查询全部模型
# 等价于 SELECT * FROM Blog
Blog.objects.all()
# 查询单个模型
# 等价于 SELECT * FROM Blog WHERE ID=1
Blog.objects.get(id=1)
# 添加单个模型
# 等价于 INSERT INTO Blog (title, content) VALUES ('hello', 'world')
blog = Blog(title='hello', content='world')
blog.save()
复制代码
有木有感受操做起来比 SQL 方便不少呢?
数据库迁移是指将用 Django 定义的模型转换成 SQL 代码(即迁移文件),并在数据库中进行建表操做(或更新表)。看下面这张图就知道了:
通常的开发流程就是这样:
makemigrations
命令建立迁移文件(存储在子应用的 migrations 目录里面)migrate
命令执行迁移终于到了动手的环节。咱们首先定义数据模型 Post
,包括标题 title
字段和 content
字段,代码以下:
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
def __str__(self):
return self.title
复制代码
定义好以后,运行如下命令建立迁移文件:
python manage.py makemigrations
复制代码
能够看到输出以下:
Migrations for 'news':
news/migrations/0001_initial.py
- Create model Post
复制代码
而且成功地自动建立了 news/migrations/0001_initial.py 迁移脚本。接着咱们进行数据库迁移:
python manage.py migrate
复制代码
输出以下图所示:
数据库迁移完成后,咱们就能够建立用于登陆后台管理的超级用户:
python manage.py createsuperuser
复制代码
按照提示填写用户名和密码便可。而后访问 localhost:8000/admin,进入后台系统的登陆页面:
填入刚才设置的用户名和密码,进入后台管理页面:
咦,咱们刚才建立的 news 应用还有 Post 模型去哪了?
那是由于咱们没有实现 news 应用的后台管理接口。在 news/admin.py 中填入代码以下:
from django.contrib import admin
from .models import Post
admin.site.register(Post)
复制代码
再次进入后台管理系统,能够看到咱们的 news 应用和 Post 模型了:
点击 Posts 一栏的 +Add 按钮,开始添加新闻(内容随意):
大概添加个两三条新闻就差很少啦。你也能够进一步探索后台管理系统,包括修改新闻、添加用户等等,均可以。
最后,咱们在视图中加入从数据库中查询的代码:
from django.shortcuts import render
from .models import Post
def index(request):
context = { 'news_list': Post.objects.all() }
return render(request, 'news/index.html', context=context)
复制代码
访问网站首页,能够看到刚才在后台管理系统添加的新闻了:
大功告成!在这篇教程中,咱们完成了一个新闻发布网站,而且能够从后台管理系统中添加新闻,最终展现到咱们的网站首页上。
但愿这篇教程可以让你对 Django 最重要的一些概念和操做有了基本的了解。Django 还有不少不少的高级玩法,例如数据模型中的高级查询、字段索引、更换数据库等等,模板中的继承机制、内部标签等等,还有视图中如何处理各种请求(POST、PUT等),咱们会在后续更多教程中逐一为你们讲解,不见不散!
想要学习更多精彩的实战技术教程?来图雀社区逛逛吧。