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 djangoCreamos un projecto en Django
django-admin startproject prueba_apiEjecutamos 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 djangorestframeworkInstalamos Django-filter
pip install django-filterY 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 APIAñ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 migrateY 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 APIpara 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 coreapiCrear 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-simplejwtSe 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)