Diario de Python | #6. Primeros pasos con Color Choice
Tags: devjournal, python, spanish
Published: 2023-02-02 21:35:05

Ahora que tengo la idea general escrita, voy a avanzar con la ejecución de los primeros pasos del proyecto. En principio necesito un repositorio, así que les voy a mostrar una manera práctica que encontré mediante PyCharm.

## GitHub y PyCharm

Primero armo un proyecto en el IDE, lo de siempre:

![Color Choice nuevo proyecto](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kjmg0004v1009j2lnqsa.png)

Luego en el menú superior voy a VSC y toco "Share Project on GitHub". Los campos se explican por sí mismos.

![Share Project on GitHub](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/spg58g6xpu64dp75z3cd.png)

Una vez le doy a share, me pedirá que haga un commit inicial. Esto es necesario para inicializar el branch:

![Initial Commit](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p5f8wsv9f8z4dybnzxx0.png)

Con esto, se generará un proyecto en nuestra cuenta. Este es el [repositorio](https://github.com/maxwellnewage/python-color-choice-api) que voy a utilizar.

## Hola mundo (version API)
Siempre hay que empezar desde lo más simple hasta alcanzar un punto de complejidad, el cual no deja de ser un conjunto funcional de cosas simples interactuando entre ellas.

Por eso, luego de importar los paquetes correspondientes, este es mi hola mundo en FastAPI:

```python
from fastapi import FastAPI, status

app = FastAPI()


@app.get('/', status_code=status.HTTP_200_OK)
async def home():
    return "hello api!"
```

Debemos correr esto con la configuración de Uvicorn que había comentado en el artículo anterior:

![Configuración PyCharm Uvicorn](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rqydt9ddj4p18asbalwr.png)

Y si vemos un "hello api!" en nuestra dirección http://127.0.0.1:8000/, ¡entonces estamos arriba!

## Persistencia, o el arte de guardar cosas
La parte más importante de este proyecto va a ser la persistencia: esos usuarios registrados con sus votos deben guardarse en algún lugar.

En el [proyecto de la pizzería](https://github.com/maxwellnewage/fastapi-pizza-delivery-api), utilicé Postgresql y no me terminó de convencer la cantidad de configuración que debía implementar. Quería algo más sencillo y ligero, por lo cual mi opción luego de meditar un buen rato será [SQLite](https://www.sqlite.org/index.html).

Por otro lado, para ORM voy a utilizar [SQLModel](https://sqlmodel.tiangolo.com/), librería que desarrolló el mismo creador de FastAPI, por lo cual la compatibilidad es absoluta.

Dicho esto, procedo a implementar mi archivo que tendrá las instrucciones para armar la BD:

```python
from sqlmodel import create_engine
import os


BASE_DIR = os.path.dirname(os.path.realpath(__file__))

conn_str = 'sqlite:///' + os.path.join(BASE_DIR, 'color_choice.db')

engine = create_engine(conn_str, echo=True)
```

Luego voy a crear el archivo que permite ejecutar la creación de mi nueva base de datos (create_db.py):

```python
from sqlmodel import SQLModel
from storage.db import engine

print("CREATING DB.......")

SQLModel.metadata.create_all(engine)
```

SQLModel utiliza el objeto engine, el cual contiene la información de la BD que vamos a crear, y luego genera la misma. Eso nos da como resultado una BD vacía a modo de archivo que encontraremos en el mismo directorio raíz del proyecto llamada color_choice.db.

Finalmente debemos crear un objeto Session para manejar las transacciones en cada endpoint, por lo cual volvemos a main.py y agregamos:

```python
from storage.db import engine

session = Session(bind=engine)
```

## Los modelos
Para SQLModel, los modelos son la representación de las tablas en nuestra base de datos. De momento no tenemos ninguno, por eso la BD está vacía. Vamos a generar los modelos que nos servirán para gestionar los votos en models.py:

```python
from sqlmodel import SQLModel, Field
from typing import Optional


class Color(SQLModel, table=True):
    color_id: int = Field(default=None, primary_key=True)
    color: str


class Vote(SQLModel, table=True):
    user_id: str = Field(default=None, primary_key=True)
    color_id_selected: Optional[int] = Field(default=None, foreign_key="color.color_id")
    color_voted: bool = Field(default=False)
```

Al principio pensé en mantener siempre los mismos colores, pero me di cuenta que podía perder escalabilidad. Por lo tanto, decidí crear una tabla para color y otra con los votos que tenga una clave foránea a la anterior. Entonces si un día decido agregar un color, o incluso cambiarle el nombre a uno existente, puedo hacerlo.

## Recrear la BD
Ahora que tenemos los modelos armados, hay que volver a generar la BD, pero en el archivo create_db.py vamos a importarlos:

```python
import models
```

No importa que nuestro IDE diga que el módulo no está en uso, porque en el momento en el que corra _create_all(engine)_ va a considerar automáticamente las clases del modelo. Esto no ocurre por arte de magia, sino que genera tablas de las clases que hereden de SQLModel.

Cuando miremos nuestra BD, ahora tendremos dos tablas: color y vote.

## Conclusiones
Todo lo que hicimos constituye un gran avance. Con el proyecto y la BD armados, ya podemos trabajar en los endpoints y la interacción con el usuario. El código, como siempre, pueden encontrarlo en el [repositorio del proyecto](https://github.com/maxwellnewage/python-color-choice-api).