本文首发于知乎https://zhuanlan.zhihu.com/p/488232953,禁止转载。
1. 什么是API?
简单看一下百科的解释:
API之主要目的是提供应用程序与开发人员以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。提供API所定义的功能的软件称作此API的实现。API是一种接口,故而是一种抽象。 应用程序接口(英语:ApplicationProgrammingInterface,简称:API),又称为应用编程接口,就是软件系统不同组成部分衔接的约定。
简单来说,某程序员写好了一个软件,希望作为服务提供给其他人使用,但又不想给别人看源码(或者别人也不想看…)。那么他可以告诉别人,我这个软件有哪些功能, 则用户可以通过调用特定函数获取到所需的数据或执行某些操作,这就是API的作用啦。
2. 安装djangorestframework包
作为网络开发程序员,我们就是那个写软件的程序员,所以我们要为用户提供API 并告诉用户如何使用我们的软件。在Django项目中,一般使用djangorestframework包来开发API。
djangorestframework包直接使用pip安装即可
pip install djangorestframework
3. 建立Django项目
在本文中,我们通过建立一个电影网站,让用户 获取、修改以及增加新的电影信息,以熟悉Django API的构建。
可以参考基本Django项目构建一文建立起项目,简单列举一下相关命令。
django-admin startproject movie_api
cd movie_api
python manage.py startapp movie # 这里记得在配置文件中加入movie
python manage.py migrate
python manage.py runserver
这样就可以跑起来一个空项目了。
因为本文只关注API的构建,这里只建立一个数据模型,其他的网页浏览视图、模板就不做了。
3.1 建立Model
在movie/models.py
中建立电影数据类,
from django.db import models
# Create your models here.
class Movie(models.Model):
name = models.CharField(max_length=200)
year = models.IntegerField()
def __str__(self):
return f'{self.name} ({self.year})'
对数据模型更改后,要进行迁移
python manage.py makemigrations
python manage.py migrate
使用python manage.py shell
进行项目的shell添加一些数据
>>> from movie.models import Movie
>>> Movie.objects.create(name="The Shawshank Redemption", year=1994)
<Movie: The Shawshank Redemption (1994)>
>>> Movie.objects.create(name="The Godfather", year=1972)
<Movie: The Godfather (1972)>
>>> Movie.objects.create(name="The Dark Knight", year=2008)
<Movie: The Dark Knight (2008)>
>>> Movie.objects.create(name='The Godfather: Part II', year=1974)
<Movie: The Godfather: Part II (1974)>
>>> Movie.objects.create(name='12 Angry Men', year=1957)
<Movie: 12 Angry Men (1957)>
>>> Movie.objects.all()
<QuerySet [<Movie: The Shawshank Redemption (1994)>, <Movie: The Godfather (1972)>, <Movie: The Dark Knight (2008)>, <Movie: The Godfather: Part II (1974)>, <Movie: 12 Angry Men (1957)>]>
4. 构建API
首先在movie_api/settings.py
文件找到INSTALLED_APPS
, 注册rest_framework,现在这个列表应该如下,
INSTALLED_APPS = [
... # 其他已有值
'movie',
'rest_framework',
]
为了避免与网站基本内容混淆,可以项目根目录下建立一个专门的api文件夹,并在其中建立一个空的__init__.py
文件,表明这是一个包。
4.1 建立序列化器
我们把变量从内存中变成可存储或传输的过程称之为序列化。——廖雪峰
也就是说,从数据库中把数据读到我们的Model类型的变量中后,还要经过序列化,才能顺利传输到客户端(浏览器),供用户浏览使用。
在api文件夹下建立serializers.py
文件,内容如下
from rest_framework import serializers
from movie.models import Movie
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields= '__all__'
这里建立了一个Movie专用的序列化器,其中序列化的数据包括所有字段。
4.2 建立视图并绑定URL
4.2.1 获取所有电影
在api文件夹下建立views.py
文件
from rest_framework.decorators import api_view
from movie.models import Movie
from .serializers import MovieSerializer
from rest_framework.response import Response
@api_view(['GET'])
def get_movies(request):
movies = Movie.objects.all()
serializer = MovieSerializer(movies, many=True)
return Response(serializer.data)
这里通过装饰器来设置请求方法,这里只是获取,用GET即可。
注意在一个普通的网站中,获取了电影列表后,应该传输给前端的模板,以供渲染,但在API中,我们进行序列化,以供传输。
然后绑定url,在api文件夹下建立urls.py
文件,
from django.urls import path
from . import views
urlpatterns = [
path('', views.get_movies, name='get_movies')
]
如文章基本Django项目构建中提到的,还没有被django项目所识别,因此,需要在movie_api/urls.py
文件中进行引用,以让项目进行识别。
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('movies/', include('api.urls')),
]
这样,运行项目后,我们就可以通过网址http://127.0.0.1:8000/movies/访问电影列表api了,效果如下:
4.2.2 新增电影
新增电影条目可以保持与电影列表的网址相同,只需要在视图中增加一个POST请求方法即可。修改api/views.py
中相关函数如下,
from rest_framework import status
@api_view(['GET', 'POST'])
def get_movies(request):
if request.method == 'GET':
movies = Movie.objects.all()
serializer = MovieSerializer(movies, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = MovieSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
要注意的是,一旦数据有变动,要使用is_valid
进行数据的验证并使用save
保存,这里包括类型、长度等的验证。
再次访问网址http://127.0.0.1:8000/movies/,可以看到下方多出了一个输入框,以增加电影条目。
4.2.3 获取某个电影的信息
这里也是获取内容,与[[#4 2 1 获取所有电影]]类似,首先在api/views.py
中添加视图函数,
@api_view(['PUT'])
def get_movie_detail(request, id):
movie = Movie.objects.get(pk=id)
serializer = MovieSerializer(movie)
return Response(serializer.data)
然后在api/urls.py
中绑定网址,
urlpatterns = [
path('<int:id>', views.get_movie_detail, name='get_movie_detail'),
]
即可通过http://127.0.0.1:8000/movies/1 访问id为1的电影信息了。
4.2.4 修改或删除某个电影的信息
修改或删除也是针对某个电影,所以网址与[[#4 2 3 获取某个电影的信息]]可以相同,不过请求方法需要设置为PUT和DELETE,修改相应的视图函数如下:
@api_view(['GET', 'PUT', 'DELETE'])
def get_movie_detail(request, id):
movie = Movie.objects.get(pk=id)
if request.method == 'GET':
serializer = MovieSerializer(movie)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = MovieSerializer(movie, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
movie.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
这里增加了对请求方法PUT和DELETE的支持,在函数体中,针对不同的请求类型,有不同的处理。
修改后页面效果如下,可以进行内容的修改和条目的删除。
5. 回顾
简单回顾一下,在一个普通的网站项目基础上,首先建立了专门的API文件夹,在其中首先建立序列化器以进行数据转化传输,然后建立了各类视图,并通过不同请求方法进行数据的增、删、改、查(即CRUD操作)API的构建。
可以看到,API的视图函数与一般网站视图函数的主要区别在于,API的视图函数是从数据库获取数据后进行序列化再传输,而一般网站视图函数则是获取数据后传递给前端模板以供展示。