基于Django Rest框架实现API构建

本文首发于知乎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了,效果如下:

电影列表API.png

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的支持,在函数体中,针对不同的请求类型,有不同的处理。

修改后页面效果如下,可以进行内容的修改和条目的删除。


某个电影信息.png

5. 回顾

简单回顾一下,在一个普通的网站项目基础上,首先建立了专门的API文件夹,在其中首先建立序列化器以进行数据转化传输,然后建立了各类视图,并通过不同请求方法进行数据的增、删、改、查(即CRUD操作)API的构建。

可以看到,API的视图函数与一般网站视图函数的主要区别在于,API的视图函数是从数据库获取数据后进行序列化再传输,而一般网站视图函数则是获取数据后传递给前端模板以供展示。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容