Search code examples
python-3.7peewee

Using alias for joining multiple tables in peewee


I have one entries table on which I have to loop over 40k records and show their data in table. As that table is related to three foreign key data, I tried to add those data in the base query itself to reduce extra queries.

I think my base query containing something like <Model: Customer> which is causing the issue.

lets say this are my classes

class BaseModel(Model):
    class Meta:
        database = database


class Customer(BaseModel):
    name = TextField()


class Farmer(BaseModel):
    name = TextField(null=False)


class Product(BaseModel):
    name = TextField()


class Entries(BaseModel):
    farmer = ForeignKeyField(Farmer, backref='entries')
    customer = ForeignKeyField(Customer, backref='entries')
    product = ForeignKeyField(Product, backref='product')
    other = TextField()

    class Meta:
        table_name = 'entries'

The query I tried

entries = Entries.select(
    Entries,
    Farmer.name.alias('farmer_name'),
    Customer.name.alias('customer_name'),
    Product.name.alias('product_name')
).join(Farmer, Customer, Product)

It generates the sql

SELECT "t1"."id", "t1"."farmer_id", "t1"."customer_id", "t1"."product_id", "t1"."other", "t2"."name" AS "farmer_name", "t3"."name" AS "customer_name", "t4"."name" AS "product_name" FROM "entries" AS "t1" <Model: Customer> "farmer" AS "t2" ON "product" AS "t4"

I also tried seperate joins using switch method but no success.

Is it possible to fetch the data within same instance or I have to do seperate queries only?

update

When I do the following query with dicts(), it works fine, but without it, it shows AttributeError: 'Entries' object has no attribute 'farmer_name'.

Query:

entries = Entries.select(
    Entries,
    Farmer.name.alias('farmer_name'),
    Customer.name.alias('customer_name'),
    Product.name.alias('product_name')
).join(Farmer, on=(Entries.farmer == Farmer.id)) \
    .join(Customer, on=(Entries.customer == Customer.id)) \
    .join(Product, on=(Entries.product == Product.id))

Solution

  • Peewee, by default, will put those aliases on the actual joined model, e.g.

    entries = (Entries
      .select(
        Entries,
        Farmer.name.alias('farmer_name'),
        Customer.name.alias('customer_name'),
        Product.name.alias('product_name'))
      .join_from(Entry, Farmer)
      .join_from(Entry, Customer)
      .join_from(Entry, Product))
    for e in entries:
        print(e.farmer.farmer_name, e.customer.customer_name)  # etc
    

    If you just want everything on the entry, then add ".objects()" which prevents the graph generation and slaps everything on "Entry":

    for e in entries.objects():  # Added .objects()
        print(e.farmer_name, e.customer_name)  # etc
    

    Doc: http://docs.peewee-orm.com/en/latest/peewee/relationships.html#selecting-from-multiple-sources