gino icon indicating copy to clipboard operation
gino copied to clipboard

Load Models from JOIN as Dictionary

Open lsabi opened this issue 5 years ago • 2 comments

  • GINO version: 1.0.1
  • Python version: 3.8.2
  • asyncpg version: 0.20.1

Description

The case is simple: a table represents product (each with one brand attribute), pointing at a brand table.

When joining the two tables, I would like to get the brand information in the product, as a dictionary (or JSON, doesn't matter since I'm developing a rest api).

The same case could be applied to product and category, where in this case a many-to-one relationship is used. Instead of having a dictionary representing the category, an array of dictionaries (representing the categories) could be used.

What I Did

    products = orm.Product
    products = products.join(orm.Brand, orm.Brand.id == orm.Product.brand).select()

    if len(category) > 0:
        products = products.where(orm.ProductCategory.category.in_(category))

    if len(brand) > 0:
        products = products.where(orm.Product.brand.in_(brand))

    if q != "":
        products = products.where(orm.Product.name.like("%" + q + "%"))

    products = await products.limit(limit).order_by(orm.Product.name).gino.load(orm.Product.load(brand=orm.Brand.to_dict())).all()

In this case an error is triggered on to_dict() method since it requires the positional argument 'self'

Just returning an attribute is possible as of now (just change to_dict() into an attribute name), but I need the complete information to be JSON serializable (via a dictionary or array of dictionaries) in order to be consumed by a client.

Can this be achieved, in a simple manner, with GINO?

Thanks

lsabi avatar Sep 15 '20 21:09 lsabi

How about just orm.Product.load(brand=orm.Brand)? It'll set orm.Brand instances on Product.brand for you.

If you really want to leverage the loader system to serialize the rows directly, you may want to take a look at the CallableLoader and write your own serializer:

subloader = orm.Product.load(brand=orm.Brand)

def serialize(row, context):
    product = subloader.do_load(row, context)
    return dict(product.to_dict(), brand=product.brand.to_dict())

serialized_products = await query.gino.load(serialize).all()

I didn't test this, but I think it explains the idea.

fantix avatar Sep 25 '20 03:09 fantix

Thanks for the answer.

In my case, I'm using gino with fastapi, so I'm returning the result as a JSON. With the first method, simply taking the result and converting it does not work

res = [x.to_dict() for x in products]

Gives me an error because orm.Brand is not serializable. to_dict() does not perform a deep conversion to dictionary so that it can be transformed into a serializable object.

I was aware of the loaders, but didn't think they fit my purpose. Will try and let you know.

Thanks

lsabi avatar Sep 25 '20 20:09 lsabi