Django REST framework 中文教程1:序列化

創建環境html

在咱們作任何事情以前,咱們將使用virtualenv建立一個新的虛擬環境。這將確保咱們的包配置與咱們正在開展的任何其餘項目保持良好的隔離。python

virtualenv
envsource env/bin/activate

如今咱們在一個virtualenv環境中,咱們能夠安裝咱們的包的要求。git

pip install django
pip install djangorestframework
pip install pygments  # 咱們將使用這個讓代碼突出顯示

注意:要隨時退出virtualenv環境,只需鍵入deactivate。有關更多信息,請參閱virtualenv文檔github

入門shell

好的,咱們準備好得到編碼。要開始,咱們來建立一個新的項目來處理。數據庫

 

cd ~
django-admin.py startproject tutorial
cd tutorial

  

一旦完成,咱們能夠建立一個咱們將用來建立一個簡單的Web API的應用程序。django

 

python manage.py startapp snippets

  

咱們須要添加咱們的新snippets應用和rest_framework應用INSTALLED_APPS。咱們來編輯tutorial/settings.py文件:編程

INSTALLED_APPS = (
    ...
    'rest_framework',
    'snippets.apps.SnippetsConfig',
)

  

請注意,若是你使用的Django <1.9,則須要更換snippets.apps.SnippetsConfig有snippets。json

好的,咱們準備好了。瀏覽器

建立一個可使用的模型

爲了本教程的目的,咱們將首先建立一個Snippet用於存儲代碼片斷的簡單模型。繼續編輯snippets/models.py文件。注意:良好的編程實踐包括評論。雖然您將在本教程代碼的存儲庫版本中找到它們,但咱們在此忽略了它們,專一於代碼自己。

from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles

LEXERS = [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(auto_now_add=True)
    title = models.CharField(max_length=100, blank=True, default='')
    code = models.TextField()
    linenos = models.BooleanField(default=False)
    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
    style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)

    class Meta:
        ordering = ('created',)

  

咱們還須要爲咱們的代碼段模型建立初始遷移,並首次同步數據庫。

 

python manage.py makemigrations snippets
python manage.py migrate

  

建立一個Serializer類

咱們須要開始使用Web API的第一件事是提供一種將代碼段實例序列化和反序列化爲表示形式的方式json。咱們能夠經過聲明與Django表單很是類似的序列化器來作到這一點。在snippets命名的目錄中建立一個文件,serializers.py並添加如下內容。

from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES


class SnippetSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    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):
        """
        Create and return a new `Snippet` instance, given the validated data.
        """
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        Update and return an existing `Snippet` instance, given the validated data.
        """
        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

  

使用Serializer

在咱們進一步瞭解以前,咱們將熟悉使用咱們的新的Serializer類。讓咱們進入Django shell。

python manage.py shell

  

如今,咱們將如下幾個方法導入進去後,能夠建立一些代碼片斷來測試如下。

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser

# 建立數據
snippet = Snippet(code='foo = "bar"\n')
snippet.save()

snippet = Snippet(code='print "hello, world"\n')
snippet.save()

  

咱們如今有幾個片斷實例能夠嘗試。咱們來看看序列化這些實例之一。

 

###
 該代碼是把剛剛保存的數據snippet對象,通過序列化保存成一個字典
     snippet = Snippet(code='print "hello, world"\n')
     snippet.save()
     
###
serializer = SnippetSerializer(snippet)
serializer.data
# {'id': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}

  

此時,咱們將模型實例轉換爲Python的數據類型。要完成序列化過程須要咱們將數據渲染成json。

 

# 將字典轉換成json格式
content = JSONRenderer().render(serializer.data)
content
# '{"id": 2, "title": "", "code": "print \\"hello, world\\"\\n", "linenos": false, "language": "python", "style": "friendly"}'

  

反序列化是相似的。首先咱們將一個流解析成Python支持數據類型。

# 將json轉換成字典格式
from django.utils.six import BytesIO

stream = BytesIO(content)
data = JSONParser().parse(stream)

  

而後咱們將這些本機數據類型恢復到徹底填充的對象實例中

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與表單的使用狀況。當咱們開始編寫使用咱們的序列化程序的視圖時,類似性將變得更加明顯。

咱們也能夠序列化查詢集而不是模型實例。爲此,咱們只many=True須要爲serializer參數添加一個標誌。

serializer = SnippetSerializer(Snippet.objects.all(), many=True)
serializer.data
# [OrderedDict([('id', 1), ('title', u''), ('code', u'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', u''), ('code', u'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', u''), ('code', u'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]

  

使用ModelSerializers

SnippetSerializer類複製Snippet模型中大量信息。若是咱們能夠保持咱們的代碼更簡潔,那就更好。

與Django提供的Form類和ModelForm類同樣,REST框架包括Serializer類和ModelSerializer類。

咱們來使用ModelSerializer該類重構咱們的serializer 。再次打開該文件snippets/serializers.py,並用SnippetSerializer如下代碼替換該類。

class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = ('id', 'title', 'code', 'linenos', 'language', 'style')

  

serializer具備的一個不錯的屬性是能夠經過打印其表示來檢查序列化器實例中的全部字段。打開Django shell python manage.py 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')...

  

重要的是要記住,ModelSerializer類不會作任何特別神奇的事情,它們只是建立序列化器類的快捷方式:

  • 一組自動肯定的字段。
  • 簡單的默認實現create()和update()方法。

使用咱們的Serializer編寫正常的Django視圖

咱們來看看咱們如何使用咱們的新的Serializer類編寫一些API視圖。目前咱們不會使用任何REST框架的其餘功能,咱們只需將視圖編寫爲常規的Django視圖。

編輯snippets/views.py文件,並添加如下內容。

from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer

  

咱們API原本是一個視圖函數,支持監聽全部已有的代碼或者新建的代碼片斷

@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()
            # serializer.data 數據建立成功後全部數據
            return JsonResponse(serializer.data, status=201)
        # serializer.errors 錯誤信息
        return JsonResponse(serializer.errors, status=400)

  

請注意,由於咱們但願可以從不具備CSRF令牌的客戶端對此視圖進行POST,所以咱們須要將視圖標記爲csrf_exempt。這不是你一般想要作的事情,REST框架視圖實際上比這更有明確的行爲,但它如今將爲咱們的目的而作。

咱們還須要一個與單個代碼片斷對應的視圖,並可用於檢索,更新或刪除該片斷

@csrf_exemptdef 
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)

  

最後咱們須要把這些觀點鏈接起來。建立snippets/urls.py文件:

from django.conf.urls import url
from snippets import views

urlpatterns = [
    url(r'^snippets/$', views.snippet_list),
    url(r'^snippets/(?P<pk>[0-9]+)/$', views.snippet_detail),
]

  

咱們還須要在tutorial/urls.py文件中鏈接根urlconf ,以包含咱們的片斷應用程序的URL。

from django.conf.urls import url, include

urlpatterns = [
    url(r'^', include('snippets.urls')),
]

  

值得注意的是,咱們目前尚未正確處理好幾個邊緣案例。若是咱們發送格式錯誤json,或者若是使用視圖不處理的方法發出請求,不過,按照如今這樣作,那麼咱們最終會出現500「服務器錯誤」響應。

測試咱們在Web API上的第一次嘗試

如今咱們能夠啓動一個服務咱們的代碼片斷的示例服務器。

退出shell...

quit()

並啓動Django的開發服務器。

python manage.py runserver

Validating models...

0 errors found
Django version 1.11, using settings 'tutorial.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

在另外一個終端窗口中,咱們能夠測試服務器。

咱們可使用curlhttpie來測試咱們的API 。Httpie是用Python編寫的用戶友好的http客戶端。咱們來安裝

您可使用pip安裝httpie:

pip install httpie

  

最後,咱們能夠獲得全部片斷的列表:

http http://127.0.0.1:8000/snippets/HTTP/1.1 200 OK...[
  {
    "id": 1,
    "title": "",
    "code": "foo = \"bar\"\n",
    "linenos": false,
    "language": "python",
    "style": "friendly"
  },
  {
    "id": 2,
    "title": "",
    "code": "print \"hello, world\"\n",
    "linenos": false,
    "language": "python",
    "style": "friendly"
  }]

  

或者咱們能夠經過引用其id來獲取特定的代碼段:

http http://127.0.0.1:8000/snippets/2/

HTTP/1.1 200 OK
...
{
  "id": 2,
  "title": "",
  "code": "print \"hello, world\"\n",
  "linenos": false,
  "language": "python",
  "style": "friendly"
}

  

一樣,您能夠經過在Web瀏覽器中訪問這些URL來顯示相同的json。

咱們如今在哪

咱們到目前爲止還行,咱們有一個相似於Django的Forms API和一些常規Django視圖的序列化API。

咱們的API視圖目前沒有特別的特別之處,除了提供json響應外,還有一些錯誤處理咱們仍然想要清理的邊緣狀況,但它是一個運行良好的Web API。

咱們將在本教程的第2部分中看到咱們如何開始改進事情。

Django REST FrameWork中文文檔目錄:

Django REST FrameWork 中文教程1:序列化

Django REST FrameWork 中文教程2:請求和響應

Django REST FrameWork 中文教程3:基於類的視圖

Django REST FrameWork 中文教程4:驗證和權限

Django REST FrameWork 中文教程5:關係和超連接API

Django REST FrameWork 中文教程6: ViewSets&Routers

Django REST FrameWork 中文教程7:模式和客戶端庫

相關文章
相關標籤/搜索