基于函数的视图 – Django Rest框架
Django REST框架允许我们与常规的Django视图一起工作。它有利于处理HTTP请求并提供适当的HTTP响应。在本节中,你将了解如何为Restful Web服务实现Django视图。我们还利用了@api_view装饰器。
在进行Django REST框架序列化工作之前,你应该确保你已经在虚拟环境中安装了Django和Django REST框架。
接下来,你可以创建一个名为emt的项目和一个名为transformers的应用程序。
在这一部分,我们将使用PostgreSQL。你必须在PostgreSQL中创建一个名为emt的数据库。
注意:如果你需要使用SQLite工作,你可以继续使用默认数据库。
为了使用PostgreSQL,我们需要安装一个Python-PostgreSQL数据库适配器(psycopg2)。这个软件包可以帮助Django与PostgreSQL数据库进行交互。你可以使用下面的命令来安装psycopg2软件包。在执行该命令之前,请确保PATH环境变量中包含PostgreSQL bin文件夹,并且虚拟环境已经激活。
pip install psycopg2
现在,你需要在你的Django项目中配置PostgreSQL数据库。默认情况下,数据库配置有一个SQLite数据库引擎和数据库文件名。你可以检查setting.py Python文件,用PostgreSQL数据库配置替换默认的数据库配置。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'emt',
'USER': 'username',
'PASSWORD': 'password',
'HOST': '127.0.0.1',
'PORT': '5432',
}
}
这里,name指的是数据库名称,user和password指的是Postgres的用户名和密码。
最后,我们需要在我们的虚拟环境中安装httpie包。我们将组成HTTPie命令进行CRUD操作。你可以激活虚拟环境并运行以下命令
pip install –upgrade httpie
现在,让我们来创建模型和序列化器。
创建模型和序列化器
在进入基于类的视图之前,让我们创建一个模型和它的序列化器,这有助于演示基于函数的视图的工作。
创建模型
在Django中,模型是以面向对象的方式处理数据库的类。每个模型类指的是一个数据库表,模型类中的每个属性指的是一个数据库列。在这里,我们将在Django中创建一个简单的Transformer模型,它存储了Transformer角色的名称、替代模式、描述以及角色是生是死。我们需要为我们的Transformer实体提供以下属性。
- name
- alternate_mode
- description
- alive
在RESTFul网络服务中,每个资源都使用唯一的URL进行访问,这意味着每个员工都有他们唯一的URL。而且,模型的每个方法都是由一个HTTP动词和范围组成的。
HTTP动词 | 范围 | 语义 | URL |
---|---|---|---|
GET | 变形器 | 检索单个变形器 | http://localhost:8000/transformers/{id}/ |
GET | 变压器的集合 | 检索集合中的所有变压器 | http://localhost:8000/transformers/ |
POST | 变压器的集合 | 在集合中创建一个新的变压器 | http://localhost:8000/transformers/ |
PUT | 变压器 | 更新一个现有的变压器 | http://localhost:8000/transformers/{id}/ |
PATCH | 变形器 | 部分更新现有的变形器 | http://localhost:8000/transformers/{id}/ |
DELETE | 变压器 | 删除一个现有的变压器 | http://localhost:8000/transformers/{id}/ |
让我们进入Django中Transformer模型的实现。你可以用下面的代码替换models.py Python文件。
from django.db import models
class Transformer(models.Model):
name = models.CharField(max_length=150, unique=True)
alternate_mode = models.CharField(
max_length=250,
blank=True,
null=True)
description = models.CharField(
max_length=500,
blank=True,
null=True)
alive = models.BooleanField(default=False)
class Meta:
ordering = ('name',)
def __str__(self):
return self.name
Transformer模型是django.db.models.Model类的一个子类,定义了属性和一个Meta内类。它有一个排序属性,根据名字将结果按升序排列。让我们使用下面的命令进行初始迁移(确保你在settings.py Python文件的INSTALLED_APPS中定义了你的应用程序(’transformers.apps.TransformersConfig’)。
python manage.py makemigrations
接下来,使用下面的命令应用所有生成的迁移。
python manage.py migrate
应用生成的迁移将在数据库中创建一个Transformer表。
创建 Serializer
在你的应用程序(transformers)文件夹中创建一个新的serializers.py Python文件并添加以下代码。
from rest_framework import serializers
from transformers.models import Transformer
class TransformerSerializer(serializers.ModelSerializer):
class Meta:
model = Transformer
fields = "__all__"
在这里,我们创建了一个TransformerSerializer类,它继承于rest_framework.serializers.ModelSerializer。如果你想深入了解不同类型的序列化,你可以查看Django REST框架(DRF)序列化。现在,是时候开始我们的基于函数的视图之旅了。我们将从普通的Django视图开始,之后我们将利用@api_view装饰器的优势。
基于函数的视图
Django视图有助于处理HTTP请求和提供HTTP响应。在收到一个HTTP请求时,Django会创建一个HttpRequest实例,并将其作为第一个参数传递给视图函数。这个实例包含HTTP动词,如GET、POST、PUT、PATCH或DELETE。视图函数检查该值并根据HTTP动词执行代码。这里的代码使用@csrf_exempt装饰器来设置一个CSRF(跨站请求伪造)cookie。这使得从没有CSRF令牌的客户端向这个视图POST成为可能。让我们进入实施过程。你可以在views.py文件中添加以下代码。
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParser
from transformers.models import Transformer
from transformers.serializers import TransformerSerializer
@csrf_exempt
def transformer_list(request):
"""
List all transformers, or create a new transformer
"""
if request.method == 'GET':
transformer = Transformer.objects.all()
serializer = TransformerSerializer(transformer, many=True)
return JsonResponse(serializer.data, safe=False)
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = TransformerSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
return JsonResponse(serializer.errors, status=400)
@csrf_exempt
def transformer_detail(request, pk):
try:
transformer = Transformer.objects.get(pk=pk)
except Transformer.DoesNotExist:
return HttpResponse(status=404)
if request.method == 'GET':
serializer = TransformerSerializer(transformer)
return JsonResponse(serializer.data)
elif request.method == 'PUT':
data = JSONParser().parse(request)
serializer = TransformerSerializer(transformer, data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data)
return JsonResponse(serializer.errors, status=400)
elif request.method == 'DELETE':
transformer.delete()
return HttpResponse(status=204)
让我们评估一下这段代码。这里我们有两个函数。
- transformer_list()
- transformer_detail()
transformer_list()
ransformer_list()函数能够处理两个HTTP动词 – GET和POST。
如果动词是GET,代码就会检索所有的转化器实例。
if request.method == 'GET':
transformer = Transformer.objects.all()
serializer = TransformerSerializer(transformer, many=True)
return JsonResponse(serializer.data, safe=False)
- 它使用objects.all()方法检索变压器的所有数据。
- 使用TransformerSerializer将任务序列化。
- 然后使用JsonResponse()渲染序列化的数据,并返回结果
注意 :指定的many=True参数可以序列化多个转化器实例。
如果动词是POST,代码会创建一个新的转化器。这里的数据是以JSON格式提供的,同时组成HTTP请求。
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = TransformerSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
return JsonResponse(serializer.errors, status=400)
- 使用JSONParser来解析请求。
- 使用TransformerSerializer将解析后的数据序列化。
- 如果数据是有效的,它被保存在数据库中,并返回带有状态代码的渲染结果。
transformer_detail()
transformer_detail()函数能够处理三种HTTP动词 – GET、PUT和DELETE。如果动词是GET,那么代码会根据主键检索一个单一的转化器实例。如果动词是PUT,代码会更新实例并将其保存到数据库中,如果是DELETE,那么代码会从数据库中删除实例。
作为下一步,我们需要将URL路由到视图。你需要在app(transformers)文件夹中创建一个新的Python文件,名称为urls.py,并添加以下代码。
from django.urls import path
from transformers import views
urlpatterns = [
path('transformers/',
views.transformer_list,
name = 'employee-list'),
path('transformers/<int:pk>/',
views.transformer_detail,
name = 'employee-detail'),
]
现在,让我们来更新根URL的配置。你可以把下面的代码添加到urls.py文件中(与settings.py文件在同一个文件夹中)。
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('', include('transformers.urls')),
]
现在是时候编写和发送HTTP请求来测试我们的视图了。
让我们创建一个新条目。HTTPie的命令是。
http POST :8000/transformers/ name=”Wheeljack” alternate_mode=”1977 Lancia Stratos Turbo” description=”Always inventing new weapons and gadgets” alive=”False”
输出
HTTP/1.1 201 Created
Content-Length: 152
Content-Type: application/json
Date: Mon, 25 Jan 2021 05:23:10 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
{
"alive": false,
"alternate_mode": "1977 Lancia Stratos Turbo",
"description": "Always inventing new weapons and gadgets",
"id": 7,
"name": "Wheeljack"
}
分享命令提示的截图供你参考
让我们把转化器的live字段(id=7)更新为True。HTTPie的命令是。
http PUT :8000/transformers/7/ name=”Wheeljack” alternate_mode=”1977 Lancia Stratos Turbo” description=”Always inventing new weapons and gadgets” alive=”True”
输出
HTTP/1.1 200 OK
Content-Length: 151
Content-Type: application/json
Date: Mon, 25 Jan 2021 05:26:22 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
{
"alive": true,
"alternate_mode": "1977 Lancia Stratos Turbo",
"description": "Always inventing new weapons and gadgets",
"id": 7,
"name": "Wheeljack"
}
让我们来检索所有的转化器。HTTPie的命令是:
http GET :8000/transformers/
输出
HTTP/1.1 200 OK
Allow: GET, POST, OPTIONS
Content-Length: 629
Content-Type: application/json
Date: Mon, 25 Jan 2021 05:43:36 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
[
{
"alive": true,
"alternate_mode": "1977 Lancia Stratos Turbo",
"description": "Always inventing new weapons and gadgets",
"id": 7,
"name": "Wheeljack"
},
{
"alive": true,
"alternate_mode": "1979 Porsche 924",
"description": "Eager and Daring",
"id": 5,
"name": "Cliffjumper"
},
{
"alive": true,
"alternate_mode": "1979 VW Beetle",
"description": "Small, eager, and brave. Acts as a messenger, scout, and spy",
"id": 3,
"name": "Bumblebee"
},
{
"alive": false,
"alternate_mode": "1979 Freightliner Semi",
"description": "Optimus Prime is the strongest and most courageous and leader of all Autobots",
"id": 1,
"name": "Optimus Prime"
}
]
分享命令提示的截图
@api_view
@api_view是rest_framework.decorators模块中的一个装饰器,它是所有Django REST框架视图的基类。我们可以在@api_view装饰器中提供允许的HTTP动词作为http_methods_names参数(字符串的列表)。如果RESTful Web服务收到一个不支持的HTTP Verb,装饰器会返回一个适当的响应,而不是Django中的一个意外错误。
@api_view(http_method_names=['GET'])
你可以用下面的代码替换views.py文件。
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from transformers.models import Transformer
from transformers.serializers import TransformerSerializer
@api_view(['GET','POST'])
def transformer_list(request):
"""
List all transformers, or create a new transformer
"""
if request.method == 'GET':
transformer = Transformer.objects.all()
serializer = TransformerSerializer(transformer, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = TransformerSerializer(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)
@api_view(['GET','PUT','PATCH','DELETE'])
def transformer_detail(request, pk):
try:
transformer = Transformer.objects.get(pk=pk)
except Transformer.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = TransformerSerializer(transformer)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = TransformerSerializer(transformer, 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 == 'PATCH':
serializer = TransformerSerializer(transformer,
data=request.data,
partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
transformer.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
在新的代码中,transformer_list和transformer_detail函数被@api_vew装饰器包装起来。transformer_list函数支持GET和POST动词,而transformer_detail方法支持GET、PUT、PATCH和DELETE动词。这些支持的动词被作为每个方法的装饰器参数传递。这里,我们删除了rest_framework.parsers.JSONParser类。这样我们可以让代码与不同的解析器一起工作。我们还用一个更通用的rest_framework.response.Response类来替换JSONResponse类。
让我们运行带有OPTIONS动词的HTTPie命令,列出transformer_list方法中支持的方法和动词。
http OPTIONS :8000/transformers/
输出
HTTP/1.1 200 OK
Allow: GET, OPTIONS, POST
Content-Length: 225
Content-Type: application/json
Date: Mon, 25 Jan 2021 16:52:10 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
{
"description": "List all transformers, or create a new transformer",
"name": "Transformer List",
"parses": [
"application/json",
"application/x-www-form-urlencoded",
"multipart/form-data"
],
"renders": [
"application/json",
"text/html"
]
}
分享命令提示的截图
输出显示transformer_list方法的细节。它允许GET、OPTIONS和POST等HTTP动词。它还显示了该函数支持的不同解析和渲染方式。
我们来看看transformer_detail函数。HTTPie的命令是
http OPTIONS :8000/transformers/1/
输出
HTTP/1.1 200 OK
Allow: OPTIONS, DELETE, GET, PATCH, PUT
Content-Length: 177
Content-Type: application/json
Date: Mon, 25 Jan 2021 16:59:23 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
{
"description": "",
"name": "Transformer Detail",
"parses": [
"application/json",
"application/x-www-form-urlencoded",
"multipart/form-data"
],
"renders": [
"application/json",
"text/html"
]
}
你可以注意到transformer_detail()函数支持OPTIONS、DELETE、GET、PATCH和PUT HTTP动词。它还显示了该函数支持的不同解析和渲染。
@api_view装饰器可以通过选择适当的解析器来解析不同的内容类型。从上面的输出中,我们可以注意到@api_view装饰器所支持的不同解析器。当我们使用@api_view装饰器时,它会自动利用APIView类和其设置。这样我们就可以使用解析器和渲染器了。
@api_view装饰器帮助Django REST框架检查数据属性中的Content-Type头,并确定解析请求的确切解析器。它还调用rest_framework.negotiation.DefaultContentNegotiation类来为请求选择合适的渲染器。
现在,是时候编排和发送不同的HTTP请求了。让我们来创建一个新的条目。HTTPie的命令是
http POST :8000/transformers/ name=”Prowl” alternate_mode=”1979 Nissan 280ZX Police Car” description=”Strives to find reason and logic in everything” alive=”False”
输出
HTTP/1.1 201 Created
Allow: GET, POST, OPTIONS
Content-Length: 149
Content-Type: application/json
Date: Mon, 25 Jan 2021 16:18:56 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
{
"alive": false,
"alternate_mode": "1979 Nissan 280ZX Police Car",
"description": "Strives to find reason and logic in everything",
"id": 10,
"name": "Prowl"
}
分享命令提示的截图
让我们把活着的字段值更新为真。HTTPie的命令是。
http PATCH :8000/transformers/10/ alive=”True”
输出
HTTP/1.1 200 OK
Allow: PATCH, PUT, DELETE, OPTIONS, GET
Content-Length: 148
Content-Type: application/json
Date: Mon, 25 Jan 2021 16:22:35 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
{
"alive": true,
"alternate_mode": "1979 Nissan 280ZX Police Car",
"description": "Strives to find reason and logic in everything",
"id": 10,
"name": "Prowl"
}
分享命令提示的截图
我们来检索所有的条目。HTTPie的命令是
http GET :8000/transformers/
输出
HTTP/1.1 200 OK
Allow: GET, POST, OPTIONS
Content-Length: 739
Content-Type: application/json
Date: Mon, 25 Jan 2021 16:23:52 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
[
{
"alive": true,
"alternate_mode": "1979 Nissan 280ZX Police Car",
"description": "Strives to find reason and logic in everything",
"id": 10,
"name": "Prowl"
},
{
"alive": true,
"alternate_mode": "1977 Lancia Stratos Turbo",
"description": "Always inventing new weapons and gadgets",
"id": 7,
"name": "Wheeljack"
},
{
"alive": true,
"alternate_mode": "1979 Porsche 924",
"description": "Eager and Daring",
"id": 5,
"name": "Cliffjumper"
},
{
"alive": true,
"alternate_mode": "1979 VW Beetle",
"description": "Small, eager, and brave. Acts as a messenger, scout, and spy",
"id": 3,
"name": "Bumblebee"
},
{
"alive": false,
"alternate_mode": "1979 Freightliner Semi",
"description": "Optimus Prime is the strongest and most courageous and leader of all Autobots",
"id": 1,
"name": "Optimus Prime"
}
]
分享命令提示的截图
在本节中,我们了解了如何编写Django视图的代码来处理HTTP请求并返回HTTP响应。我们还认识到用@api_view装饰器来包装我们的视图的重要性。最后,我们组成并发送了不同的请求,包括OPTIONS HTTP请求,它显示了支持的HTTP动词、解析器和渲染器。