Django API 开发:一个 Todo 应用的后端
引言
本文中,我们将学习应用 Django 构建一个 Todo(待办事项)应用的 API 后端,然后将其与 React 前端衔接。 我们曾经制造了第一个 API,并回忆了 HTTP 和 REST 的工作原理,但是您依然可能还没有“完整”理解它们如何分离在一同。
我们将创立一个 todo 构造,其中包含我们的后端 Django Python 代码和我们的前端 React Javascript 代码。
最终的规划将如下所示。
todo
| ├──frontend
| ├──React...
| ├──backend
| ├──Django...
初始化创立后端应用
任何 Django API 的第一步一直是装置 Django,然后在其之上添加 Django REST Framework。 首先在桌面上的代码目录中创立一个专用的 todo 目录。
翻开一个新的命令行控制台,然后输入以下命令:
$ cd ~/Desktop
$ cd code
$ mkdir todo && cd todo
留意: 确保已从上一章中停用了虚拟环境。 您能够经过键入退出来执行此操作。 命令行前面能否没有括号? 好。 那么您就不在现有的虚拟环境中。
在此 todo 文件夹中将是我们的后端和前端目录。 让我们创立一个后端文件夹,装置 Django,然后激活一个新的虚拟环境。
$ mkdir backend && cd backend
$ pipenv install django==2.2.6
$ pipenv shell
您应该在命令行上看到括号,以确认虚拟环境(后端)已激活。
如今曾经装置了 Django,我们应该首先创立一个传统的 Django 项目 todo_project,在其中添加第一个应用程序 todo,然后迁移初始数据库。
(backend) $ django-admin startproject todo_project . (backend) $ python manage.py startapp todos
(backend) $ python manage.py migrate
在 Django 中,我们一直需求将新应用添加到 INSTALLED_APPS 设置中,所以如今就这样做。 在文本编辑器中翻开 todo_project/settings.py。 在文件底部,添加 todos.apps.TodosConfig。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Local
'todos.apps.TodosConfig', # new
]
假如您如今在命令行上运转 python manage.py runserver 并导航至 http://127.0.0.1:8000/ ,则能够看到我们的项目已胜利装置。
准备开端吧!
Models
接下来是在 todo 应用程序中定义我们的 Todo 数据库模型。 我们将坚持根本状态,只要两个字段:title 和 body。
from django.db import models
class Todo(models.Model):
title = models.CharField(max_length=200)
body = models.TextField()
def __str__(self):
return self.title
我们在顶部导入模型,然后将其子类化以创立本人的 Todo 模型。 我们还添加了 str 办法,以为每个未来的模型实例提供易于了解的称号。
由于我们曾经更新了模型,如今该是 Django 停止两步操作的时分了:制造一个新的迁移文件,然后每次将数据库与更改同步。 在命令行上,键入 Control + c 以中止我们的本地效劳器。 然后运转以下两个命令:
(backend) $ python manage.py makemigrations todos
(backend) $ python manage.py migrate
能够选择添加我们要为其创立迁移文件的特定应用程序(我们能够只键入 python manage.py makemigrations ),但这是采用的最佳做法。 迁移文件是调试应用程序的一种绝妙办法,您应该努力为每个小的更改创立一个迁移文件。 假如我们在两个不同的应用程序中更新了模型,然后运转 python manage.py makemigrations ,则生成的单个迁移文件将包含两个应用程序中的数据。 这只会增加调试难度。 尝试使您的迁移尽可能小。
如今,我们能够运用内置的 Django 管理应用程序与我们的数据库停止交互。 假如我们立刻进入管理员,我们的Todo 应用程序将不会呈现。 我们需求经过 todos/admin.py 文件显式添加它,如下所示。
from django.contrib import admin
from .models import Todo
admin.site.register(Todo)
而已! 如今,我们能够创立一个超级用户帐户来登录管理员。
(backend) $ python manage.py createsuperuser
然后再次启动本地效劳器:
(backend) $ python manage.py runserver
如今,假如您阅读至 http://127.0.0.1:8000/admin/, 则能够登录到这个系统。
单击 Todos 旁边的 “ +Add” ,并创立 3 个新的待办事项,并确保为两者添加标题和正文。 如图:
至此,我们实践上曾经完成了 Todo API 的传统 Django 局部。 由于我们不用为该项目树立网页,因而不需求网站URL,视图或模板。 我们需求的只是一个模型,而 Django REST Framework将担任其他的工作。
Dajngo REST 框架
中止本地效劳器 Control + c ,然后经过 pipenv 装置 Django REST Framework。
(backend) $ pipenv install djangorestframework==3.10.3
然后像其他任何第三方应用程序一样,将 rest_framework 添加到我们的 INSTALLED_APPS 设置中。 我们还希望开端配置一切 REST_FRAMEWORK 下存在的 Django REST Framework 特定设置。 首先,我们将权限明白设置为 AllowAny 。 此行位于文件的底部。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 3rd party
'rest_framework', # new
# Local
'todos.apps.TodosConfig',
]
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
]
}
Django REST Framework 包含冗长的隐式设置默许设置列表。 您能够在此处查看完好列表。 AllowAny 是其中之一,这意味着当我们像上面所做的那样显式设置它时,其效果与没有设置 DEFAULT_PERMISSION_CLASSES 的配置完整相同。
好的,这样就装置了Django REST Framework。
相反,我们将更新三个特定于 Django REST 框架的文件,以将数据库模型转换为 Web API:urls.py,views.py 和 serializers.py。
URLs
我喜欢先从 URL 开端,由于它们是我们 API 端点的入口点。 就像在传统的 Django 项目中一样,urls.py 文件使我们能够配置路由。
从 Django 项目级文件 todo_project/urls.py 开端。 我们在第二行导入 include,并在 api/ 为我们的 todo 应用添加一条道路。
from django.contrib import admin
from django.urls import include, path # new
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('todos.urls')), # new
]
接下来,创立我们的应用程序级别的 todos/urls.py 文件。
(backend) $ touch todos/urls.py
并运用以下代码对其停止更新。
from django.urls import path
from .views import ListTodo, DetailTodo
urlpatterns = [
path('/', DetailTodo.as_view()),
path('', ListTodo.as_view()),
]
请留意,我们援用的还有两个尚未创立的视图:ListTodo 和 DetailTodo。 但是,路由现已完成。 api/ 有一切待办事项的列表位于空字符串 '',即。 每个待办事项都将在其主键上可用,这是 Django 在每个数据库表中自动设置的值。 第一个条目是 1,第二个条目是 2,依此类推。 因而,我们的第一个待办事项最终将位于API端点api/1/。
Serializers
让我们回忆一下到目前为止。 我们从一个传统的 Django 项目和应用程序开端,我们创立了数据库模型并添加了数据。 然后,我们装置了 Django REST Framework 并配置了 URL。 如今,我们需求将模型中的数据转换为将在 URL 输出的 JSON。 因而,我们需求一个序列化器。
Django REST Framework 附带了一个强大的内置序列化程序类,我们能够运用少量代码快速扩展它们。 这就是我们在这里要做的。
首先在 todos 应用中创立一个新的 serializers.py 文件。
(backend) $ touch todos/serializers.py
然后更新代码,如下所示:
from rest_framework import serializers
from .models import Todo
class TodoSerializer(serializers.ModelSerializer):
class Meta:
model = Todo
fields = ('id', 'title', 'body',)
在顶部,我们从 Django REST Framework 以及我们的 models.py 文件导入了序列化器。 接下来,我们创立一个类 TodoSerializer。 这里的格式与我们在 Django 自身中创立模型类或表单的方式十分类似。 我们正在指定要运用的模型以及我们要公开的特定字段。 请记住,id 是 Django 自动创立的,因而我们不用在 Todo 模型中定义它,但是我们将在细节视图中运用它。
就是这样。 Django REST Framework 如今将神奇地将我们的数据转换为 JSON,从而公开来自 Todo 模型的 id,title 和 body字段。
我们需求做的最后一件事是配置我们的 views.py 文件。
Views
在传统的 Django 中,视图用于自定义要发送到模板的数据。 在 Django REST Framework 中,视图执行相同的操作,但对序列化的数据而言。
Django REST Framework 视图的语法成心与常规 Django 视图十分类似,就像常规 Django 一样,Django REST Framework 随附了通用视图以用于常见用例。 这就是我们在这里运用的。
更新 todos/views.py 文件,如下所示:
from rest_framework import generics
from .models import Todo
from .serializers import TodoSerializer
class ListTodo(generics.ListAPIView):
queryset = Todo.objects.all()
serializer_class = TodoSerializer
class DetailTodo(generics.RetrieveAPIView):
queryset = Todo.objects.all()
serializer_class = TodoSerializer
在顶部,我们导入 Django REST Framework 的泛型视图以及我们的 models.py 和 serializers.py 文件。
从我们的 todos/urls.py 文件中调用,我们有两条道路,因而有两个不同的视图。 我们将运用 ListAPIView 显现一切待办事项,并运用 RetrieveAPIView 显现单个模型实例。
精明的读者会留意到这里的代码有些冗余。 即便扩展的通用视图有所不同,我们本质上还是为每个视图反复运用 queryset 和 serializer_class 。
但是如今我们完成了! 我们的API已准备就绪,能够运用。 如您所见,Django REST Framework和Django之间的独一真正区别是,运用Django REST Framework,我们需求添加 serializers.py 文件,而无需模板文件。 否则,urls.py 和 views.py 文件的内容一样。
Consuming the API
传统上运用 API 是一个应战。 关于给定的 HTTP 响应或恳求的正文和标头中包含的一切信息,基本没有很好的可视化效果。
取而代之的是,大多数开发人员运用命令行 HTTP 客户端(例如 cURL)。
2012 年,第三方软件产品 Postman 投放市场,如今全球有数百万希望经过可视化,功用丰厚的方式与 API 交互的开发人员运用。
但是 Django REST 框架最令人惊奇的事情之一是,它附带了功用强大的可阅读 API ,我们能够立刻运用它。 假如您发现需求运用 API 停止更多自定义,则能够运用 Postman 之类的工具。 但是通常内置的可阅读 API 绰绰有余。
Browsable API
如今让我们运用可阅读的 API 与我们的数据停止交互。 确保本地效劳器正在运转。
(backend) $ python manage.py runserver
然后导航到 http://127.0.0.1:8000/api/ 以查看我们的工作 API 列表视图端点。
该页面显现了我们先前在数据库模型中创立的三个待办事项。
首先,让我们看一下原始的 JSON视 图,即实践经过互联网传输的视图。 单击右上角的“ GET”按钮,然后选择 JSON。
假如您返回 http://127.0.0.1:8000/api/ 的列表视图页面,我们能够看到其他信息。 回想一下,HTTP 动词 GET 用于读取数据,而 POST 用于更新或创立数据。
在“列表待办事项”下显现 GET/api/ ,它通知我们我们在此端点上执行了 GET 。 下方显现的是 HTTP 200 OK ,这是我们的状态代码,一切正常。 最重要的是,它下面显现允许:GET,HEAD,OPTIONS。 请留意,由于这是一个只读端点,因而不包括 POST,我们只能执行 GET。
我们还为每个模型制造了一个 DetailTodo 视图。 这称为实例,在 http://127.0.0.1:8000/api/1/ 上可见。
您也能够导航至以下端点:
http://127.0.0.1:8000/api/2
http://127.0.0.1:8000/api/3
CORS
我们需求做的最后一步是跨源资源共享(CORS)。 每当客户端与不同域( mysite.com 与 yoursite.com )或端口( localhost:3000 与 localhost:8000 )上托管的 API 停止交互时,都会存在潜在的平安问题。
详细来说,CORS 请求效劳器包含特定的 HTTP 标头,以允许客户端肯定能否以及何时应允许跨域恳求。
我们的 Django API 后端将与专用前端通讯,该前端位于用于本地开发的不同端口上,并在部署后位于另一个域上。
处置此问题的最简双方法(以及 Django REST 框架倡议的一种办法)是运用中间件,该中间件将依据我们的设置自动包括恰当的 HTTP 标头。
我们将运用的软件包是 django-cors-header ,能够轻松将其添加到我们现有的项目中。
首先运用 Control + c 退出我们的效劳器,然后运用 Pipenv 装置 django-cors-headers 。
(backend) $ pipenv install django-cors-headers==3.1.1
接下来,在三个位置更新我们的 settings.py 文件:
将corsheaders添加到 INSTALLED_APPS
在MIDDLEWARE中把 CorsMiddleware 添加到 CommonMiddleWare 的上方
创立一个 CORS_ORIGIN_WHITELIST
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 3rd party
'rest_framework',
'corsheaders', # new
# Local
'todos.apps.TodosConfig',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware', # new
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
CORS_ORIGIN_WHITELIST = (
'http://localhost:3000',
'http://localhost:8000',
)
将 corsheaders.middleware.CorsMiddleware 呈现在正确的位置十分重要。 由于中间件是从上到下加载的,因而它位于 MIDDLEWARE 设置中的 django.middleware.common.CommonMiddleware 之上。 另外请留意,我们已将两个域列入白名单:localhost:3000 和 localhost:"8000。 前者是 React 的默许端口,下一章将在前端运用它。 后者是默许的 Django 端口。
测试
您应该一直为 Django 项目编写测试。 前期破费的少量时间将为您俭省大量的时间和以后的调试错误。 让我们添加两个根本测试,以确认标题和正文内容的行为契合预期。
翻开 todos/tests.py文件,并添加以下内容:
from django.test import TestCase
from .models import Todo
class TodoModelTest(TestCase):
@classmethod
def setUpTestData(cls):
Todo.objects.create(title='first todo', body='a body here')
def test_title_content(self):
todo = Todo.objects.get(id=1)
expected_object_name = f'{todo.title}'
self.assertEquals(expected_object_name, 'first todo')
def test_body_content(self):
todo = Todo.objects.get(id=1)
expected_object_name = f'{todo.body}'
self.assertEquals(expected_object_name, 'a body here')
它运用 Django 的内置 TestCase 类。 首先,我们在 setUpTestData 中设置数据,然后编写两个新测试。 然后运用 python manage.py test 命令运转测试。
(backend) $ python manage.py test
...
Ran 2 tests in 0.002s
OK
就是这样! 我们的后端现已完成。 确保效劳器正在运转,由于我们将在下一章中运用它。
(backend) $ python manage.py runserver
总结
在本文中,我们能够看到只需最少的代码,Django REST Framework 便允许我们从头开端创立 Django API。 我们从典型 Django 开发需求的独一组件是 models.py 文件和 urls.py 路由。 views.py 和 serializers.py 文件完整是 Django REST Framework 特有的。
与上一示例不同,我们没有为该项目构建任何网页,由于我们的目的只是创立一个 API。 但是,在未来的任何时分,我们都能够轻松完成! 只需添加一个新视图,URL 和一个模板即可公开我们现有的数据库模型。
在此示例中,重要的一点是我们添加了 CORS 标头,并且仅将域 localhost:3000 和 localhost:8000 明白设置为能够访问我们的 API。 第一次开端构建 API 时,很容易混杂正确设置 CORS 标头。
我们能够做更多的配置,以后再做,但最终,创立 Django API 的过程是树立模型,编写一些 URL 路由,然后添加Django REST Framework 的序列化程序和视图所提供的一些魔术。