Issue: Prefetch problem
Author: anjianshi
State: closed
Created: 2015-09-07 05:37:16

Is there a way to manually specify which foreign key to use, if a model has multiple foreign keys?

For example, there's 4 tables:

```
School
    id
    name

SchoolClass
    id
    school  (foreign key)
    name

Club
    id
    school  (foreign key)
    name

Student
    id
    school  (foreign key)
    school_class  (foreign key)
    club  (foreign key)
    name
```

I want got results like this:

``` json
[
   {
        "name": "school_name",
        "classes": [{"name": "class_name"}, ...],
        "clubs": [{"name": "club_name"}, ...],
        "students": [{"name": "student_name"}, ...],
    }
]
```

``` python
# but when I run the code:
school_pf = prefetch(School, SchoolClass, Club, Student)
for school in pf:
    # I can't find student_prefetch attribute in school object
    hasattr(school, "student_prefetch")   # false

    # because it's under the club object
    for club in school.club_prefetch:
        hasattr(club, "student_prefetch")  # true
```

If I can manually specify which foreign key to use, may be I can do something like this:

``` python
prefetch(School, SchoolClass, Club, (Student, School))
```

And the problem will resolve.

Thanks!


Unfortunately not right now, though that's a deficiency in the API I've definitely thought about before. I will think about improving it.


Ok!

I've done some fix, if someone else have this requirement, maybe they can use this as a temporary solution. ^_^

``` python
import peewee
from peewee import PrefetchResult, Query, Model, prefetch

def _prefetch_add_subquery(sq, subqueries):
    fixed_queries = [PrefetchResult(sq)]
    for i, subquery in enumerate(subqueries):
        # === custom ===
        if isinstance(subquery, tuple):
            subquery, target_model = subquery
        else:
            target_model = None
        # === /custom ===

        if not isinstance(subquery, Query) and issubclass(subquery, Model):
            subquery = subquery.select()
        subquery_model = subquery.model_class
        fkf = backref = None
        if target_model is None:    # custom
            for j in reversed(range(i + 1)):
                last_query = fixed_queries[j][0]
                last_model = last_query.model_class
                foreign_key = subquery_model._meta.rel_for_model(last_model)
                if foreign_key:
                    fkf = getattr(subquery_model, foreign_key.name)
                    to_field = getattr(last_model, foreign_key.to_field.name)
                else:
                    backref = last_model._meta.rel_for_model(subquery_model)

                if fkf or backref:
                    break
        else:
            # === custom ===
            last_query = first(fixed_queries, lambda q: q.model == target_model).query
            foreign_key = subquery_model._meta.rel_for_model(target_model)
            fkf = getattr(subquery_model, foreign_key.name)
            to_field = getattr(target_model, foreign_key.to_field.name)
            # === /custom ===

        if not (fkf or backref):
            raise AttributeError('Error: unable to find foreign key for '
                                 'query: %s' % subquery)

        if fkf:
            inner_query = last_query.select(to_field)
            fixed_queries.append(
                PrefetchResult(subquery.where(fkf << inner_query), fkf, False))
        elif backref:
            q = subquery.where(backref.to_field << last_query.select(backref))
            fixed_queries.append(PrefetchResult(q, backref, True))

    return fixed_queries

def first(seq, condition):
    for value in seq:
        if condition(value):
            return value
    return None

peewee.prefetch_add_subquery = _prefetch_add_subquery
```

Usage:

```
prefetch(School, SchoolClass, Club, (Student, School))
```