Framework: Django Rest Framework
Contents
Framework: Django Rest Framework#

Esta es la documentación que he recopilado para trabajar con Djang rest framework. Una extensión de django para crear API Rest.
Primeros Pasos#
Crear un proyecto Django#
Lo primero que vamos a hacer es crear un entorno virtual.
Luego instalamos Django
pip install django
Creamos un projecto en Django
django-admin startproject prueba_api
Ejecutamos el servidor para ver que se ha creado correctamente
python manage.py runserver
Instalar y configurar Django Rest Framework#
Empezamos instalando las librerías necesarias:
Instalamos Django Rest Framework
pip install djangorestframework
Instalamos Django-filter
pip install django-filter
Y por último, instalamos Markdown:
pip install markdown
Pasamos a configurar el proyecto:
Lo primero será añadir rest_framework a settings.py:
1INSTALLED_APPS = [
2 'django.contrib.admin',
3 'django.contrib.auth',
4 'django.contrib.contenttypes',
5 'django.contrib.sessions',
6 'django.contrib.messages',
7 'django.contrib.staticfiles',
8 'rest_framework'
9]
Ahora como mínimo necesitamos tener una app creada, para ello ejecutamos
python manage.py startapp API
Añadimos la app a settings.py:
1INSTALLED_APPS = [
2 'django.contrib.admin',
3 'django.contrib.auth',
4 'django.contrib.contenttypes',
5 'django.contrib.sessions',
6 'django.contrib.messages',
7 'django.contrib.staticfiles',
8 'rest_framework',
9 'API'
10]
Realizamos la migración de la base de datos
python manage.py migrate
Y creamos un superusuario
python manage.py createsuperuser
Nota
Una forma cómoda de trabajar con aplicaciones mixtas es crear un directorio API dentro de la app y ahí un nuevo views.py y serializers.py
Serializadores#
Los serializadores se encargan de trabajar con los modelos de datos para adaptarlos a la API.
Para comenzar tenemos que crear un modelo en nuestra API (API/models.py):
1from django.db import models
2
3
4class Consola(models.Model):
5 """ Modelo de datos para videoconsolas """
6
7 marca = models.CharField(max_length=200)
8 modelo = models.CharField(max_length=200)
9 lanzamiento = models.IntegerField(blank=True, null=True)
10
11 def __str__(self):
12 return self.modelo
13
14
15class Videojuego(models.Model):
16 """ Modelo de datos para videojuegos """
17
18 titulo = models.CharField(max_length=200)
19 consola = models.ForeignKey(Consola, on_delete=models.CASCADE)
20
21 def __str__(self):
22 return self.modelo
Ejecutamos
python manage.py makemigrations API
para preparar las migraciones y migramos la tablapython manage.py migrate API
Serializador básico#
Vamos a crear un archivo para el serializador en la ruta (API/serializers/consolaSerializer.py):
1# Importamos la librería de serializers:
2from rest_framework import serializers
3# Importamos el modelo de datos a usar:
4from API.models import Consola
5
6# Creamos el serializador:
7class ConsolaSerializer(serializers.ModelSerializer):
8 # de forma similar a los formularios, es conveniente definir los campos del modelo:
9 marca = serializers.CharField(required=True, max_length=200)
10 modelo = serializers.CharField(required=True, max_length=200)
11 lanzamiento = serializers.IntegerField(required=False)
12
13 class Meta:
14 # Elegimos el modelo:
15 model = Consola
16
17 # Aquí se definen los campos que va a manejar el serializador:
18 fields = [
19 'marca',
20 'modelo',
21 'lanzamiento'
22 ]
23
24 # el metodo validador recibirá los datos que se van a regsitrar y los comprobará antes de ingresarlos:
25 def validate(self, data):
26 # podemos crear validaciones específicas:
27 if data.get('lanzamiento') and data.get('lanzamiento') < 1970:
28 raise serializers.ValidationError('El año de lanzamiento es demasiado bajo')
Con esto ya hemos preparado el primer serializador.
Importante
Recuerda crear un archivo vacío llamado __init__.py en cada carpeta que se generes para convertirlo en módulo.
Modificar salida de datos#
Se puede modificar la salida de datos a mostrar en el JSON:
1from rest_framework import serializers
2# importamos el modelo videojuego para listarlos:
3from API.models import Consola, Videojuego
4
5class ConsolaSerializer(serializers.ModelSerializer):
6 marca = serializers.CharField(required=True, max_length=200)
7 modelo = serializers.CharField(required=True, max_length=200)
8 lanzamiento = serializers.IntegerField(required=False)
9
10 class Meta:
11 model = Consola
12
13 # Aquí se definen los campos que va a manejar el serializador:
14 fields = [
15 'id',
16 'marca',
17 'modelo',
18 'lanzamiento'
19 ]
20
21 def validate(self, data):
22 # podemos crear validaciones específicas:
23 if data.get('lanzamiento') and data.get('lanzamiento') < 1970:
24 raise serializers.ValidationError('El año de lanzamiento es demasiado bajo')
25
26 return data
27
28 # Se puede personalizar la salida de datos con este método:
29 def to_representation(self, instance):
30 # podemos ir llamando cada uno de los títulos disponibles por su consola:
31 juegos = Videojuego.objects.filter(consola=instance.id)
32 return {
33 "id": instance.id,
34 "marca": instance.marca,
35 "modelo": instance.modelo,
36 "lanzamiento": instance.lanzamiento,
37 "juegos": [juego.titulo for juego in juegos] # tan solo hay que añadir el titulo de cada elemento del listado que hemos localizado
38 }
Modificar creación de datos#
Se puede modificar la creacíon de datos para ajustar su guardado:
1from rest_framework import serializers
2from API.models import Consola, Videojuego
3
4class ConsolaSerializer(serializers.ModelSerializer):
5 marca = serializers.CharField(required=True, max_length=200)
6 modelo = serializers.CharField(required=True, max_length=200)
7 lanzamiento = serializers.IntegerField(required=False)
8 # creamos un campo nuevo opcional para recibir un videojuego y crearlo si no existe:
9 videojuego = serializers.CharField(required=False, max_length=200)
10
11 class Meta:
12 model = Consola
13
14 fields = [
15 'id',
16 'marca',
17 'modelo',
18 'lanzamiento',
19 'videojuego' # este hay que añadirlo a la lista de campos para que el validador lo verifique
20 ]
21
22 def validate(self, data):
23 if data.get('lanzamiento') and data.get('lanzamiento') < 1970:
24 raise serializers.ValidationError('El año de lanzamiento es demasiado bajo')
25
26 return data
27
28 def to_representation(self, instance):
29 juegos = Videojuego.objects.filter(consola=instance.id)
30 return {
31 "id": instance.id,
32 "marca": instance.marca,
33 "modelo": instance.modelo,
34 "lanzamiento": instance.lanzamiento,
35 "juegos": [juego.titulo for juego in juegos]
36 }
37
38 # se usa el método create y en el se localiza si existe la consola y sino se procede a crear:
39 def create(self, validated_data):
40 # se crea la consola con los datos validados:
41 consola = Consola.objects.create(**validated_data)
42 # se crea el videojuego:
43 if validated_data.get('videojuego'):
44 Videojuego.objects.create(titulo=validated_data.get('videojuego'), consola_id=consola.id)
45
46 # se retorna la creación del objeto:
47 return consola
Atención
la información que se recibe a continuación ya ha sido validada, por lo tanto no se puede modificar aquello que no pase la validación.
Modificar actualización de datos#
Se puede modificar la creacíon de datos para ajustar su guardado:
1from rest_framework import serializers
2from API.models import Consola, Videojuego
3
4class ConsolaSerializer(serializers.ModelSerializer):
5 marca = serializers.CharField(required=True, max_length=200)
6 modelo = serializers.CharField(required=True, max_length=200)
7 lanzamiento = serializers.IntegerField(required=False)
8 videojuego = serializers.CharField(required=False, max_length=200)
9
10 class Meta:
11 model = Consola
12
13 fields = [
14 'id',
15 'marca',
16 'modelo',
17 'lanzamiento',
18 'videojuego'
19 ]
20
21 def validate(self, data):
22 if data.get('lanzamiento') and data.get('lanzamiento') < 1970:
23 raise serializers.ValidationError('El año de lanzamiento es demasiado bajo')
24
25 return data
26
27 def to_representation(self, instance):
28 juegos = Videojuego.objects.filter(consola=instance.id)
29 return {
30 "id": instance.id,
31 "marca": instance.marca,
32 "modelo": instance.modelo,
33 "lanzamiento": instance.lanzamiento,
34 "juegos": [juego.titulo for juego in juegos]
35 }
36
37 def create(self, validated_data):
38 consola = Consola.objects.create(**validated_data)
39 if validated_data.get('videojuego'):
40 Videojuego.objects.create(titulo=validated_data.get('videojuego'), consola_id=consola.id)
41
42 return consola
43
44
45 # se puede modificar el update para editar campos como queramos:
46 def update(self, instance, validated_data):
47
48 # se recuperan los campos validados que queremos modificar:
49 validated_data["marca"] = instance.marca # recuperamos del registro original la marca y evitamos que sea modificable.
50 validated_data["lanzamiento"] = 1990 # se puede establecer un valor fijo que no cambiará aunque enviemos un dato distinto.
51
52 # ahora se recorren los valores de ambos objetos y se combinan:
53 for key, value in validated_data.items():
54 setattr(instance,key,value)
55
56 # se guarda y retornan los cambios:
57 instance.save()
58 return instance
Al intentar actualizar el registro con los siguientes datos no permitirá el cambio de marca y el lanzamiento asignará siempre 1990:
1{
2 "marca": "Sammy",
3 "modelo": "Play Funstation",
4 "lanzamiento": 2015
5}
Atención
la información que se recibe a continuación ya ha sido validada, por lo tanto no se puede modificar aquello que no pase la validación.
ViewSets#
Los viewsets se implementan en las vistas de Django y sirven para mostrar los valores de la API o bien en su frontend o bien como un JSON.
GET (mostrar información)#
Para crear un ViewSet nos vamos a (API/views/consolaViewSet.py):
1# importamos el apiview:
2from rest_framework.views import APIView
3# y el response:
4from rest_framework.response import Response
5# también es necesario traer el modelo:
6from API.models import Consola
7# se carga el serializador creado:
8from API.serializers.consolaSerializer import ConsolaSerializer
9
10# creamos la vista:
11class ConsolaApiView(APIView):
12
13 # con APIView se va definiendo que tipo de entrada permite la vista(funciona como una vista común en Django en lo referente a parametros):
14 def get(self, request):
15 # se carga los datos en una variable:
16 consolas = Consola.objects.all()
17 # se le envía al serializador, definimos si va a ser uno o varios resultados con many:
18 serializer = ConsolaSerializer(consolas, many=True)
19
20 # se muestra la respuesta:
21 return Response(serializer.data, status=status.HTTP_200_OK)
GET - Paginación y personalizar salida#
Se puede retornar un número determinado de elementos por página creando un paginador personalizado:
1from rest_framework.views import APIView
2from rest_framework.response import Response
3from API.models import Consola
4from API.serializers.consolaSerializer import ConsolaSerializer
5# se cargan los siguientes módulos de paginador:
6from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
7
8# creamos la vista:
9class ConsolaApiView(APIView):
10 # se define un nuevo parámetro para recibir por url con el número de página:
11 def get(self, request, consola=None):
12
13 # Si queremos un solo resultado, usamos el parámetro consola por url y recuperamos solo un valor:
14 consola = Consola.objects.filter(id=consola).first()
15 if consola:
16 serializer = ConsolaSerializer(consola)
17 return Response(serializer.data, status=status.HTTP_200_OK)
18
19
20 consolas = Consola.objects.all()
21 serializer = ConsolaSerializer(consolas, many=True)
22 page = request.data.get('page')
23 # el paginador recibe la data del serializador y un número de páginas:
24 paginator = Paginator(serializer.data, 5)
25 # Se controla si se recibe o no página y el número de páginas:
26 try:
27 consolas_paginated = paginator.page(page)
28 except PageNotAnInteger:
29 consolas_paginated = paginator.page(1)
30 except EmptyPage:
31 consolas_paginated = paginator.page(paginator.num_pages)
32 # Se puede modificar la respuesta para retornar datos de la paginación:
33 return Response({
34 "cantidad_paginas": paginator.num_pages,
35 "pagina_actual": page,
36 "hay_siguiente": consolas_paginated.has_next(),
37 "pagina_siguiente": consolas_paginated.next_page_number() if consolas_paginated.has_next() else False,
38 "consolas": consolas_paginated.object_list
39 }, status=status.HTTP_200_OK)
POST (Crear registro)#
El post se define como un método nuevo para procesar la información:
1from email.policy import HTTP
2from rest_framework.views import APIView
3from rest_framework.response import Response
4from API.models import Consola
5from API.serializers.consolaSerializer import ConsolaSerializer
6from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
7from rest_framework import status
8
9class ConsolaApiView(APIView):
10 def get(self, request, consola=None):
11
12 consola = Consola.objects.filter(id=consola).first()
13 if consola:
14 serializer = ConsolaSerializer(consola)
15 return Response(serializer.data, status=status.HTTP_200_OK)
16
17 consolas = Consola.objects.all()
18 serializer = ConsolaSerializer(consolas, many=True)
19
20 page = request.data.get("page")
21 paginator = Paginator(serializer.data, 5)
22
23 try:
24 consolas_paginated = paginator.page(page)
25 except PageNotAnInteger:
26 consolas_paginated = paginator.page(1)
27 except EmptyPage:
28 consolas_paginated = paginator.page(paginator.num_pages)
29 # Se puede modificar la respuesta para retornar datos de la paginación:
30 return Response({
31 "cantidad_paginas": paginator.num_pages,
32 "pagina_actial": page,
33 "hay_siguiente": consolas_paginated.has_next(),
34 "pagina_siguiente": consolas_paginated.next_page_number() if consolas_paginated.has_next() else False,
35 "consolas": consolas_paginated.object_list
36 }, status=status.HTTP_200_OK)
37
38 # se define el post en la api view:
39 def post(self, request):
40 # se carga la información de la petición post en el serializador:
41 serializer = ConsolaSerializer(data=request.data)
42 # se verifica si ha pasado la validación:
43 if serializer.is_valid():
44 # se guarda la información y se retorna el resultado:
45 serializer.save()
46 return Response(serializer.data, status=status.HTTP_201_CREATED)
47 # si no ha salido bien se retornan los errores:
48 return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
DELETE - Eliminar elemento#
1from email.policy import HTTP
2from rest_framework.views import APIView
3from rest_framework.response import Response
4from API.models import Consola
5from API.serializers.consolaSerializer import ConsolaSerializer
6from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
7from rest_framework import status
8
9class ConsolaApiView(APIView):
10 def get(self, request, consola=None):
11
12 consola = Consola.objects.filter(id=consola).first()
13 if consola:
14 serializer = ConsolaSerializer(consola)
15 return Response(serializer.data, status=status.HTTP_200_OK)
16
17 consolas = Consola.objects.all()
18 serializer = ConsolaSerializer(consolas, many=True)
19
20 page = request.data.get("page")
21 paginator = Paginator(serializer.data, 5)
22
23 try:
24 consolas_paginated = paginator.page(page)
25 except PageNotAnInteger:
26 consolas_paginated = paginator.page(1)
27 except EmptyPage:
28 consolas_paginated = paginator.page(paginator.num_pages)
29
30 return Response({
31 "cantidad_paginas": paginator.num_pages,
32 "pagina_actial": page,
33 "hay_siguiente": consolas_paginated.has_next(),
34 "pagina_siguiente": consolas_paginated.next_page_number() if consolas_paginated.has_next() else False,
35 "consolas": consolas_paginated.object_list
36 }, status=status.HTTP_200_OK)
37
38 def post(self, request):
39 serializer = ConsolaSerializer(data=request.data)
40 if serializer.is_valid():
41 serializer.save()
42 return Response(serializer.data, status=status.HTTP_201_CREATED)
43 return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
44
45 # Eliminar un registro:
46 def delete(self, request, consola):
47 # se busca el valor:
48 consola = Consola.objects.filter(id=consola).first()
49 # si existe se borra:
50 if consola:
51 consola.delete()
52 return Response("Consola eliminada", status=status.HTTP_204_NO_CONTENT)
53 return Response("No existe consola", status=status.HTTP_400_BAD_REQUEST)
PUT - Actualizar registros#
1from email.policy import HTTP
2from rest_framework.views import APIView
3from rest_framework.response import Response
4from API.models import Consola
5from API.serializers.consolaSerializer import ConsolaSerializer
6from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
7from rest_framework import status
8
9class ConsolaApiView(APIView):
10 def get(self, request, consola=None):
11 consola = Consola.objects.filter(id=consola).first()
12 if consola:
13 serializer = ConsolaSerializer(consola)
14 return Response(serializer.data, status=status.HTTP_200_OK)
15
16 consolas = Consola.objects.all()
17 serializer = ConsolaSerializer(consolas, many=True)
18
19 page = request.data.get("page")
20 paginator = Paginator(serializer.data, 5)
21
22 try:
23 consolas_paginated = paginator.page(page)
24 except PageNotAnInteger:
25 consolas_paginated = paginator.page(1)
26 except EmptyPage:
27 consolas_paginated = paginator.page(paginator.num_pages)
28
29 return Response({
30 "cantidad_paginas": paginator.num_pages,
31 "pagina_actial": page,
32 "hay_siguiente": consolas_paginated.has_next(),
33 "pagina_siguiente": consolas_paginated.next_page_number() if consolas_paginated.has_next() else False,
34 "consolas": consolas_paginated.object_list
35 }, status=status.HTTP_200_OK)
36
37 def post(self, request):
38 serializer = ConsolaSerializer(data=request.data)
39 if serializer.is_valid():
40 serializer.save()
41 return Response(serializer.data, status=status.HTTP_201_CREATED)
42 return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
43
44
45 def delete(self, request, consola):
46 consola = Consola.objects.filter(id=consola).first()
47 if consola:
48 consola.delete()
49 return Response("Consola eliminada", status=status.HTTP_204_NO_CONTENT)
50 return Response("No existe consola", status=status.HTTP_400_BAD_REQUEST)
51
52
53 # actualizamos registros con put:
54 def put(self, request, consola):
55 # se recupera el registro a editar:
56 consola = Consola.objects.get(id=consola)
57
58 # se envía el objeto y la data recibida por put para hacer los cambios:
59 serializer = ConsolaSerializer(consola, data=request.data, partial=True) # la opción partial indica que no es necesario enviar todo
60
61 # se comprueba que todo es correcto:
62 if serializer.is_valid():
63 serializer.save()
64 return Response(serializer.data, status=status.HTTP_200_OK)
65 return Response("Hay errores en la actualización", status=status.HTTP_400_BAD_REQUEST)
Con esto ya tenemos listo el ViewSet.
Nota
Si entramos a la ruta http://localhost:8000/api/consolas veremos una interfaz con el resultado, pero lo más recomendable es trabajar con software como Postman o Insomnia
Rutas de la API#
Archivo principal de rutas urls.py:
1from django.contrib import admin
2from django.urls import path, include
3
4urlpatterns = [
5 path('admin/', admin.site.urls),
6 path('api/', include('API.urls'))
7]
Archivo de rutas adicional: (API/urls.py):
1from django.urls import path
2from API.views.consolaViewset import ConsolaApiView
3
4urlpatterns = [
5 path('consolas', ConsolaApiView.as_view(), name='consolas'),
6 # agregamos la ruta para una consola individual:
7 path('consolas/<int:consola>', ConsolaApiView.as_view(), name='consola')
8]
Ahora podemos ejecutar la API en http://localhost:8080/api/consolas
o http://localhost:8080/api/consolas/1
y ver como podemos añadir registros.
Atención
Con el nivel actual de permisos cualquiera puede introducir valores en la API. Para cambiar eso tenemos que ir al apartado de permisos
Permisos#
Tenemos varios tipos de permisos para gestionar nuestra API. Para establecer permisos creamos una lista al final de (prueba_api/settings.py):
Por defecto nuestra API estará disponible para lectura y escritura ante cualquier extraño.
Permisos Globales (settings.py)#
Se pueden establecer permisos globales en toda la aplicación editando settings.py:
Establecer permisos a solo lectura:
1REST_FRAMEWORK = {
2 'DEFAULT_PERMISSION_CLASSES': [
3 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',
4 ],
5}
Añadir acceso por login para poder editar y ver datos.
1REST_FRAMEWORK = {
2 'DEFAULT_PERMISSION_CLASSES': [
3 'rest_framework.permissions.IsAuthenticated',
4 ],
5}
Login requerido para editar y visualización sin login:
1REST_FRAMEWORK = {
2 'DEFAULT_PERMISSION_CLASSES': [
3 'rest_framework.permissions.IsAuthenticatedOrReadOnly',
4 ],
5}
Permisos individuales#
En los ViewSets se pueden definir permisos para cada vista usando la variable permission_classes:
1# Se importan los permisos que vayamos a establecer como IsAuthenticatedOrReadOnly, IsAuthenticate, o IsAdminUser:
2from rest_framework.permissions import IsAuthenticatedOrReadOnly
3
4class ConsolaApiView(APIView):
5 # se asigna al atributo siguiente el tipo de permiso de esta APIView:
6 permission_classes = [IsAuthenticatedOrReadOnly]
7
8 def get(self, request, consola=None):
9 ...
Permisos personalizados#
Se puede crear un nuevo permiso y utilizarlo tanto como permiso global como para ViewSets individuales:
En la aplicación deseada se crea un archivo permissions.py:
1# se creará un permiso para que acepte solo un usuario:
2# se importa el permiso base:
3from rest_framework.permissions import BasePermission
4
5# se crea la clase del permiso:
6class SoloMeOrReadOnly(BasePermission):
7
8 # tendrá una función que comprueba si soy yo el que inicia sesión o no:
9 def has_permission(self, request, view):
10 if request.method == 'GET':
11 return True
12 else:
13 return bool(request.user.is_authenticated and request.user.username == 'guillermo')
14
15
16 # Este segundo método se invoca para establecer los permisos en un objeto (un articulo o elemento simple):
17 def has_object_permission(self, request, view, obj):
18 if request.method == 'GET':
19 return True
20 else:
21 return bool(request.user.is_authenticated and request.user.username == 'guillermo')
En el ViewSet se invoca y se aplica como un permiso cualquiera:
1# se importa el permiso creado:
2from API.permissions import SoloMeOrReadOnly
3
4class ConsolaApiView(APIView):
5 # se agrega el permiso como cualquier estandar:
6 permission_classes = [SoloMeOrReadOnly]
7
8 def get(self, request, consola=None):
9 ...
Nota
El método has_object_permission que se ocupa de los permisos de un elemento en la tabla no es obligatorio si vamos a tener los mismos permisos como en el ejemplo anterior.
Crear documentación automática#
Es muy interesante crear un sistema de documentación automática en nuestra api rest.
Para ello se hace lo siguiente:
Instalar coreapi:
pip install coreapi
Crear la ruta de la documentación en API/urls.py:
1from django.urls import path
2from API.views.consolaViewset import ConsolaApiView
3# cargamos el modulo de rutas de rest_framework:
4from rest_framework.documentation import include_docs_urls
5
6urlpatterns = [
7 path('consolas', ConsolaApiView.as_view(), name='consolas'),
8 # agregamos la ruta para una consola individual:
9 path('consolas/<int:consola>', ConsolaApiView.as_view(), name='consola'),
10 path('docs/', include_docs_urls(title='Nombre API', public=False))
11]
Se añade a la constante REST_FRAMEWORK el siguiente valor en settings.py:
1REST_FRAMEWORK = {'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema' }
Autenticación con JWT#
Instalar jwt:
pip install djangorestframework-simplejwt
Se añade a INSTALLED_APPS la aplicación simplejwt:
'rest_framework_simplejwt'
Se añaden a la constante REST_FRAMEWORK los siguientes valores en settings.py:
1REST_FRAMEWORK = {
2 'DEFAULT_PERMISSION_CLASSES': [
3 'rest_framework.permissions.IsAuthenticated',
4 ],
5 'DEFAULT_AUTHENTICATION_CLASSES': ( # Este apartado define los metodos de autenticación
6 'rest_framework_simplejwt.authentication.JWTAuthentication', # este es el método jwt que vamos a usar
7 'rest_framework.authentication.SessionAuthentication', # este es el método por sesión
8 'rest_framework.authentication.BasicAuthentication', # y este es el método básico de usuario y contraseña
9 ),
10
11}
Toca añadir las rutas para obtener el token de autenticación en API/urls.py:
1from django.urls import path
2from API.views.consolaViewset import ConsolaApiView
3from rest_framework.documentation import include_docs_urls
4# Se importan los metodos para obtener y refrescar el token:
5from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
6
7urlpatterns = [
8 path('consolas', ConsolaApiView.as_view(), name='consolas'),
9 path('consolas/<int:consola>', ConsolaApiView.as_view(), name='consola'),
10 path('docs/', include_docs_urls(title='Nombre API', public=False)),
11 # Añadimos la ruta para obtener el token y para refrescarlo:
12 path('token', TokenObtainPairView.as_view(), name='token_obtain_pair'),
13 path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh')
14]
Obtener un Token#
Para obtener un Token se hace lo siguiente: 1. Abrir Postman o Insomnia (u otro cliente API). 2. Ejecutar una petición POST a la ruta http://127.0.0.1:8000/api/token con usuario y contraseña:
1 {
2 "username":"pepillo",
3 "password":"maizfrito"
4 }
Esto nos devolverá un token por ejemplo:
1{
2 "refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTY0NjA1MzkxNCwiaWF0IjoxNjQ1OTY3NTE0LCJqdGkiOiJmOGVhOWUzNTdhMGU0ZWU4ODU0Y2NiNWE2NTdjOGY1ZiIsInVzZXJfaWQiOjN9.9DvZVyzfZcmB-v9P_mgETFighXz2KjChPc_EslH5X3M",
3 "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjQ1OTY3ODE0LCJpYXQiOjE2NDU5Njc1MTQsImp0aSI6IjBhNmEyNWM2YjVlMDQ4ZjQ5MzQxYzM2MGNlODM2OTdiIiwidXNlcl9pZCI6M30.He7w5XxjCrgeWupFOnGdVH4EusJ5fRZMbY3zkyZetCI"
4}
Ahora este token «access» se utilizará para todas las operaciones contra la API que requieran autenticación. Solo hay que elegir en la pestaña Auth de Postman la opción Bearer e ingresar el token en cada consulta.
Haciendo una petición contra la API con JWT#
Ejemplo de uso con Python.
La petición se podría dividir en dos partes:
Solicitud de token:
1import requests
2
3headers = {
4 'Content-Type': 'application/json',
5 'Accept': '*/*',
6}
7
8data = '{"username":"misterg@gmail.com", "password":"maizfrito"}'
9
10r = requests.post('http://127.0.0.1:8000/api/token', headers=headers, data=data)
11print(r.status_code)
12token = r.json()
Petición de datos (listado de series):
1headers['Authorization'] = 'Bearer ' + token.get('access')
2
3r = requests.get('http://127.0.0.1:8000/api/consolas', headers=headers)
4print(r.status_code)
5print(r.content)