emmett-framework / emmett

The web framework for inventors
BSD 3-Clause "New" or "Revised" License
1.08k stars 72 forks source link

Selecting fields when joining relationships #460

Closed robinvandernoord closed 1 year ago

robinvandernoord commented 1 year ago

I'm having some trouble with many-to-many relationships in the Emmett ORM. My database schema was copied from the Relations documentation, so I have a User, Group and Membership table. User 1 is connected to Groups 1, 2 and 3 via the Membership table.

An example query is:

data = db(db.User.id == 1).join("groups").select()
user_1_groups = data.first().groups()

In this case, user_1_groups is filled with the three right groups. I looked at the db.execution_timings and it appears the data is collected in one database query, which is nice but not really well documented. so when I was looking at data, it seemed kind of empty. However, I seem to be able to access all user and group info now. My problem starts when I want to select specific fields. This is required because for some tables, we store large blobs of data (which may not be ideal but it's in there) and we don't want to select all data every time.

For example, I may want only the User and Group id's:

    data = db(db.User.id == 1).join("groups").select(
        db.User.id,
        db.Group.id,
    )

This leads to the following error: image

When omitting the User.id, the Select works but now the data structure is different (one row per combination of User and Group):

    data = db(db.User.id == 1).join("groups").select(
        # db.User.id,
        db.User.name,
        db.Group.id,
        db.Group.name,
    )
# ->
# [<Row {'users': {'name': 'First User'}, 'groups': {'id': 1, 'name': 'Group 1'}}>, <Row {'users': {'name': 'First User'}, 'groups': {'id': 2, 'name': 'Group 2'}}>, <Row {'users': {'name': 'First User'}, 'groups': {'id': 3, 'name': 'Group 3'}}>]

Summary: How can I join many-to-many tables and only select the fields I need?

gi0baro commented 1 year ago

@robinvandernoord the intended usage of .join() is to work with structured records.

The vast majority of ORM implementations in Emmett requires a "concrete" record to work, meaning you need all the involved fields.

For custom queries, the intended usage is the standard DSL, so in your case:

data = db(
    (db.User.id == 1) & 
    (db.Membership.user == db.User.id) & 
    (db.Group.id == db.Membership.group)
).select(db.User.name, db.Group.id, db.Group.name)