2015-03-14 3 views
0
class Parent(BaseModel): 
    name = peewee.CharField() 

class Child(BaseModel): 
    name = peewee.CharField() 
    parent = peewee.ForeignKeyField(Parent, related_name='children') 

parent = Parent.create(name="Parent1") 
child1 = Child.create(name="Child1", parent=parent) 
chilld2 = Child.create(name="Child2", parent=parent) 

query = (Parent.select() 
     .join(Child)) 

for p in query: 
    print p.name 
    for c in p.children: 
     print "\t",c.name 

Это приводит следующий вывод:Peewee возвращает несколько объектов во внутреннем соединении

Parent1 
    Child1 
    Child2 
Parent1 
    Child1 
    Child2 

я ожидал:

Parent1 
    Child1 
    Child2 

Казалось бы, что я итерация через SQL результат в отличие от объекта в смысле ORM. Учитывая, что в запросе есть только один «родительский объект», с точки зрения ORM, как я могу изменить свой запрос или мою итерацию по запросу, чтобы получить ожидаемые результаты?

Я хочу вернуть запрос к моему шаблону, как в примере примера peewee, и просто перебрать его, чтобы отобразить объект, но если я это сделаю, он отобразит один и тот же объект дважды (или n раз для n числа связанных дети).

Я знаю, что я могу сделать следующее:

p = query.get() 
print p.name 
for c in p.children: 
    print "\t", c.name 

Но когда у меня есть несколько родителей и просто хочу проходные их всех, я не могу определить, сколько родителей есть. Метод запроса count возвращает счетчик результирующего набора, в отличие от количества их родительских объектов.

Это также работа вокруг:

pars = [] 
for p in query: 
    if p not in pars: 
     pars.append(p) 

for p in pars: 
    print p.name 
    for c in p.children: 
     print "\t", c.name 
+0

Попробуйте 'запрос = (Child.select() .join (Parent))' и читайте о [join] (https://en.wikipedia.org/wiki/Join_%28SQL%29). Существуют разные виды соединения. Я думаю, что «Естественное соединение» вернет вам набор. Появляется, как вы более осведомлены в этой области. – User

ответ

2

Есть две проблемы с вашим кодом.

Во-первых, хотя вы присоединились к Child, вызов parent.children выполнит дополнительный отдельный запрос.

Из-за первой проблемы вы ошибаетесь, что делает внешний цикл, который просто дает вам родительскую часть Parent1 + Child1, Parent1 + Child2.

Чтобы исправить это, вы можете сделать использование prefetch или aggregate_rows:

# Execute a single query and de-dupe the duplicated parent rows. 
query = Parent.select(Parent, Child).join(Child).aggregate_rows() 
for parent in query: 
    print parent.name 
    for child in parent.children: 
     print childname 

# Execute a query for each joined table. Generally more efficient 
# than aggregate_rows(). 
query = prefetch(Parent.select(), Child) 
for parent in query: 
    print parent.name 
    for child in parent.children_prefetch: # NOTE the `_prefetch` 
     print child.name 

Это документировано обширно: http://docs.peewee-orm.com/en/latest/peewee/querying.html#avoiding-n-1-queries

+0

Привет Coleifer. Неудивительно, что мои запросы были медленными :). Следующее: 'query = Parent.select (родительский, дочерний) .join (дочерний элемент) .aggregate_rows()' приводит к 'AttributeError: объект SelectQuery 'не имеет атрибута' aggregate_rows''. Любая идея почему? Предвыборка работает для меня. В документации вы указали, что «Обратите внимание, что ни запрос пользователя, ни запрос Tweet не содержали предложение JOIN. При использовании prefetch() вам не нужно указывать соединение». Мне нужно подключить таблицу A к B и таблицу B к таблицам X и Z. Однако таблицы X и Z имеют FK для B и A, но я хочу, чтобы соединение было на B.id. –

+0

Будут ли эти запросы работать успешно? –

+0

Я смог 'для a в peewee.prefetch (A.select(), B.select(), X.select(), Y.select())' успешно. –