Eu resolvi estudar um pouco mais de DRF depois do tutorial do Hugo Brilhante na Python Brasil 11.
Obs: se você não sabe Django sugiro que leia este tutorial antes.
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
Clonando o projeto
$ git clone https://github.com/rg3915/drf.git $ cd drf $ python3 -m venv .venv $ source .venv/bin/activate $ pip install -r requirements.txt $ python contrib/env_gen.py $ python manage.py migrate
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 $ python contrib/env_gen.py
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.pyEdite
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()eupdate()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.conf.urls import url from core import views urlpatterns = [ url(r'^persons/$', views.person_list), url(r'^persons/(?P<pk>[0-9]+)/$', views.person_detail), ]
E acrescente a seguinte linha em myproject/urls.py.
urlpatterns = [ url(r'^', include('core.urls')), url(r'^admin/', include(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.
