From 98662dd7f13a21362a6f7cf58c4d87705049bf91 Mon Sep 17 00:00:00 2001 From: Cristian Chiera Date: Tue, 22 Oct 2024 11:47:51 -0300 Subject: [PATCH] comit inicial --- part-01-hello-world/README.md | 253 +++++++++++++++++- part-01-hello-world/app/main.py | 89 +++++- part-02-path-parameters/app/main.py | 47 +++- part-04-pydantic-schemas/app/main.py | 16 +- part-04-pydantic-schemas/app/recipe_data.py | 18 ++ part-04-pydantic-schemas/app/schemas.py | 2 +- .../app/templates/index.html | 50 ++++ part-06-jinja-templates/app/main.py | 9 +- part-06-jinja-templates/app/recipe_data.py | 18 ++ part-07-database.zip | Bin 0 -> 68513 bytes part-07-database/alembic.ini | 2 +- part-07-database/app/backend_pre_start.py | 9 +- part-07-database/app/db/init_db.py | 9 +- part-07-database/app/db/session.py | 30 ++- part-07-database/app/initial_data.py | 4 + part-07-database/prestart.sh | 1 + 16 files changed, 526 insertions(+), 31 deletions(-) create mode 100644 part-05-basic-error-handling/app/templates/index.html create mode 100644 part-07-database.zip mode change 100755 => 100644 part-07-database/alembic.ini diff --git a/part-01-hello-world/README.md b/part-01-hello-world/README.md index 61e6803..fb8b776 100644 --- a/part-01-hello-world/README.md +++ b/part-01-hello-world/README.md @@ -1,11 +1,248 @@ -## Part 1 Local Setup +# FastAPI -1. `pip install poetry` (or safer, follow the instructions: https://python-poetry.org/docs/#installation) -2. Install dependencies `cd` into the directory where the `pyproject.toml` is located then `poetry install` -3. [UNIX]: Run the FastAPI server via poetry with the bash script: `poetry run ./run.sh` -4. [WINDOWS]: Run the FastAPI server via poetry with the Python command: `poetry run python app/main.py` -5. Open http://localhost:8001/ +## FastAPI course that I deliver to my colleagues. -To stop the server, press CTRL+C +- pip install poetry (or safer, follow the instructions: https://python-poetry.org/docs/#installation) +- pip install requeriments.txt +- Install dependencies cd into the directory where the pyproject.toml is located then poetry install +- [UNIX]: Run the FastAPI server via poetry with the bash script: poetry run ./run.sh +- [WINDOWS]: Run the FastAPI server via poetry with the Python command: poetry run python app/main.py +- Open http://localhost:8001/ +- To stop the server, press CTRL+C -If you get stuck, checkout the [troubleshooting readme](../troubleshooting/README.md) \ No newline at end of file +--- + +## Entornos + +Para crear un entorno virtual en Python en Windows, puedes seguir los pasos a continuación. Usar entornos virtuales es una buena práctica, ya que te permite aislar las dependencias de diferentes proyectos. + +### Pasos para crear un entorno virtual en Windows: + +#### 1. **Instalar Python (si aún no lo tienes)** + +- Primero, asegúrate de tener Python instalado en tu sistema. Puedes verificarlo abriendo el símbolo del sistema (CMD) y ejecutando: + +`python --version` + +Si Python no está instalado, descárgalo de la página oficial: [https://www.python.org/downloads/](https://www.python.org/downloads/) y asegúrate de seleccionar la opción **"Add Python to PATH"** durante la instalación. + +#### 2. **Abrir el Símbolo del Sistema (CMD)** + +- Presiona `Windows + R`, escribe `cmd` y presiona `Enter`. + +#### 3. **Instalar `virtualenv` (opcional)** + +- Python 3.3 y versiones superiores ya incluyen el módulo `venv` para crear entornos virtuales, por lo que no es necesario instalar `virtualenv` a menos que prefieras usarlo. +- Si prefieres usar `virtualenv`, instálalo ejecutando: + + ``` + pip install virtualenv + ``` + +#### 4. **Crear un entorno virtual** + +- Navega a la carpeta de tu proyecto o a la carpeta donde deseas crear el entorno virtual. Usa el comando `cd` para cambiar de directorio, por ejemplo: + + ``` + cd C:\ruta\de\mi\proyecto + ``` + +. Crea el entorno virtual usando el siguiente comando: + +- Crea el entorno virtual usando el siguiente comando: + + - Si usas el módulo `venv` incluido en Python 3: + - ```bash + python -m venv nombre_del_entorno + ``` + + - Si prefieres virtualenv (después de instalarlo): + - ````bash + virtualenv nombre_del_entorno + ``` + ```` + +Esto creará una carpeta llamada nombre_del_entorno en el directorio actual. Dentro de esta carpeta estarán los archivos necesarios para ejecutar el entorno virtual. + +#### 5. **Activar el entorno virtual** + +- Una vez creado el entorno virtual, debes activarlo. Ejecuta el siguiente comando: + + ```bash + nombre_del_entorno\Scripts\activate + ``` + +Si el entorno se activa correctamente, verás el nombre del entorno virtual al principio del prompt del símbolo del sistema. Por ejemplo: + +```bash +(nombre_del_entorno) C:\ruta\de\mi\proyecto> + +``` + +#### 6. **Instalar paquetes en el entorno** + +- Ahora que el entorno está activo, puedes instalar paquetes usando pip y estarán aislados en este entorno. Por ejemplo: + +```bash +pip install nombre_paquete + +``` + +aca hacemos pip install requeriments.txt + +#### 7. **Desactivar el entorno virtual** + +- Cuando termines de trabajar en el entorno virtual, puedes desactivarlo ejecutando: + +```bash +deactivate + +``` + +#### 8. **Ejemplo completo:** + +8.1. Crear el entorno virtual: + +```bash +python -m venv mi_entorno +``` + +8.2. Activar el entorno: + +```bash +mi_entorno\Scripts\activate +``` + +8.3. Instalar paquetes (por ejemplo, FastAPI): + +```bash +pip install fastap +``` + +8.4. Desactivar el entorno: + +```bash +deactivate +``` + +## Introducción a los Tipos de Python + +Estos type hints son una nueva sintaxis, desde Python 3.6+, que permite declarar el tipo de una variable. + +Usando las declaraciones de tipos para tus variables, los editores y otras herramientas pueden proveerte un soporte mejor. + +Este es solo un tutorial corto sobre los Python type hints. Solo cubre lo mínimo necesario para usarlos con FastAPI... realmente es muy poco lo que necesitas. + +Todo FastAPI está basado en estos type hints, lo que le da muchas ventajas y beneficios. + +Pero, así nunca uses FastAPI te beneficiarás de aprender un poco sobre los type hints. + +[Lectura Obligatoria](https://fastapi.tiangolo.com/es/python-types/) + +--- + +## algo mas de Python + +### 1. **Tupla (`tuple`)** + +Una **tupla** es una secuencia ordenada de elementos inmutables. No puedes modificar, añadir o eliminar elementos de una tupla una vez que ha sido creada. + +- **Características** : +- Inmutable: No se puede modificar una vez creada. +- Puede contener elementos duplicados. +- Puede almacenar cualquier tipo de datos. + +```python +#Definición de una tupla +mi_tupla = (1, 2, 3, "a", "b") +print(mi_tupla) # Salida: (1, 2, 3, 'a', 'b') + +#Accediendo a un elemento +print(mi_tupla[0]) # Salida: 1 + +#Intentar modificar una tupla lanzará un error +mi_tupla[0] = 100 # Esto generaría un error +``` + +### 2. **Conjunto (`set`)** + +Un **conjunto** es una colección desordenada de elementos únicos. No permite duplicados, y sus elementos no están indexados ni ordenados. + +- **Características** : +- Desordenado: No tiene un orden específico. +- No se permite duplicados. +- Mutable: Puedes agregar y eliminar elementos. + +```python +# Definición de un set +mi_set = {1, 2, 3, 4, 5, 3, 2} +print(mi_set) # Salida: {1, 2, 3, 4, 5} (Los duplicados se eliminan) + +# Agregar un elemento +mi_set.add(6) +print(mi_set) # Salida: {1, 2, 3, 4, 5, 6} + +# Eliminar un elemento +mi_set.remove(3) +print(mi_set) # Salida: {1, 2, 4, 5, 6} + +``` + +### 3. **Lista (`list`)** + +Una **lista** es una colección ordenada y mutable de elementos. A diferencia de las tuplas, las listas pueden modificarse: puedes agregar, eliminar o cambiar sus elementos. + +- **Características** : +- Ordenada: Mantiene el orden de inserción de los elementos. +- Mutable: Los elementos pueden ser modificados. +- Puede contener elementos duplicados. + +```python +# Definición de una lista +mi_lista = [1, 2, 3, "a", "b", "a"] +print(mi_lista) # Salida: [1, 2, 3, 'a', 'b', 'a'] + +# Agregar un elemento +mi_lista.append(4) +print(mi_lista) # Salida: [1, 2, 3, 'a', 'b', 'a', 4] + +# Eliminar un elemento +mi_lista.remove("a") +print(mi_lista) # Salida: [1, 2, 3, 'b', 'a', 4] + +# Modificar un elemento +mi_lista[0] = 100 +print(mi_lista) # Salida: [100, 2, 3, 'b', 'a', 4] + +``` + +### 4. **Diccionario (`dict`)** + +Un **diccionario** es una colección desordenada de pares clave-valor. Cada clave debe ser única, y se usa para acceder a su valor asociado. + +- **Características** : +- Desordenado (aunque en versiones recientes de Python mantiene el orden de inserción). +- Mutable: Se pueden agregar, eliminar y modificar los pares clave-valor. +- Las claves deben ser únicas, pero los valores pueden repetirse. + +```python +# Definición de un diccionario +mi_dict = {"nombre": "Cristian", "edad": 30, "ciudad": "Buenos Aires"} +print(mi_dict) # Salida: {'nombre': 'Cristian', 'edad': 30, 'ciudad': 'Buenos Aires'} + +# Acceder a un valor por su clave +print(mi_dict["nombre"]) # Salida: Cristian + +# Agregar un nuevo par clave-valor +mi_dict["profesion"] = "Programador" +print(mi_dict) # Salida: {'nombre': 'Cristian', 'edad': 30, 'ciudad': 'Buenos Aires', 'profesion': 'Programador'} + +# Modificar un valor +mi_dict["edad"] = 31 +print(mi_dict) # Salida: {'nombre': 'Cristian', 'edad': 31, 'ciudad': 'Buenos Aires', 'profesion': 'Programador'} + +# Eliminar un par clave-valor +del mi_dict["ciudad"] +print(mi_dict) # Salida: {'nombre': 'Cristian', 'edad': 31, 'profesion': 'Programador'} + +``` diff --git a/part-01-hello-world/app/main.py b/part-01-hello-world/app/main.py index 231e708..dcc624b 100644 --- a/part-01-hello-world/app/main.py +++ b/part-01-hello-world/app/main.py @@ -1,4 +1,7 @@ from fastapi import FastAPI, APIRouter +from datetime import date +from datetime import datetime +import uuid app = FastAPI(title="Recipe API", openapi_url="/openapi.json") @@ -11,7 +14,91 @@ def root() -> dict: """ Root GET """ - return {"msg": "Hello, World!"} + return {"msg": "Hello, Policies"} + + +@api_router.get("/health", status_code=200) +def health() -> dict: + """ + Health Check + """ + return {"status": "ok"} + +@api_router.get("/quiensos", status_code=200) +def health() -> dict: + """ + Quien sos Check + """ + return {"mensaje": "I'm a API an this is one endpoint"} + +@api_router.get("/quiensos/{name}", status_code=200) +async def health(name: str) -> dict: + """ + interactuando con un parametro Check + """ + return {"mensaje": "I'm a FastAPI y vos " + name + " esta enviando un request al que estoy respondiendo"} + + +@api_router.get("/hoy", status_code=200) +async def get_cowsay() -> dict: + """ + Fecha Check + """ + #Día actual + today = date.today() + + #Fecha actual + now = datetime.now() + + return {"status": "ok", "today": today, "now": now} + +@api_router.get("/fibo/{n}", status_code=200) +async def get_fibo(n: int) -> dict: + """ + Devuelve un diccionario con la serie de Fibonacci hasta el n-ésimo término. + + :param n: El número de términos de la serie que se desea obtener. + :return: Diccionario con los términos de la serie Fibonacci. + """ + if n < 1: + return {"error": "El número debe ser mayor o igual a 1"} + + fib_series = {0: 0, 1: 1} + for i in range(2, n): + fib_series[i] = fib_series[i - 1] + fib_series[i - 2] + + return {i: fib_series[i] for i in range(n)} + + +#uuid +def generate_uuid_from_number(number: int) -> str: + """ + Genera un UUID basado en un número dado. + + :param number: Un número entero. + :return: Un UUID generado a partir del número. + """ + # Convertir el número a una cadena + number_str = str(number) + + # Generar un UUID a partir del espacio de nombres y la cadena del número + generated_uuid = uuid.uuid5(uuid.NAMESPACE_DNS, number_str) + + return str(generated_uuid) + + +@app.get("/uuid/{number}") +async def get_uuid_from_number(number: int) -> dict: + """ + Endpoint que recibe un número y devuelve un UUID generado a partir de ese número. + + :param number: Un número entero. + :return: Un diccionario con el UUID generado. + """ + generated_uuid = generate_uuid_from_number(number) + return {"uuid": generated_uuid, "valid_period": "24 hours"} + + app.include_router(api_router) diff --git a/part-02-path-parameters/app/main.py b/part-02-path-parameters/app/main.py index 293a2f8..d6b148b 100644 --- a/part-02-path-parameters/app/main.py +++ b/part-02-path-parameters/app/main.py @@ -1,5 +1,6 @@ from fastapi import FastAPI, APIRouter +from typing import Optional RECIPES = [ { @@ -20,6 +21,25 @@ "source": "Serious Eats", "url": "http://www.seriouseats.com/recipes/2011/02/cauliflower-and-tofu-curry-recipe.html", }, + { + "id": 4, + "label": "Masas para sopaipillas", + "source": "Recetas Gratis", + "url": "https://www.recetasgratis.net/receta-de-masas-para-sopaipillas-77646.html", + }, + { + "id": 5, + "label": "Causa limeña", + "source": "Recetas Gratis", + "url": "https://www.recetasgratis.net/receta-de-causa-limena-31268.html", + }, + { + "id": 6, + "label": "Torta de manzana sin harina", + "source": "Recetas Gratis", + "url": "https://www.recetasgratis.net/receta-de-torta-de-manzana-sin-harina-77668.html", + }, + ] @@ -45,8 +65,33 @@ def fetch_recipe(*, recipe_id: int) -> dict: """ result = [recipe for recipe in RECIPES if recipe["id"] == recipe_id] - if result: + # if result: + # return result[0] + try: return result[0] + except IndexError: + return {"status": 404, "error": "Recipe not found"} + + + +# New addition, query parameter +# https://fastapi.tiangolo.com/tutorial/query-params/ +@api_router.get("/search/", status_code=200) +def search_recipes( + keyword: Optional[str] = None, max_results: Optional[int] = 10 +) -> dict: + """ + Search for recipes based on label keyword + """ + if not keyword: + # we use Python list slicing to limit results + # based on the max_results query parameter + return {"results": RECIPES[:max_results]} + + results = filter(lambda recipe: keyword.lower() in recipe["label"].lower(), RECIPES) + return {"results": list(results)[:max_results]} + + app.include_router(api_router) diff --git a/part-04-pydantic-schemas/app/main.py b/part-04-pydantic-schemas/app/main.py index a76a0ff..bf17c83 100644 --- a/part-04-pydantic-schemas/app/main.py +++ b/part-04-pydantic-schemas/app/main.py @@ -1,4 +1,4 @@ -from fastapi import FastAPI, APIRouter, Query +from fastapi import FastAPI, APIRouter, Query, HTTPException from typing import Optional @@ -28,8 +28,12 @@ def fetch_recipe(*, recipe_id: int) -> dict: """ result = [recipe for recipe in RECIPES if recipe["id"] == recipe_id] - if result: - return result[0] + # Si no se encuentra, lanzamos una excepción con código 404 + # if not result: + # raise HTTPException(status_code=404, detail=f"Recipe with ID {recipe_id} not found") + + # Si se encuentra, devolvemos la receta (el primer elemento de la lista) + return result[0] # Updated using the FastAPI parameter validation `Query` class @@ -59,7 +63,13 @@ def search_recipes( results = filter(lambda recipe: keyword.lower() in recipe["label"].lower(), RECIPES) return {"results": list(results)[:max_results]} + # results = list(filter(lambda recipe: keyword.lower() in recipe["label"].lower(), RECIPES)) + # if not results: + # raise HTTPException(status_code=404, detail=f"Recipe with LABEL {keyword} not found") + + #return {"results": results[:max_results]} + # New addition, using Pydantic model `RecipeCreate` to define # the POST request body diff --git a/part-04-pydantic-schemas/app/recipe_data.py b/part-04-pydantic-schemas/app/recipe_data.py index d5434fe..7ee499a 100644 --- a/part-04-pydantic-schemas/app/recipe_data.py +++ b/part-04-pydantic-schemas/app/recipe_data.py @@ -17,4 +17,22 @@ "source": "Serious Eats", "url": "http://www.seriouseats.com/recipes/2011/02/cauliflower-and-tofu-curry-recipe.html", }, + { + "id": 4, + "label": "Masas para sopaipillas", + "source": "Recetas Gratis", + "url": "https://www.recetasgratis.net/receta-de-masas-para-sopaipillas-77646.html", + }, + { + "id": 5, + "label": "Causa limeña", + "source": "Recetas Gratis", + "url": "https://www.recetasgratis.net/receta-de-causa-limena-31268.html", + }, + { + "id": 6, + "label": "Torta de manzana sin harina", + "source": "Recetas Gratis", + "url": "https://www.recetasgratis.net/receta-de-torta-de-manzana-sin-harina-77668.html", + }, ] diff --git a/part-04-pydantic-schemas/app/schemas.py b/part-04-pydantic-schemas/app/schemas.py index e1bb430..59aba57 100644 --- a/part-04-pydantic-schemas/app/schemas.py +++ b/part-04-pydantic-schemas/app/schemas.py @@ -18,4 +18,4 @@ class RecipeCreate(BaseModel): label: str source: str url: HttpUrl - submitter_id: int + #submitter_id: int diff --git a/part-05-basic-error-handling/app/templates/index.html b/part-05-basic-error-handling/app/templates/index.html new file mode 100644 index 0000000..d0a195c --- /dev/null +++ b/part-05-basic-error-handling/app/templates/index.html @@ -0,0 +1,50 @@ + + + + + + +
+
+
+

+ Recipes - Better than all the REST +

+

+ Latest recipes...

+
+ {% for recipe in recipes %} +
+
+
+

+ {{recipe.label}} +

+

+ + + + + {{recipe.source}} +

+
+
+
+
+

+ View Recipe +

+ +
+
+
+ {% endfor %} +
+
+ + diff --git a/part-06-jinja-templates/app/main.py b/part-06-jinja-templates/app/main.py index c2ae746..892af39 100644 --- a/part-06-jinja-templates/app/main.py +++ b/part-06-jinja-templates/app/main.py @@ -62,8 +62,13 @@ def search_recipes( # based on the max_results query parameter return {"results": RECIPES[:max_results]} - results = filter(lambda recipe: keyword.lower() in recipe["label"].lower(), RECIPES) - return {"results": list(results)[:max_results]} + results = list(filter(lambda recipe: keyword.lower() in recipe["label"].lower(), RECIPES)) + if not results: + raise HTTPException(status_code=404, detail=f"Recipe with LABEL {keyword} not found") + + return {"results": results[:max_results]} + + @api_router.post("/recipe/", status_code=201, response_model=Recipe) diff --git a/part-06-jinja-templates/app/recipe_data.py b/part-06-jinja-templates/app/recipe_data.py index d5434fe..7ee499a 100644 --- a/part-06-jinja-templates/app/recipe_data.py +++ b/part-06-jinja-templates/app/recipe_data.py @@ -17,4 +17,22 @@ "source": "Serious Eats", "url": "http://www.seriouseats.com/recipes/2011/02/cauliflower-and-tofu-curry-recipe.html", }, + { + "id": 4, + "label": "Masas para sopaipillas", + "source": "Recetas Gratis", + "url": "https://www.recetasgratis.net/receta-de-masas-para-sopaipillas-77646.html", + }, + { + "id": 5, + "label": "Causa limeña", + "source": "Recetas Gratis", + "url": "https://www.recetasgratis.net/receta-de-causa-limena-31268.html", + }, + { + "id": 6, + "label": "Torta de manzana sin harina", + "source": "Recetas Gratis", + "url": "https://www.recetasgratis.net/receta-de-torta-de-manzana-sin-harina-77668.html", + }, ] diff --git a/part-07-database.zip b/part-07-database.zip new file mode 100644 index 0000000000000000000000000000000000000000..abdc8cebfa564c235fc86ca0baad95ba73ce6a6c GIT binary patch literal 68513 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 literal 0 HcmV?d00001 diff --git a/part-07-database/alembic.ini b/part-07-database/alembic.ini old mode 100755 new mode 100644 index 921aaf1..2eb3ec3 --- a/part-07-database/alembic.ini +++ b/part-07-database/alembic.ini @@ -68,4 +68,4 @@ formatter = generic [formatter_generic] format = %(levelname)-5.5s [%(name)s] %(message)s -datefmt = %H:%M:%S +datefmt = %H:%M:%S \ No newline at end of file diff --git a/part-07-database/app/backend_pre_start.py b/part-07-database/app/backend_pre_start.py index 3363a41..ce14228 100644 --- a/part-07-database/app/backend_pre_start.py +++ b/part-07-database/app/backend_pre_start.py @@ -1,8 +1,13 @@ import logging +import sys +import os -from tenacity import after_log, before_log, retry, stop_after_attempt, wait_fixed +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from tenacity import after_log, before_log, retry, stop_after_attempt, wait_fixed # type: ignore + +from db.session import SessionLocal -from app.db.session import SessionLocal logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) diff --git a/part-07-database/app/db/init_db.py b/part-07-database/app/db/init_db.py index 4a15058..87e32f7 100644 --- a/part-07-database/app/db/init_db.py +++ b/part-07-database/app/db/init_db.py @@ -7,7 +7,7 @@ logger = logging.getLogger(__name__) -FIRST_SUPERUSER = "admin@recipeapi.com" +FIRST_SUPERUSER = "admin@remanofe.com" # make sure all SQL Alchemy models are imported (app.db.base) before initializing DB # otherwise, SQL Alchemy might fail to initialize relationships properly @@ -23,9 +23,10 @@ def init_db(db: Session) -> None: user = crud.user.get_by_email(db, email=FIRST_SUPERUSER) if not user: user_in = schemas.UserCreate( - full_name="Initial Super User", - email=FIRST_SUPERUSER, - is_superuser=True, + first_name="Initial", + surname="Superuser", + email=FIRST_SUPERUSER, + is_superuser=True, ) user = crud.user.create(db, obj_in=user_in) # noqa: F841 else: diff --git a/part-07-database/app/db/session.py b/part-07-database/app/db/session.py index d04453e..0fb0b3f 100644 --- a/part-07-database/app/db/session.py +++ b/part-07-database/app/db/session.py @@ -1,12 +1,26 @@ -from sqlalchemy import create_engine +from sqlalchemy import create_engine, text from sqlalchemy.orm import sessionmaker +from sqlalchemy.exc import OperationalError -SQLALCHEMY_DATABASE_URI = "sqlite:///example.db" +# Cambiamos la URI de conexión para PostgreSQL +SQLALCHEMY_DATABASE_URI = "postgresql://cchiera:Chiera+3@10.100.1.80:5432/remanofe" # 1 - -engine = create_engine( - SQLALCHEMY_DATABASE_URI, - # required for sqlite - connect_args={"check_same_thread": False}, +# Creamos el motor de la base de datos +engine = create_engine( # 2 + SQLALCHEMY_DATABASE_URI + # No es necesario 'connect_args' para PostgreSQL ) -SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +# Creamos la sesión +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) # 4 + +def test_connection(): + try: + # Creamos una sesión para probar la conexión + with SessionLocal() as session: + session.execute(text("SELECT 1")) # Ejecuta una consulta de prueba + print("Conexión exitosa.") + except OperationalError as e: + print("Error de conexión:", e) + +#test_connection() \ No newline at end of file diff --git a/part-07-database/app/initial_data.py b/part-07-database/app/initial_data.py index 9c9153e..8662937 100644 --- a/part-07-database/app/initial_data.py +++ b/part-07-database/app/initial_data.py @@ -1,9 +1,13 @@ import logging + from app.db.base import Base from app.db.init_db import init_db from app.db.session import SessionLocal + + + logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) diff --git a/part-07-database/prestart.sh b/part-07-database/prestart.sh index 006770a..60eb1b5 100755 --- a/part-07-database/prestart.sh +++ b/part-07-database/prestart.sh @@ -6,5 +6,6 @@ python ./app/backend_pre_start.py # Run migrations alembic upgrade head + # Create initial data in DB python ./app/initial_data.py \ No newline at end of file