開發(fā)網(wǎng)站開發(fā)工程師附近的教育培訓(xùn)機(jī)構(gòu)有哪些
概述
本教程將介紹如何創(chuàng)建一個簡單的粘貼板代碼高亮 Web API。在此過程中,它將介紹構(gòu)成 REST 框架的各種組件,讓你全面了解所有組件是如何組合在一起的。
本教程相當(dāng)深入,因此在開始學(xué)習(xí)之前,你可能需要先吃一塊餅干,再喝一杯你最喜歡的啤酒。如果你只想快速了解概況,那就去看看快速入門文檔吧。
注意:本文代碼基于入門教程的代碼繼續(xù),如果有不明白的地方,建議先看入門教程的代碼。
安裝依賴
# 之前已經(jīng)安裝的
pip install django
pip install djangorestframework# 需要新安裝的
pip install pygments
創(chuàng)建新的應(yīng)用
完成后,我們就可以創(chuàng)建一個應(yīng)用程序,用來創(chuàng)建一個簡單的 Web API。
python manage.py startapp snippets
注冊新的應(yīng)用:
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','quickstart','snippets','rest_framework',
]
創(chuàng)建模型
在本教程中,我們將首先創(chuàng)建一個用于存儲代碼片段的簡單片段模型。繼續(xù)編輯 snippets/models.py 文件。注:良好的編程實(shí)踐包括注釋。雖然您可以在本教程代碼的存儲庫版本中找到注釋,但我們在此省略了它們,以專注于代碼本身。
from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_stylesLEXERS = [item for item in get_all_lexers() if item[1]]
# 編程語言
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
# 代碼樣式
STYLE_CHOICES = sorted([(item, item) for item in get_all_styles()])class Snippet(models.Model):"""代碼片段"""created = models.DateTimeField(verbose_name="創(chuàng)建時間", auto_now_add=True)title = models.CharField(verbose_name="標(biāo)題", max_length=100, blank=True, default='')code = models.TextField(verbose_name="代碼")linenos = models.BooleanField(verbose_name="開啟行號", default=False)language = models.CharField(verbose_name="編程語言", choices=LANGUAGE_CHOICES, default='python', max_length=100)style = models.CharField(verbose_name="代碼樣式", choices=STYLE_CHOICES, default='friendly', max_length=100)class Meta:# 根據(jù)創(chuàng)建時間升序ordering = ['created']
我們還需要為片段模型創(chuàng)建初始遷移,并首次同步數(shù)據(jù)庫。
python manage.py makemigrations snippets
python manage.py migrate snippets
創(chuàng)建序列化類
要開始使用 Web API,我們首先需要提供一種將片段實(shí)例序列化和反序列化為 json 等表示形式的方法。我們可以通過聲明與 Django 的表單非常相似的序列化器來實(shí)現(xiàn)這一點(diǎn)。在片段目錄中創(chuàng)建一個名為 serializers.py 的文件,并添加以下內(nèi)容。
from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICESclass SnippetSerializer(serializers.Serializer):"""代碼片段序列化類"""# 主鍵id = serializers.IntegerField(read_only=True)# 標(biāo)題title = serializers.CharField(required=False, allow_blank=True, max_length=100)# 代碼code = serializers.CharField(style={'base_template': 'textarea.html'})# 是否開啟行號linenos = serializers.BooleanField(required=False)# 編程語言language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')# 代碼樣式style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')def create(self, validated_data):"""根據(jù)驗證數(shù)據(jù)創(chuàng)建并返回一個新的 `Snippet` 實(shí)例。"""return Snippet.objects.create(**validated_data)def update(self, instance, validated_data):"""根據(jù)驗證數(shù)據(jù)更新并返回現(xiàn)有的 `Snippet` 實(shí)例。"""instance.title = validated_data.get('title', instance.title)instance.code = validated_data.get('code', instance.code)instance.linenos = validated_data.get('linenos', instance.linenos)instance.language = validated_data.get('language', instance.language)instance.style = validated_data.get('style', instance.style)instance.save()return instance
序列化器類的第一部分定義了序列化/反序列化的字段。create() 和 update() 方法定義了在調(diào)用 serializer.save() 時如何創(chuàng)建或修改完整的實(shí)例。
序列化器類與 Django 表單類非常相似,包括各種字段的類似驗證標(biāo)志,如 required、max_length 和 default。
字段標(biāo)志還可以控制序列化器在某些情況下的顯示方式,例如渲染為 HTML 時。上面的 {‘base_template’: ‘textarea.html’} 標(biāo)志相當(dāng)于在 Django 表單類上使用 widget=widgets.Textarea。這對于控制如何顯示可瀏覽 API 尤為有用,我們將在本教程稍后部分看到這一點(diǎn)。
實(shí)際上,我們還可以通過使用 ModelSerializer 類來節(jié)省時間,稍后我們將看到這一點(diǎn),但現(xiàn)在我們將保持序列化器定義的明確性。
序列化類的用法
在進(jìn)一步了解之前,我們先熟悉一下如何使用新的序列化器類。讓我們進(jìn)入 Django shell。
python manage.py shell
好了,在我們完成一些導(dǎo)入后,讓我們創(chuàng)建幾個代碼片段來使用。
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParsersnippet = Snippet(code='foo = "bar"\n')
snippet.save()snippet = Snippet(code='print("hello, world")\n')
snippet.save()
現(xiàn)在,我們有了幾個片段實(shí)例可以使用。讓我們看看如何將其中一個實(shí)例序列化。
serializer = SnippetSerializer(snippet)
serializer.data
# {'id': 2, 'title': '', 'code': 'print("hello, world")\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}
至此,我們已將模型實(shí)例轉(zhuǎn)化為 Python 本地數(shù)據(jù)類型。為了最終完成序列化過程,我們將數(shù)據(jù)渲染為 json。
content = JSONRenderer().render(serializer.data)
content
# b'{"id": 2, "title": "", "code": "print(\\"hello, world\\")\\n", "linenos": false, "language": "python", "style": "friendly"}'
反序列化與此類似。首先,我們將數(shù)據(jù)流解析為 Python 原生數(shù)據(jù)類型…
import iostream = io.BytesIO(content)
data = JSONParser().parse(stream)
…然后我們將這些本地數(shù)據(jù)類型還原成一個完全填充的對象實(shí)例。
serializer = SnippetSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# OrderedDict([('title', ''), ('code', 'print("hello, world")\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
serializer.save()
# <Snippet: Snippet object>
請注意,API 與使用表單是多么相似。當(dāng)我們開始編寫使用序列化器的視圖時,這種相似性會變得更加明顯。
我們還可以序列化查詢集而不是模型實(shí)例。為此,我們只需在序列化器參數(shù)中添加 many=True 標(biāo)志即可。
serializer = SnippetSerializer(Snippet.objects.all(), many=True)
serializer.data
# [OrderedDict([('id', 1), ('title', ''), ('code', 'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', ''), ('code', 'print("hello, world")\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', ''), ('code', 'print("hello, world")'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]
使用模型序列化類
我們的 SnippetSerializer 類正在復(fù)制 Snippet 模型中包含的大量信息。如果我們能讓代碼更簡潔一些就更好了。
就像 Django 提供表單類和 ModelForm 類一樣,REST 框架也包括序列化器類和模型序列化器類。
讓我們看看如何使用 ModelSerializer 類重構(gòu)序列化器。再次打開文件 snippets/serializers.py,用以下代碼替換 SnippetSerializer 類。
class SnippetSerializer(serializers.ModelSerializer):class Meta:model = Snippetfields = ['id', 'title', 'code', 'linenos', 'language', 'style']
序列化器有一個很好的特性,那就是你可以通過打印序列化器實(shí)例的表示來檢查它的所有字段。使用 python manage.py shell 打開 Django shell,然后嘗試以下操作:
from snippets.serializers import SnippetSerializer
serializer = SnippetSerializer()
print(repr(serializer))
# SnippetSerializer():
# id = IntegerField(label='ID', read_only=True)
# title = CharField(allow_blank=True, max_length=100, required=False)
# code = CharField(style={'base_template': 'textarea.html'})
# linenos = BooleanField(required=False)
# language = ChoiceField(choices=[('Clipper', 'FoxPro'), ('Cucumber', 'Gherkin'), ('RobotFramework', 'RobotFramework'), ('abap', 'ABAP'), ('ada', 'Ada')...
# style = ChoiceField(choices=[('autumn', 'autumn'), ('borland', 'borland'), ('bw', 'bw'), ('colorful', 'colorful')...
請務(wù)必記住,ModelSerializer 類并沒有什么特別神奇的功能,它們只是創(chuàng)建序列化類的快捷方式:
- 自動確定的字段集。
- create() 和 update() 方法的簡單默認(rèn)實(shí)現(xiàn)。
完整代碼如下:
from rest_framework import serializers
from snippets.models import Snippetclass SnippetSerializer(serializers.ModelSerializer):class Meta:model = Snippetfields = ['id', 'title', 'code', 'linenos', 'language', 'style']
使用我們的序列化器編寫常規(guī) Django 視圖
讓我們看看如何使用新的 Serializer 類編寫 API 視圖。目前,我們不會使用 REST 框架的任何其他功能,我們只會把視圖寫成普通的 Django 視圖。
編輯 snippets/views.py 文件,添加以下內(nèi)容。
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParser
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
我們 API 的根將是一個視圖,它支持列出所有現(xiàn)有片段或創(chuàng)建新片段。
@csrf_exempt
def snippet_list(request):"""List all code snippets, or create a new snippet."""if request.method == 'GET':snippets = Snippet.objects.all()serializer = SnippetSerializer(snippets, many=True)return JsonResponse(serializer.data, safe=False)elif request.method == 'POST':data = JSONParser().parse(request)serializer = SnippetSerializer(data=data)if serializer.is_valid():serializer.save()return JsonResponse(serializer.data, status=201)return JsonResponse(serializer.errors, status=400)
請注意,由于我們希望從沒有 CSRF 標(biāo)記的客戶端向該視圖進(jìn)行 POST,因此需要將視圖標(biāo)記為 csrf_exempt。這并不是你通常想要做的事情,REST 框架視圖實(shí)際上使用了比這更合理的行為,但對于我們現(xiàn)在的目的來說,這已經(jīng)足夠了。
我們還需要一個與單個片段相對應(yīng)的視圖,用于檢索、更新或刪除片段。
@csrf_exempt
def snippet_detail(request, pk):"""Retrieve, update or delete a code snippet."""try:snippet = Snippet.objects.get(pk=pk)except Snippet.DoesNotExist:return HttpResponse(status=404)if request.method == 'GET':serializer = SnippetSerializer(snippet)return JsonResponse(serializer.data)elif request.method == 'PUT':data = JSONParser().parse(request)serializer = SnippetSerializer(snippet, data=data)if serializer.is_valid():serializer.save()return JsonResponse(serializer.data)return JsonResponse(serializer.errors, status=400)elif request.method == 'DELETE':snippet.delete()return HttpResponse(status=204)
完整代碼如下:
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParser
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer@csrf_exempt
def snippet_list(request):"""列出所有代碼片段,或創(chuàng)建一個新片段。"""# 查詢所有的代碼if request.method == 'GET':snippets = Snippet.objects.all()serializer = SnippetSerializer(snippets, many=True)return JsonResponse(serializer.data, safe=False)# 新增代碼elif request.method == 'POST':data = JSONParser().parse(request)serializer = SnippetSerializer(data=data)if serializer.is_valid():serializer.save()return JsonResponse(serializer.data, status=201)return JsonResponse(serializer.errors, status=400)@csrf_exempt
def snippet_detail(request, pk):"""檢索、更新或刪除代碼片段。"""try:snippet = Snippet.objects.get(pk=pk)except Snippet.DoesNotExist:return HttpResponse(status=404)# 根據(jù)ID獲取代碼if request.method == 'GET':serializer = SnippetSerializer(snippet)return JsonResponse(serializer.data)# 根據(jù)ID更新代碼elif request.method == 'PUT':data = JSONParser().parse(request)serializer = SnippetSerializer(snippet, data=data)if serializer.is_valid():serializer.save()return JsonResponse(serializer.data)return JsonResponse(serializer.errors, status=400)# 根據(jù)ID刪除代碼elif request.method == 'DELETE':snippet.delete()return HttpResponse(status=204)
配置路由
最后,我們需要將這些視圖連接起來。創(chuàng)建 snippets/urls.py 文件:
from django.urls import path
from snippets import viewsurlpatterns = [path('', views.snippet_list),path('<int:pk>/', views.snippet_detail),
]
我們還需要對 tutorial/urls.py 文件中的 urlconf 根目錄進(jìn)行布線,以包含我們的片段應(yīng)用程序的 URL。
from django.urls import include, path
from rest_framework import routersfrom quickstart import views# 創(chuàng)建子路由
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet)# 使用自動 URL 路由為我們的應(yīng)用程序接口布線。
# 此外,我們還提供了可瀏覽 API 的登錄 URL。
urlpatterns = [path('', include(router.urls)),path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),path('snippets/', include('snippets.urls')),
]urlpatterns += router.urls
值得注意的是,有幾種邊緣情況我們目前還沒有處理好。如果我們發(fā)送了畸形的 json,或者請求使用了視圖無法處理的方法,那么我們最終會收到 500 "服務(wù)器錯誤 "的響應(yīng)。不過,現(xiàn)在這樣也可以。
測試我們的接口
新增代碼片段:
(venv) PS D:\tmp\drf_demo> http http://127.0.0.1:8000/snippets/ code=abc
HTTP/1.1 201 Created
Content-Length: 98
Content-Type: application/json
Cross-Origin-Opener-Policy: same-origin
Date: Sun, 07 Jan 2024 04:30:19 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.12.0
X-Content-Type-Options: nosniff
X-Frame-Options: DENY{"code": "abc","id": 1,"language": "python","linenos": false,"style": "friendly","title": ""
}
請求所有代碼片段:
(venv) PS D:\tmp\drf_demo> http http://127.0.0.1:8000/snippets/
HTTP/1.1 200 OK
Content-Length: 100
Content-Type: application/json
Cross-Origin-Opener-Policy: same-origin
Date: Sun, 07 Jan 2024 04:30:35 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.12.0
X-Content-Type-Options: nosniff
X-Frame-Options: DENY[{"code": "abc","id": 1,"language": "python","linenos": false,"style": "friendly","title": ""}
](venv) PS D:\tmp\drf_demo>
總結(jié)
到目前為止,我們做得還不錯,我們已經(jīng)有了一個序列化 API,感覺與 Django 的表單 API 和一些常規(guī)的 Django 視圖非常相似。
目前,我們的 API 視圖除了提供 json 響應(yīng)外,并沒有做什么特別的事情,而且我們還想清理一些錯誤處理的邊緣情況,但這已經(jīng)是一個正常運(yùn)行的 Web API 了。
我們將在本教程的第二部分看看如何開始改進(jìn)。