DaftAcademy / daftacademy-python_levelup-spring2021

19 stars 10 forks source link

pobieranie tylko niezbędnych kolumn i pakowanie ich do pydantic #44

Closed mateusz91t closed 3 years ago

mateusz91t commented 3 years ago

Czy jest bardziej efektywny sposób na pobieranie danych z tabel i pakowanie ich do pydantic tak, aby nie pobierać od razu całej tabeli, tylko wybrane kolumny? Chodzi o sytuację, gdy jedna klasa pydantic ma w sobie pole będące inną klasą pydantic.

Mam takie klasy pydantic'owe:

class CategoryIdName(BaseModel):
    CategoryID: PositiveInt
    CategoryName: constr(max_length=40)
    class Config:
        orm_mode = True

class Product(BaseModel):
    ProductID: PositiveInt
    ProductName: constr(max_length=40)
    Category: Optional[CategoryIdName]
    Discontinued: int
    class Config:
        orm_mode = True

i mam takie query, które nie chce się spakować do schemy Product -> Product.Category

def get_suppliers_products(supplier_id: int, db: Session):
    o = db.query(
        models.Product.ProductID,
        models.Product.ProductName,
        models.Product.Discontinued,
        # models.Category,
        models.Category.CategoryID,
        models.Category.CategoryName
    ).join(models.Category, models.Supplier).\
        filter(models.Supplier.SupplierID == supplier_id).\
        order_by(models.Product.ProductID)

    print(str(o.statement.compile(dialect=postgresql.dialect())))

    return o.all()

i to mi zwraca nulla w Category:

  {
    "ProductID": 4,
    "ProductName": "Chef Anton's Cajun Seasoning",
    "Category": null,
    "Discontinued": 0
  },
[...]

chociaż sql z printa wygląda spoko:

SELECT products."ProductID", products."ProductName", products."Discontinued", categories."CategoryID", categories."CategoryName"
FROM products JOIN categories ON categories."CategoryID" = products."CategoryID" JOIN suppliers ON suppliers."SupplierID" = products."SupplierID"
WHERE suppliers."SupplierID" = %(SupplierID_1)s ORDER BY products."ProductID"

Jeśli pobiorę komplet z tabeli categories:

    o = db.query(
        models.Product.ProductID,
        models.Product.ProductName,
        models.Product.Discontinued,
        models.Category
        # models.Category.CategoryID,
        # models.Category.CategoryName
    ).join(models.Category, models.Supplier).\
        filter(models.Supplier.SupplierID == supplier_id).\
        order_by(models.Product.ProductID)

    print(str(o.statement.compile(dialect=postgresql.dialect())))

    return o.all()

to działa:

  {
    "ProductID": 4,
    "ProductName": "Chef Anton's Cajun Seasoning",
    "Category": {
      "CategoryID": 2,
      "CategoryName": "Condiments"
    },
    "Discontinued": 0
  },

ale select nie wygląda zbyt dobrze z tego query - z tabeli categories pobrane jest select * ..... :

SELECT products."ProductID", products."ProductName", products."Discontinued", categories."CategoryID", categories."CategoryName", categories."Description", categories."Picture"
FROM products JOIN categories ON categories."CategoryID" = products."CategoryID" JOIN suppliers ON suppliers."SupplierID" = products."SupplierID"
WHERE suppliers."SupplierID" = %(SupplierID_1)s ORDER BY products."ProductID"
korowiov commented 3 years ago

Trzeba zobaczyć jak sqlalchemy sobie mapuje te kolumny. Zauważ, że w Category ma być zagnieżdżony obiekt. Przy query po kolumnach otrzymujesz obiekt z takimi kluczami:

Zrzut ekranu 2021-05-18 o 16 02 25

Zamiast takimi, gdy robisz query po całym obiekcie category:

Zrzut ekranu 2021-05-18 o 16 06 06

Gdyby schema wyglada tak np:

class Product(BaseModel):
    ProductID: PositiveInt
    ProductName: constr(max_length=40)
    CategoryID: PositiveInt
    CategoryName: constr(max_length=40)

to wtedy query po kolumnach powinno Tobie zabanglać.

mateusz91t commented 3 years ago

@korowiov , dzięki. Schema po kolumnach działa jak najbardziej, ale rozwiązanie zadania jest inne. Nie mam pomysłu, czy da się połączyć optymalne pobieranie danych z bazy z zagnieżdżonym jsonem instancji Category. Ale spoko - select * from categories działa, więc ten jeden raz mogę zrobić poprawnie, ale nie optymalnie 😄