En el anterior post – Introducción a Django REST Framework – creamos una simple API para crear, modificar, eliminar y listar encuestas. Hoy continuaremos con el mismo proyecto para ver como utilizar permisos y autentificarnos a la hora de conectarnos a la API.
La autentificación más básica
Para poder autentificarnos, necesitaremos un usuario, por lo que vamos a modificar nuestro modelo de datos (dado que el proyecto es tan simple que no estamos usando south, bastará con eliminar la base de datos db.sqlite3 para luego poder sincronizarla de nuevo –sincdb– con el nuevo modelo.
Modificamos el usuario (owner) en survey/models.py
from django.db import models
class Survey(models.Model):
owner = models.ForeignKey(‘auth.User’, related_name=’surveys’)
title = models.CharField(max_length=50)
question = models.CharField(max_length=300)
active = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
Incorporamos el usuario al serializer
Tenemos que crear un dato miembro en el serializer para nuestro usuario, así que modificamos api/serialiers.py:
from rest_framework import serializers
from survey.models import Survey, SurveyVotes
class SurveySerializer(serializers.ModelSerializer):
owner = serializers.Field(‘owner.username’)class Meta:
model = Survey
fields = (‘title’, ‘description’, ‘owner’, ‘recipients’, ‘options’, ‘active’)
Es importante que mantengamos owner como uno de los campos a serializar.
Creamos los permisos de uso
En nuestro ejemplo, vamos a considerar los siguientes usos:
1. Cuaquier usuario no autentificado, podrá leer las encuestras
2. Para crear una encuesta, será necesario estar autentificado
3. Para editar o borrar una encuesta, será necesario ser el propietario de la misma.
Con este objetivo, creamos el fichero api/permissions.py y añadimos el siguiente contenido:
from rest_framework.permissions import BasePermission, SAFE_METHODS
#SAFE_METHODS are get, post and headclass IsOwnerOrReadOnly(BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in SAFE_METHODS:
return True
return obj.owner == request.user
¿Qué hemos hecho?
Hemos creado una clase que hereda de BasePermission, donde hemos implementado método has_object_permission(), que devuelve True si el método se considera como seguro (permisos de lectura: GET, HEAD y OPTIONS). En caso contrario, comprueba si el usuario es el propietario del objeto, y si se da el caso afirmativo, también da el permiso.
Esta clase se deberá usar en las vistas para realizar la comprobación deseada.
Incorporamos la autentificación en las vistas
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView
from survey.models import Survey
from .permissions import IsOwnerOrReadOnly
from .serializers import SurveySerializerclass SurveyMixin(object):
queryset = Survey.objects.all()
serializer_class = SurveySerializer
permission_classes = (IsOwnerOrReadOnly,)def pre_save(self, obj):
obj.owner = self.request.userclass SurveyList(SurveyMixin, ListCreateAPIView):
passclass SurveyDetails(SurveyMixin, RetrieveUpdateDestroyAPIView):
pass
Como vemos, dotamos al SurveyMixin del que heredan nuestras vistas de un dato miembro permission_classes, donde debemos incluir una tupla con las clases de permisos que requieren nuestras vistas.
Es interesante ver como creamos el método pre_save() para que incorpore al usuario como propietario, lo que permitirá que IsOwnerOrReadOnly.has_object_permission() devuelva True cuando se crea una nueva encuesta.
Primeras pruebas
Hecho esto, podremos crear nuestra base de datos
(entornoSurvey)$ ./manage.py syncdb
y empezar a probar nuestros cambios con curl, PostMan, o lo que usemos.
Como detalle, indicar que para pasar un usuario y password en curl se utiliza la opción -u user:pswd, mientras que PostMan tiene una pestaña en la parte superior para proporcionar usuario y password (Basic Auth).
Por otro lado, si queremos probar varios usuarios para ver que pasa cuando intentamos acceder a una encuesta de la que no tenemos propiedad, podemos usar el comando:
(entornoSurvey)$ ./manage.py createsuperuser
Utilizando el front-end de Django REST Framework: Login
Como ya habrás visto, si te conectas mediante el navegador a la API, en lugar de obtener el resultado en JSON, lo que obtienes es una vista de información sobre la API, que te dice qué métodos puedes llamar concretamente para la URL que has introducido, e incluso te proporciona un formulario para realizar las peticiones.
Algo del estilo a lo que vemos en la imagen:
Si pruebas con la API que hemos desarrollado actualmente, por ejemplo, en http://127.0.0.1:8000/api/surveys/, deberías hechar de menos el login o usuario a la derecha de la barra superior. También deberías ver, si intentas crear una nueva survey desde el frontend, que no te dejará. Tienes que estar logueado.
Para solucionar esto, vamos a añadir URLs para que el front-end nos permita loguearnos, y además vamos a crear también la API para ver usuarios y sus encuestas.
Añadimos el UserSerializer vinculado a las surveys
Modificamos nuestro archivo api/serializers.py que debería quedar así:
from django.contrib.auth.models import User
from rest_framework import serializers
from survey.models import Surveyclass SurveySerializer(serializers.ModelSerializer):
owner = serializers.Field(source=’owner.username’)class Meta:
model = Survey
fields = (‘title’, ‘description’,’owner’, ‘recipients’, ‘options’, ‘active’)class UserSerializer(serializers.ModelSerializer):
#surveys es una relacion inversa en User, por lo que la declaramos explícitamente
surveys = serializers.PrimaryKeyRelatedField(many=True)class Meta:
model = User
fields = (‘id’, ‘username’, ‘surveys’)
Añadimos las vistas de User
En nuestro archivo api/views.py añadimos:
from rest_framework.generics import ListAPIView, ListCreateAPIView, RetrieveAPIView, RetrieveUpdateDestroyAPIView
from django.contrib.auth.models import User
from .serializers import SurveySerializer, UserSerializerclass UserList(ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializerclass UserDetail(RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
Como se podía intuir cuando hemos definido en Survey el owner como ForeignKey del modelo User de Django, vamos a utilizar directamente dicho modelo, por lo que no tocaremos el fichero models.py.
Acceso URL a nuestra API de users
Editamos el archivo api/urls.py para incluir las siguientes urls:
url(r’^users/$’, views.UserList.as_view(), name=’user_list’),
url(r’^users/(?P[0-9]+)$’, views.UserDetail.as_view(), name=’user_details’),
Acceso URL para loguearnos
Editamos el archivo de rutas principal survey_proj/urls.py para incluir las urls de Django REST framework:
url(r’^api-auth/’, include(‘rest_framework.urls‘,
namespace=’rest_framework‘)),
A partir de este momento, debería aparecernos a la derecha de la barra superior un enlace para loguearnos y desloguearnos, con lo que podremos usar el frontend para acceder a la api de forma autentificada, y por supuesto, ahora dipondremos también de una API para obtener una lista de usuarios, o información de un usuario concreto.