📌  相关文章
📜  基于函数的视图——Django Rest Framework

📅  最后修改于: 2022-05-13 01:55:48.074000             🧑  作者: Mango

基于函数的视图——Django Rest Framework

Django REST Framework 允许我们使用常规的 Django 视图。它有助于处理 HTTP 请求并提供适当的 HTTP 响应。在本节中,您将了解如何为 Restful Web 服务实现 Django 视图。我们还使用了@api_view 装饰器。

在使用 Django REST Framework 序列化程序之前,您应该确保您已经在虚拟环境中安装了 Django 和 Django REST Framework。您可以查看以下教程:

  • 使用 venv 创建虚拟环境 | Python
  • Django 安装
  • Django REST 框架安装

接下来,您可以创建一个名为 emt 的项目和一个名为 Transformers 的应用程序。参考以下文章了解如何在 Django 中创建项目和应用程序。

  • 如何在 Django 中使用 MVT 创建一个基本项目?
  • 如何在 Django 中创建应用程序?

在本节中,我们将使用 PostgreSQL。您必须在 PostgreSQL 中创建一个名为 emt 的数据库。您可以查看以下链接进行安装。

在 Windows 上安装 PostgreSQL

要使用 PostgreSQL,我们需要安装一个 Python-PostgreSQL 数据库适配器 (psycopg2)。这个包帮助 Django 与 PostgreSQL 数据库交互。您可以使用以下命令安装 psycopg2 包。确保PATH环境变量中包含PostgreSQL bin文件夹,并在执行命令前激活虚拟环境。

现在,您需要在 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 包。我们将为 CRUD 操作编写 HTTPie 命令。您可以激活虚拟环境并运行以下命令

现在,让我们创建模型和序列化程序。

创建模型和序列化程序

在进入基于类的视图之前,让我们创建一个模型及其序列化程序,这有助于演示基于函数的视图的工作。

创建模型

在 Django 中,模型是以面向对象的方式处理数据库的类。每个模型类引用一个数据库表,模型类中的每个属性引用一个数据库列。在这里,我们将在 Django 中创建一个简单的 Transformer 模型,其中存储了 Transformer字符名称、替代模式、描述以及字符是生是死。我们的 Transformer 实体需要以下属性:

  • 姓名
  • 交替模式
  • 描述

在 RESTFul Web 服务中,每个资源都使用唯一的 URL 访问,这意味着每个员工都有其唯一的 URL。并且,模型的每个方法都由一个 HTTP Verb 和 Scope 组成。

HTTP VerbScopeSemanticsURL
GETTransformerRetrieve a single transformerhttp://localhost:8000/transformers/{id}/
GETCollection of TransformerRetrieve all transformers in the collectionhttp://localhost:8000/transformers/
POSTCollection of TransformerCreate a new transformer in the collectionhttp://localhost:8000/transformers/
PUTTransformerUpdate an existing transformerhttp://localhost:8000/transformers/{id}/
PATCHTransformerPartially update an existing transformerhttp://localhost:8000/transformers/{id}/
DELETETransformerDelete an existing transformerhttp://localhost:8000/transformers/{id}/

让我们进入 Django 中 Transformer 模型的实现。您可以使用以下代码替换 models.py Python文件:

Python3
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


Python3
from rest_framework import serializers
from transformers.models import Transformer
  
class TransformerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Transformer
        fields = "__all__"


Python3
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)


Python3
from django.urls import path
from transformers import views
  
urlpatterns = [
    path('transformers/',
         views.transformer_list,
         name = 'employee-list'),
    path('transformers//',
         views.transformer_detail,
         name = 'employee-detail'),
]


Python3
from django.contrib import admin
from django.urls import path, include
  
urlpatterns = [
    path('', include('transformers.urls')),
]


Python3
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 模型是 django.db.models.Model 类的子类,定义了属性和 Meta 内部类。它有一个排序属性,可以根据名称按升序对结果进行排序。让我们使用以下命令进行初始迁移(确保您在 settings.py Python文件的 INSTALLED_APPS 中定义了您的应用程序('transformers.apps.TransformersConfig'))

接下来,使用以下命令应用所有生成的迁移:

应用生成的迁移将在数据库中创建一个 Transformer 表。

创建序列化程序

在您的应用程序(转换器)文件夹中创建一个新的 serializers.py Python文件并添加以下代码:

蟒蛇3

from rest_framework import serializers
from transformers.models import Transformer
  
class TransformerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Transformer
        fields = "__all__"

在这里,我们创建了一个继承自 rest_framework.serializers.ModelSerializer 的 TransformerSerializer 类。如果您想深入了解不同类型的序列化,您可以查看 Django REST Framework (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 文件中添加以下代码。

蟒蛇3

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)

让我们评估一下代码。这里我们有两个函数。

  • 变压器列表()
  • 变压器细节()

变压器列表()

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 参数序列化多个 Transformer 实例。

如果动词是POST ,代码会创建一个新的转换器。这里的数据是在编写 HTTP 请求时以 JSON 格式提供的。

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()函数能够处理三个 HTTP 动词——GET、PUT 和 DELETE。如果动词是 GET,则代码根据主键检索单个转换器实例。如果动词是 PUT,则代码更新实例并将其保存到数据库中,如果是 DELETE,则代码从数据库中删除实例。

下一步,我们需要将 URL 路由到视图。您需要在 app (transformers) 文件夹中创建一个新的Python文件名 urls.py 并添加以下代码。

蟒蛇3

from django.urls import path
from transformers import views
  
urlpatterns = [
    path('transformers/',
         views.transformer_list,
         name = 'employee-list'),
    path('transformers//',
         views.transformer_detail,
         name = 'employee-detail'),
]

现在,让我们更新根 URL 配置。您可以将以下代码添加到 urls.py 文件(与 settings.py 文件相同的文件夹)。

蟒蛇3

from django.contrib import admin
from django.urls import path, include
  
urlpatterns = [
    path('', include('transformers.urls')),
]

现在是时候编写和发送 HTTP 请求来测试我们的视图了。

让我们创建一个新条目。 HTTPie 命令是:

输出

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"
}

分享命令提示符截图供大家参考

让我们将转换器的活动字段 (id=7) 更新为 True。 HTTPie 命令是:

输出

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/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_viewrest_framework.decorators模块中的装饰器,它是所有 Django REST 框架视图的基类。我们可以在@api_view装饰器中提供允许的 HTTP 动词作为 http_methods_names 参数(字符串列表)。如果 RESTful Web 服务收到不受支持的 HTTP 动词,装饰器将返回适当的响应,而不是 Django 中的意外错误。

@api_view(http_method_names=['GET'])

您可以使用以下代码替换 views.py 文件。

蟒蛇3

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/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/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 框架检查 data 属性中的 Content-Type 标头,并确定解析请求的确切解析器。它还调用rest_framework.negotiation.DefaultContentNegotiation类来为请求选择合适的渲染器。

现在,是时候编写和发送不同的 HTTP 请求了。让我们创建一个新条目。 HTTPie 命令是

输出

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"
}

分享命令提示符截图

让我们将活动字段值更新为 True。 HTTPie 命令是:

输出

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/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 动词、解析器和渲染器。