Issue: Preserve model structure when get dicts from SelectQuery
Author: gugadev
State: closed
Created: 2017-05-13 15:59:44

I have three models:

```python
class User(Base):
    name = CharField(null=False)
    photo = BlobField()
    email = CharField(unique=True, null=False)
    password = CharField(null=False)
    website = CharField(unique=True, null=False)
    role = CharField(null=False, default="user")
    reason = TextField(null=True)
    state = CharField(null=False, default="waiting")
    created_at = DateTimeField(null=False, default=datetime.now())

class Channel(Base):
    name = CharField(unique=True, null=False)
    cost = DoubleField(null=False)
    passback = CharField(null=False)

class Campaign(Base):
    name = CharField(null=False)
    user = ForeignKeyField(User, null=False)
    channel = ForeignKeyField(Channel, null=False)
    target = CharField(null=False)
    reason = TextField(null=True)
    description = TextField(null=True)
    state = CharField(null=False, default="waiting")
    created_at = DateTimeField(null=False, default=datetime.now())
```

If a made a simple query I get the campaigns that I want, joining User and Channel models:

```python
qs = Box(request.args, default_box=True)
page = int(qs.page[0])
state = qs.state[0]
query = (Campaign
               .select(Campaign, User, Channel)
               .join(User, on=(Campaign.user == User.id))
               .join(Channel, on=(Campaign.channel == Channel.id))
               .where(Campaign.state == state)
               .order_by(Campaign.name)
               .paginate(page, 10))
```

At this point I can simple iterate the query and get the data of joined models:

```ptyhon
for cursor in query:
    print(cursor.user.website)  # http://johndoe.com
```

However, I need to return the query results in JSON, so, I did this (if I try to return the raw SelectQuery I get an *"maximum recursion level reached" from ujson*):

```python
return json(query.dicts())
```

And the result is this:

```javascript
[
  {
    "id": 5,
    "name": "Computers and technology",
    "user": 3,
    "channel": 5,
    "target": "http://janedoe.com",
    "reason": null,
    "description": "This is a campaign for my homepage",
    "state": "active",
    "created_at": 1494099722,
    "photo": "/avatars/jane-doe.jpg",
    "email": "jane.doe.68@mailna.in",
    "password": "$2y$10$p/BuVFoKi9F9SVOc2yJzA.CM6n88xHhMBipNShp4skv9.WBbYxgQ2",
    "website": "http://janedoe.com",
    "role": "USER",
    "cost": 0.04,
    "passback": "https://pcmag.com"
  }
]
```
All fields are mixed and, obviously, duplicated keys (name property of `User` and `Channel` are removed). This is not the expected result. Obviously, I can make a for loop to create a dict for each result and for model properties use `model_to_dict`:

```python
# convert a select query to a dicts array
    campaigns = []
    for row in query:
        campaign = {}
        campaign["name"] = row.name
        campaign["target"] = row.target
        campaign["user"] = model_to_dict(row.user)
        campaign["channel"] = model_to_dict(row.channel)
        campaign["description"] = row.description
        campaign["reason"] = row.reason
        campaign["state"] = row.state
        campaign["created_at"] = row.created_at
        campaigns.append(campaign)
```
But this means extra code and I like to keep things simple. So, the question is: exists some way to preserve the model structure when I convert the `SelectQuery` to dicts?

Just one hint: you need to call `switch()` here:

```python
query = (Campaign
               .select(Campaign, User, Channel)
               .join(User, on=(Campaign.user == User.id))
              .switch(Campaign)  # ADD THIS.
               .join(Channel, on=(Campaign.channel == Channel.id))
               .where(Campaign.state == state)
               .order_by(Campaign.name)
               .paginate(page, 10))
```

Secondly, why not try using the `model_to_dict` helper? I don't see why it would be more code or why you'd need to explicitly create the dictionary:

```python
campaigns = [model_to_dict(campaign, recurse=True) for campaign in query]
```

You are complety right. I don't need do that. I've applied what you do and works fine. Thanks for clarify this and forgive me if was a silly question, I have little time with Python. Thanks again; I going to close this thread.

Not silly at all, glad to help.

Hi @coleifer I didn't open a new issue because it's related with this.

I have a problem using `model_to_dict` on a join query where the foreign key is on inverse side.

```python
class User(Base):
    name = CharField(null=False)
    photo = CharField()
    email = CharField(unique=True, null=False)
    password = CharField(null=False)
    website = CharField(unique=True, null=False)
    role = CharField(null=False, default="user")
    reason = TextField(null=True)
    state = CharField(null=False, default="waiting")
    created_at = DateTimeField(null=False, default=datetime.now())

class Wallet(Base):
    user = ForeignKeyField(User, null=False, related_name="wallet")
    funds = DoubleField(null=False, default=0.0)
```
So, I did this query to get all users with their wallets:

```python
qs = Box(request.args, default_box=True)
    page = int(qs.page[0])
    state = qs.state[0]
    query = (User
             .select(User, Wallet)
             .join(Wallet)
             .switch(User)
             .where(User.state == state)
             .order_by(User.name)
             .paginate(page, 10))
```
If iterate the query accesing to the `wallet.funds` field, it works (show me available users funds). But when I try to convert it to dicts the wallet field is missing:

```python
users = [model_to_dict(user, recurse=True) for user in query]
for user in users:
        print(user.get("wallet", "There is no wallet"))  # prints "There is no wallet"
```

That's because, due to the way you've modeled the data, a User may have many wallets. So model_to_dict isn't able to infer, ahead-of-time, that a user will 0, 1, or many wallets. If you want to include back-references, just call `model_to_dict` with `backrefs=True` (in addition to `recurse=True`) and the wallets will be included as a list.

This is all very clearly documented, in my opinion: http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#model_to_dict

Please let me know if you found the docs confusing.