Creacion de una api de pokemon
Se abrió Visual Studio en una carpeta que llamarémos PokemonAPI
y se creó un ambiente virtual desde la terminal con el comando:
py -m venv my_venv
Para poder usar el ambiente virtual, se procedio a activarlo:
.\my_venv\Scripts\activate
En el my_venv
se instalaron las librerias a usar con el comando pip:
pip install django
Se creó un nuevo projecto en Django:
django-admin startproject restAPI .
Para comprobar que no hay algún error se ejecutó la aplicación:
python manage.py runserver 8090
A continuación, creamos la app para nuestra api con la instrucción:
django-admin startapp api
Dentro de la carpeta restAPI
en el archivo settings.py, agregamos el nombre de nuestra app, llamada api en la lista INSTALLED_APPS como se muestra:
Dentro de la carpeta api
en el archivo models.py, creamos una clase para describir a los entrenadores pokemon:
from django.db import models
# Create your models here.
class EntrenadorPokemon(models.Model):
region = models.CharField(max_length=50)
tipo = models.CharField(max_length=50)
numero_medallas = models.PositiveSmallIntegerField()
Para poder verlo desde el administrador que nos provee Django, primero iremos al archivo de admin.py y escribimos el siguiente código:
from django.contrib import admin
from .models import EntrenadorPokemon
# Register your models here.
admin.site.register(EntrenadorPokemon)
En la terminal, debemos ejecutar el siguiente comando para crear las bases de datos que necesita Django:
python manage.py migrate
Como confirmación de que todo ha ido bien hasta aquí, nos debe mandar los siguientes mensajes de OK:
Para poder acceder al panel de administración, debemos crear un super usuario, esto lo haremos escribiendo el siguiente comando en la terminal:
python manage.py createsuperuser
Nos pedirá un nombre de usuario, un correo, una contraseña y la confirmación de la contraseña. Por último creamos las migraciones del modelo, esto lo hacemos ingresando en la terminal
python manage.py makemigrations
Podemos visualizar estas migraciones dentro de PokemonAPI/api/migrations/0001_initial.py
. Cabe resaltar que se nos ha creado por defecto un campo llamado id que es autoincremental y funcionará como llave primaria.
Hacemos una migracion de nuevo, para que actualize los datos:
python manage.py migrate
Volvemos a ejecutar nuestro panel de administración con el comando:
python manage.py runserver 8090
Cambiamos la dirección a 127.0.0.1:8090/admin
como se muestra:
Se nos desplegará un LogIn, donde debemos introducir nuestro usuario y contraseña creado anteriormente.
Damos click en Log in y podemos acceder al administrador de Django:
Damos click en + add y se nos desplegará el siguiente formulario:
Llenamos los campos y damos click en SAVE para guardar nuestro primer registro:
y nos dará un mensaje de que se ha agregado:
Cremos uno más:
Antes de empezar con el método GET, debemos incluir las url que usarémos desde nuestra api. Iremos al archivo urls.py que se encuetra en PokemonAPI/restAPI/urls.py
e incluiremos la libreria include y el path que se muestra:
El archivo donde se encuentran estas api.urls aun no existe. Haci que crearemos un archivo llamado urls.py en la dirección PokemonAPI/api/urls.py
y ahi agregaremos el path:
path('entrenadores/', EntrenadorPokemonView.as_view(), name='entrenadores_list'),
que nos servira cuando se quieran consultar todos los entrenadores, y el path:
path('entrenadores/<int:id>', EntrenadorPokemonView.as_view(), name='entrenadores_procesos'),
que nos servirá cuando se quiere hacer una consulta GET para un id especifico o para hacer un POST, PUT y DELETE.
En este archivo tambien debemos incluir la clase EntrenadorPokemonView que importaremos de views, esta clase aun no existe.
En resumen, este archivo debe quedar por el momento, como se muestra a continuación:
Para comenzar con el método GET debemos ir PokemonAPI/api/views.py
en el archivo views.py. Aquí debemos crear la clase EntrenadorPokemonView con su método GET usando el siguiente código:
from django.shortcuts import render
from django.views import View
from .models import EntrenadorPokemon
from django.http.response import JsonResponse
# Create your views here.
class EntrenadorPokemonView(View):
def get(self, request, id=0):
if id>0:
entrenadores = list(EntrenadorPokemon.objects.filter(id=id).values())
if len(entrenadores)>0:
entrenador=entrenadores[0]
datos={'message':"Entrenador encontrado", 'entrenador':entrenador}
else:
datos={'message':"Entrenador no encontrado..."}
return JsonResponse(datos)
else:
entrenadores = list(EntrenadorPokemon.objects.values())
if len(entrenadores)>0:
datos={'message':"Entrenador encontrado", 'entrenadores':entrenadores}
else:
datos={'message':"Entrenador no encontrado..."}
return JsonResponse(datos)
Este método nos permite hacer una petición para consultar uno o todos los entrenadores pokemon que esten en nuestra base de datos.
Para probar nuestro método GET, harémos usos del plugin Thunder Client que servirá como cliente REST. Para usarlo, damos click en New Request.
Se nos desplegará una nueva pantalla y debemos ingresar la dirección:
http://127.0.0.1:8090/api/entrenadores/
y dar click en Send:
esto nos devolverá una respuesta en formato JSON con todos los resultados que teniamos en la base de datos:
Consultaremos ahora solo con un id:
http://127.0.0.1:8090/api/entrenadores/2
Por último, harémos una consulta de un id que no existe:
http://127.0.0.1:8090/api/entrenadores/99
Esto demuestra que el método GET funciona muy bien.
Primero debemos crear un crsf para poder autenticar que estamos haciendo las peticiones desde nuestro proyecto, esto lo evitamos con el decorador csrf_exempt. Y tambíen harémos uso de la librería para JSON, todo esto lo importamos como:
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
import json
Para usar el csrf_exempt usarémos una función llamada dispatch:
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
Ahora sí, nuestro método POST quedaría como:
def post(self, request):
jd = json.loads(request.body)
print(jd)
EntrenadorPokemon.objects.create(
region = jd['region'],
tipo = jd['tipo'],
numero_medallas = jd['numero_medallas']
)
datos={'message':"Entrenador creado !"}
return JsonResponse(datos)
En resumen, nuestro archivo views.py quedaría hasta ahora como:
from django.shortcuts import render
from django.views import View
from .models import EntrenadorPokemon
from django.http.response import JsonResponse
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
import json
# Create your views here.
class EntrenadorPokemonView(View):
def get(self, request, id=0):
if id>0:
entrenadores = list(EntrenadorPokemon.objects.filter(id=id).values())
if len(entrenadores)>0:
entrenador=entrenadores[0]
datos={'message':"Entrenador encontrado", 'entrenador':entrenador}
else:
datos={'message':"Entrenador no encontrado..."}
return JsonResponse(datos)
else:
entrenadores = list(EntrenadorPokemon.objects.values())
if len(entrenadores)>0:
datos={'message':"Entrenador encontrado", 'entrenadores':entrenadores}
else:
datos={'message':"Entrenador no encontrado..."}
return JsonResponse(datos)
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def post(self, request):
jd = json.loads(request.body)
print(jd)
EntrenadorPokemon.objects.create(
region = jd['region'],
tipo = jd['tipo'],
numero_medallas = jd['numero_medallas']
)
datos={'message':"Entrenador creado !"}
return JsonResponse(datos)
Para probar el método POST hacemos un nuevo request (cuadro verde) a la dirección (cuadro rojo):
http://127.0.0.1:8090/api/entrenadores/
pero mandando como parámetro el JSON en la sección del body (cuadro azul):
{
"region":"Alola",
"tipo":"Maestro Pokémon",
"numero_medallas":15
}
y al dar click en Send, obtenemos que la respuesta fue exitosa:
El método PUT nos permitirá modificar los datos ingresados.
Queda definido por la siguiente función:
def put(self, request, id):
jd = json.loads(request.body)
entrenadores = list(EntrenadorPokemon.objects.filter(id=id).values())
if len(entrenadores)>0:
entrenador = EntrenadorPokemon.objects.get(id=id)
entrenador.region = jd['region']
entrenador.tipo = jd['tipo']
entrenador.numero_medallas = jd['numero_medallas']
entrenador.save()
datos={'message':"Entrenador editado!"}
else:
datos={'message':"entrenador no encontrado..."}
return JsonResponse(datos)
Antes de modificar algun registro, usarémos el método GET para obtener el entrenador 2 y así poder comparar.
El resultado de la consulta es:
Para probar el método PUT hacemos un nuevo request (cuadro verde) a la dirección (cuadro rojo):
http://127.0.0.1:8090/api/entrenadores/2
pero mandando como parámetro el JSON en la sección del body (cuadro azul):
{
"region":"Sinnoh",
"tipo":"Coordinador Pokémon",
"numero_medallas":10
}
y al dar click en Send, obtenemos que la respuesta fue exitosa:
Usando el método GET para obtener el entrenador 2, vemos que ha sido editado:
Por último, harémos una edición de un id que no existe:
http://127.0.0.1:8090/api/entrenadores/99
Esto demuestra que el método PUT funciona muy bien.
El método DELETE nos permitirá eliminar los datos ingresados.
Queda definido por la siguiente función:
def delete(self, request, id):
Entrenadores = list(EntrenadorPokemon.objects.filter(id=id).values())
if len(Entrenadores)>0:
EntrenadorPokemon.objects.filter(id=id).delete()
datos={'message':"Entrenador eliminado!"}
else:
datos={'message':"Entrenador no encontrado..."}
return JsonResponse(datos)
Antes de usar el método DELETE, hacemos un GET para listar todos los cursos y así poder comparar:
Para probar el método DELETE hacemos un nuevo request (cuadro verde) a la dirección (cuadro rojo) para eliminar el registro 2:
http://127.0.0.1:8090/api/entrenadores/2
y al dar click en Send, obtenemos que la respuesta fue exitosa:
Usando el método GET volvemos a listar todos los items y vemos que ha sido eliminado el registro 2:
Por último, elimarémos un id que no existe:
http://127.0.0.1:8090/api/entrenadores/99
Esto demuestra que el método DELETE funciona muy bien.
Para poder consumir la api de Pokémon debemos usar el método get para obtener toda la información de https://pokeapi.co/
y transformala a un formato JSON para poder obtener las características que necesitemos del pokémon, que en este caso serán su id, nombre, peso y altura.
Crearémos una clase nueva en nuestro archivo views.py, que se llamará PokemonView que quedará de la siguiente forma:
class PokemonView(View):
def get(self, request, pokemon):
try:
url_pokeapi = urllib.request.Request(f'https://pokeapi.co/api/v2/pokemon/{pokemon}/')
url_pokeapi.add_header('User-Agent', 'charmander')
source = urllib.request.urlopen(url_pokeapi).read()
jd = json.loads(source)
datos={'id':jd['id'], 'nombre':jd['name'], 'altura':jd['height'], 'peso':jd['weight']}
return JsonResponse(datos)
except:
datos={'message':"Pokemon no encontrado..."}
return JsonResponse(datos)
Para que esta clase pueda funcionar importamos la librería que se muestra a continuación:
import urllib.request
Por último debemos incluir esta clase en el path, así que nos dirigimos a PokemonAPI/api/urls.py
. Este archivo debe quedar como se muestra:
from django.urls import path
from .views import EntrenadorPokemonView, PokemonView
urlpatterns = [
path('entrenadores/', EntrenadorPokemonView.as_view(), name='entrenadores_list'),
path('entrenadores/<int:id>', EntrenadorPokemonView.as_view(), name='entrenadores_procesos'),
path('pokemon/<str:pokemon>', PokemonView.as_view(), name='pokemon'),
]
Con esto también hemos terminado de editar el archivo views.py. A manera de resumen se muestra el código terminado:
from django.shortcuts import render
from django.views import View
from .models import EntrenadorPokemon
from django.http.response import JsonResponse
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
import json
import urllib.request
# Create your views here.
class EntrenadorPokemonView(View):
def get(self, request, id=0):
if id>0:
entrenadores = list(EntrenadorPokemon.objects.filter(id=id).values())
if len(entrenadores)>0:
entrenador=entrenadores[0]
datos={'message':"Entrenador encontrado", 'entrenador':entrenador}
else:
datos={'message':"Entrenador no encontrado..."}
return JsonResponse(datos)
else:
entrenadores = list(EntrenadorPokemon.objects.values())
if len(entrenadores)>0:
datos={'message':"Entrenador encontrado", 'entrenadores':entrenadores}
else:
datos={'message':"Entrenador no encontrado..."}
return JsonResponse(datos)
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def post(self, request):
jd = json.loads(request.body)
print(jd)
EntrenadorPokemon.objects.create(
region = jd['region'],
tipo = jd['tipo'],
numero_medallas = jd['numero_medallas']
)
datos={'message':"Entrenador creado!"}
return JsonResponse(datos)
def put(self, request, id):
jd = json.loads(request.body)
entrenadores = list(EntrenadorPokemon.objects.filter(id=id).values())
if len(entrenadores)>0:
entrenador = EntrenadorPokemon.objects.get(id=id)
entrenador.region = jd['region']
entrenador.tipo = jd['tipo']
entrenador.numero_medallas = jd['numero_medallas']
entrenador.save()
datos={'message':"Entrenador editado!"}
else:
datos={'message':"Entrenador no encontrado..."}
return JsonResponse(datos)
def delete(self, request, id):
Entrenadores = list(EntrenadorPokemon.objects.filter(id=id).values())
if len(Entrenadores)>0:
EntrenadorPokemon.objects.filter(id=id).delete()
datos={'message':"Entrenador eliminado!"}
else:
datos={'message':"Entrenador no encontrado..."}
return JsonResponse(datos)
class PokemonView(View):
def get(self, request, pokemon):
try:
url_pokeapi = urllib.request.Request(f'https://pokeapi.co/api/v2/pokemon/{pokemon}/')
url_pokeapi.add_header('User-Agent', 'charmander')
source = urllib.request.urlopen(url_pokeapi).read()
jd = json.loads(source)
datos={'id':jd['id'], 'nombre':jd['name'], 'altura':jd['height'], 'peso':jd['weight']}
return JsonResponse(datos)
except:
datos={'message':"Pokemon no encontrado..."}
return JsonResponse(datos)
Ahora vamos a probar el funcionamiento de la clase PokemonView usando Thunder Client, así que ingresamos la dirección (cuadro rojo):
http://127.0.0.1:8090/api/pokemon/squirtle
, seleccionamos el método GET (cuadro verde) y damos click en en Send (cuadro azul):
Esto nos retorna la siguente respuesta:
Ingresaremos ahora un pokemón que no exista:
http://127.0.0.1:8090/api/pokemon/squirtle_XX
Esto demuestra que el método GET de la clase PokemonView funciona muy bien.
Dentro de nuestro archivo .gitignore
vamos a escribir:
db.sqlite3
my_venv
__pycache__
Esto para ignorar esos archivos. En la termial escribimos:
git add .
y damos enter. Despues escribimos:
git commit -m "archivos a ignorar"
y damos enter.
Como ayuda para desplegar el proyecto me apoyaré de la documentación que viene en la sección de Update Your App For Render en la dirección https://render.com/docs/deploy-django
.
Lo primero es ir a PokemonAPI/restAPI/settings.py
e importar la libreria os y cambiar el SECRET_KEY':
luego nos pide cambiar el modo de DEBUG de True a 'RENDER' not in os.environ
:
después debajo de ALLOWED_HOSTS nos pide escribir lo siguiente:
RENDER_EXTERNAL_HOSTNAME = os.environ.get('RENDER_EXTERNAL_HOSTNAME')
if RENDER_EXTERNAL_HOSTNAME:
ALLOWED_HOSTS.append(RENDER_EXTERNAL_HOSTNAME)
en la terminal instalamos dj-database-url psycopg2-binary
con el comando pip install:
e importamos el modulo dj-database-url
en el archivo settings.py:
y lo asignamos a DATABASES de la siguiente forma:
El siguiente paso es instalar 'whitenoise[brotli]'
con el comando pip install:
Añadimos en MIDDLEWARE 'whitenoise.middleware.WhiteNoiseMiddleware',
como se muestra:
Debajo de STATIC_URL pegamos lo siguiente:
if not DEBUG:
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
Antes de continuar, vamos a crear un archivo requirements.txt. En la terminal escribimos pip freeze > requirements.txt
Nos pide crear un Build Script, haci que creamos un nuevo archivo llamado build.sh y dentro de este archivo debe ir:
#!/usr/bin/env bash
# exit on error
set -o errexit
poetry install -r requirements.txt
python manage.py collectstatic --no-input
python manage.py migrate
Este archivo build.sh debe tener permisos de ejecutable, así que abrimos el Git Bash dando click en la pestaña
Y escribimos chmod a+x build.sh
en la consola y damos enter:
Como vamos a correr la aplicación con Gunicorn, debemos añadir esta dependencia al poyecto. Escribimos pip install gunicorn
en la termial y despues damos enter.
Tambien debemos actualizar el archivo requirements.txt, asi que escribimos pip freeze > requirements.txt
en la termial y despues damos enter.
Escribimos git status
y damos enter. Tambien git add .
y damos enter. Hacemos un comit, escribimos git commit -m "configuración de Django lista"
y damos enter. Por último git push -u origin main
y damos enter.
Nos dirigimos a la página de render: https://render.com/
y nos creamos una cuenta, es gratis y no pide tarjeta de credito.
Ahora debemos crear una base de datos en PostgreSQL, así que nos dirigimos a https://dashboard.render.com/
y damos click para crear la base de datos:
Ingresamos los datos correspondientes como se muestra, seleccionamos el plan gratuito, damos click en Create Databse y esperamos a que termine de crearlo:
Cremos un nuevo proyecto, asi que vamos a https://render.com/
y damos click en New y después en Web Service:
Damos click en nuestra cuenta:
Y seleccionamos el repositorio que le queremos dar acceso a Render y damos click a Save:
esto nos redirecciona a Render y le damos click en Connect:
Se nos pedirán unos datos.
En el campo de Name:
ponemos el nombre del proyecto, en este caso PokemonAPI.
En el campo de Environment:
ponemos Python 3.
En el campo de Build Command:
ponemos ./build.sh.
En el campo de Start Command:
ponemos gunicorn restAPI.wsgi. Seleccionamos el plan gratuito. Después damos click al botón Advanced para crear nuestras variables de entorno. Damos click derecho a Dashboard y así tendremos dos ventanas para copiar las variables que necesitamos.
Damos click a PokemonAPI:
Y copiamos de la seccion de Connections la Internal Database URL
La pegamos donde se muestra y le damos el nombre de DATABASE_URL.
Damos click en Add Environment Variable y creamos la variable SECRET_KEY, esta variable es una clave aleatoria que podemos crear usando por ejemplo https://www.allkeysgenerator.com/Random/Security-Encryption-Key-Generator.aspx
:
Damos click en Add Environment Variable y creamos la variable PYTHON_VERSION, esta variable es la version que estamos usando de python. La obtenemos desde la terminal con python --version
:
Y por útimo damos click en Create Web Service. Pero esto me botó un error en el despliegue:
,debido a que en mi archivo build.sh tenia escrito:
poetry install -r requirements.txt
y debe de ser:
pip install -r requirements.txt
damos click en Deploy y esperamos a que termine.
Una ves terminado, si todo ha salido bien se verá así:
Para probar por ejemplo con el pokemon electabuzz, nos vamos a la dirección: https://pokemonapi-wild.onrender.com/api/pokemon/electabuzz
y obtenemos: