Regis da Silva
Publicado em:

Sun 24 April 2016

←Home

Django Rest Framework Serialization

Eu resolvi estudar um pouco mais de DRF depois do tutorial do Hugo Brilhante na Python Brasil 11.

Este artigo foi atualizado em 14 de Fevereiro de 2018.

Este artigo está usando:

  • Python 3.5.2
  • Django 2.0.2
  • djangorestframework 3.7.7

Favor clonar o projeto do GitHub, favor ler o README para instalação.

Obs: se você não sabe Django sugiro que leia este tutorial antes.

Obs: Tem coisas que é melhor nem traduzir. ;)

Pra quem não sabe, para usar API Web usamos REST, no caso, Django Rest Framework, framework web do Django.

Nota: este tutorial não é exatamente igual ao do Hugo, é baseado nele. E baseado também em Tutorial 1: Serialization.

Então para criar a API, no meu caso, eu usei:

  • Ambiente: .venv
  • Projeto: myproject
  • App: core
  • Model: Person
  • Fields: first_name, last_name, email, active (boolean), created

img

Configurando um novo ambiente

$ python3 -m venv .venv
$ source .venv/bin/activate
$ mkdir drf; cd drf
$ pip install django==2.0.2 djangorestframework==3.7.7
$ pip install django-filter drf-nested-routers
$ pip freeze > requirements.txt
$ django-admin.py startproject myproject .
$ python manage.py startapp core

Veja o meu requirements.txt

dj-database-url==0.4.2
Django==2.0.2
django-extensions==1.9.9
django-filter==1.1.0
djangorestframework==3.7.7
drf-nested-routers==0.90.0
python-decouple==3.1

Step-0 Projeto inicial

Abra o arquivo settings.py e em INSTALLED_APPS acrescente

INSTALLED_APPS = (
    ...
    'rest_framework',
    'core',
)

Step-1 Serializer

models.py: Criando o modelo Person

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    email = models.EmailField(null=True, blank=True)
    active = models.BooleanField(default=True)
    created = models.DateTimeField(auto_now_add=True, auto_now=False)

    class Meta:
        ordering = ['first_name']
        verbose_name = u'pessoa'
        verbose_name_plural = u'pessoas'

    def __str__(self):
        return self.first_name + " " + self.last_name

    full_name = property(__str__)

serializers.py: Criando PersonSerializer

Precisamos proporcionar uma forma de serialização e desserialização das instâncias de person em uma representação JSON.

$ cd core/
$ touch serializers.py

Edite

from rest_framework import serializers
from core.models import Person

class PersonSerializer(serializers.Serializer):
    pk = serializers.IntegerField(read_only=True)
    first_name = serializers.CharField(max_length=30)
    last_name = serializers.CharField(max_length=30)
    email = serializers.EmailField()
    active = serializers.BooleanField(default=True)
    created = serializers.DateTimeField()

    def create(self, validated_data):
        """
        Create and return a new `Person` instance, given the validated data.
        :param validated_data:
        """
        return Person.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        Update and return an existing `Person` instance, given the validated data.
        """

        instance.first_name = validated_data.get(
            'first_name', instance.first_name)
        instance.last_name = validated_data.get(
            'last_name', instance.last_name)
        instance.email = validated_data.get('email', instance.email)
        instance.save()
        return instance

A primeira parte da classe define os campos que serão serializados. Os métodos create() e update() criam e atualizam as instâncias, respectivamente, quando chamados.

Uma classe de serialização é similar a uma classe Form do Django, e inclui validações similares para os campos, tais como required, max_length e default.

Fazendo a migração

$ ./manage.py makemigrations core
$ ./manage.py migrate

Trabalhando com a serialização

Abra o shell do Django.

$ ./manage.py shell

Primeiro vamos criar uma pessoa.

>>> from core.models import Person
>>> from core.serializers import PersonSerializer
>>> from rest_framework.renderers import JSONRenderer
>>> from rest_framework.parsers import JSONParser

>>> person = Person(first_name='Paul', last_name='Van Dyke', email='paul@email.com')
>>> person.save()

>>> person = Person(first_name='Regis', last_name='Santos', email='regis@email.com')
>>> person.save()

Agora que já temos alguns dados podemos ver a serialização da última instância.

>>> serializer = PersonSerializer(person)
>>> serializer.data
# {'pk': 2, 'first_name': 'Regis', 'created': '2015-11-15T03:20:25.084990Z', 'last_name': 'Santos', 'email': 'regis@email.com', 'active': True}

Neste ponto nós traduzimos a instância do modelo em tipos de dados nativos do Python. Para finalizar o processo de serialização nós vamos renderizar os dados em json.

>>> content = JSONRenderer().render(serializer.data)
>>> content
# b'{"pk":2,"first_name":"Regis","last_name":"Santos","email":"regis@email.com","active":true,"created":"2015-11-15T03:20:25.084990Z"}'

A desserialização é similar.

>>> from core.models import Person
>>> from core.serializers import PersonSerializer
>>> from rest_framework.renderers import JSONRenderer
>>> from rest_framework.parsers import JSONParser
>>> from django.utils.six import BytesIO

>>> person = Person.objects.get(pk=1)
>>> serializer = PersonSerializer(person)
>>> content = JSONRenderer().render(serializer.data)
>>> stream = BytesIO(content)
>>> data = JSONParser().parse(stream)
>>> serializer = PersonSerializer(data=data)
>>> serializer.is_valid()
# True
>>> serializer.validated_data
# OrderedDict([('first_name', 'Paul'), ('last_name', 'Van Dyke'), ('email', 'paul@email.com'), ('active', True)])

Step-2 ModelSerializer

Nossa classe PersonSerializer está replicando um monte de informações que está contido no modelo Person.

Da mesma forma que o Django fornece Form e ModelForm, REST framework inclui as classes Serializer e ModelSerializer.

Vamos refatorar nosso arquivo serializers.py, que agora ficará assim:

from rest_framework import serializers
from core.models import Person

class PersonSerializer(serializers.ModelSerializer):

    class Meta:
        model = Person
        fields = ('pk', 'first_name', 'last_name','email', 'active', 'created')

Uma propriedade legal que a serialização tem é que você pode inspecionar todos os campos em uma instância serializer, imprimindo sua representação. Abra o shell do Django.

$ ./manage.py shell
>>> from core.serializers import PersonSerializer
>>> serializer = PersonSerializer()
>>> print(repr(serializer))
# PersonSerializer():
#     pk = IntegerField(label='ID', read_only=True)
#     first_name = CharField(max_length=30)
#     last_name = CharField(max_length=30)
#     email = EmailField(allow_blank=True, allow_null=True, max_length=254, required=False)
#     active = BooleanField(required=False)
#     created = DateTimeField(read_only=True)

É importante lembrar que as classes ModelSerializer não faz nenhuma mágica, são simplesmente um atalho para a criação das classes de serialização:

  • Os campos são definidos automaticamente.
  • Os métodos create() e update() são implementados por padrão de uma forma simplificada.

views.py: Criando views regulares usando nosso Serializer

Vamos criar uma view simples para visualizar os dados em json.

Edite o arquivo views.py

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from core.models import Person
from core.serializers import PersonSerializer

class JSONResponse(HttpResponse):
    """
    An HttpResponse that renders its content into JSON.
    """

    def __init__(self, data, **kwargs):
        content = JSONRenderer().render(data)
        kwargs['content_type'] = 'application/json'
        super(JSONResponse, self).__init__(content, **kwargs)

A raiz da nossa API será uma lista de todas as pessoas, ou podemos criar uma pessoa nova.

@csrf_exempt
def person_list(request):
    """
    List all persons, or create a new person.
    """
    if request.method == 'GET':
        persons = Person.objects.all()
        serializer = PersonSerializer(persons, many=True)
        return JSONResponse(serializer.data)

    elif request.method == 'POST':
        data = JSONParser().parse(request)
        serializer = PersonSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return JSONResponse(serializer.data, status=201)
        return JSONResponse(serializer.errors, status=400)

Note que nós queremos usar o POST mas não temos o CSRF Token, por isso usamos o @csrf_exempt.

Também vamos precisar visualizar os detalhes de cada pessoa. Assim podemos recuperar, atualizar ou deletar cada registro.

@csrf_exempt
def person_detail(request, pk):
    """
    Retrieve, update or delete a person.
    """
    try:
        person = Person.objects.get(pk=pk)
    except Person.DoesNotExist:
        return HttpResponse(status=404)

    if request.method == 'GET':
        serializer = PersonSerializer(person)
        return JSONResponse(serializer.data)

    elif request.method == 'PUT':
        data = JSONParser().parse(request)
        serializer = PersonSerializer(person, data=data)
        if serializer.is_valid():
            serializer.save()
            return JSONResponse(serializer.data)
        return JSONResponse(serializer.errors, status=400)

    elif request.method == 'DELETE':
        person.delete()
        return HttpResponse(status=204)

Agora, vamos criar as urls. Crie um novo arquivo core/urls.py.

from django.urls import path
from core import views

urlpatterns = [
    path('persons/', views.PersonList.as_view()),
    path('persons/<int:pk>/', views.PersonDetail.as_view()),
]

E acrescente a seguinte linha em myproject/urls.py.

from django.urls import include, path
from django.contrib import admin

urlpatterns = [
    path('', include('core.urls')),
    path('admin/', admin.site.urls),
]

Instalando httpie

Podemos usar o curl, mas o httpie é mais amigável, e escrito em Python.

$ sudo pip install httpie

Vamos usar o httpie digitando

$ http http://127.0.0.1:8000/persons/

HTTP/1.0 200 OK
Content-Type: application/json
Date: Sun, 15 Nov 2015 03:24:44 GMT
Server: WSGIServer/0.2 CPython/3.4.3
X-Frame-Options: SAMEORIGIN

[
    {
        "active": true, 
        "created": "2015-11-15T03:20:24.938378Z", 
        "email": "paul@email.com", 
        "first_name": "Paul", 
        "last_name": "Van Dyke", 
        "pk": 1
    }, 
    {
        "active": true, 
        "created": "2015-11-15T03:20:25.084990Z", 
        "email": "regis@email.com", 
        "first_name": "Regis", 
        "last_name": "Santos", 
        "pk": 2
    }
]

Veja os detalhes

$ http http://127.0.0.1:8000/persons/1/

Atenção: se você receber erro 301, muito provavelmente é porque você esqueceu da barra / no final da url.

Como seria em curl?

Assim

$ curl http://127.0.0.1:8000/persons/

[{"pk":1,"first_name":"Paul","last_name":"Van Dyke","email":"paul@email.com","active":true,"created":"2015-11-15T03:20:24.938378Z"},{"pk":2,"first_name":"Regis","last_name":"Santos","email":"regis@email.com","active":true,"created":"2015-11-15T03:20:25.084990Z"}]

GitHub: Se você quiser pode olhar meu GitHub, mas terá que ver os commits para ver os passos.

Topo
comments powered by Disqus