2011-06-25 9 views
16

Korzystanie SQLAlchemy 0.7.1 i MySQL 5.1 bazy danych, mam wiele-do-wielu relacja skonfigurować w następujący sposób:SQLAlchemy: Filtr z członkostwa w co najmniej jeden wiele-do-wielu pokrewnych stół

user_groups = Table('user_groups', Base.metadata, 
    Column('user_id', String(128), ForeignKey('users.username')), 
    Column('group_id', Integer, ForeignKey('groups.id')) 
) 

class ZKUser(Base, ZKTableAudit): 
    __tablename__ = 'users' 

    username = Column(String(128), primary_key=True) 
    first_name = Column(String(512)) 
    last_name = Column(String(512)) 

    groups = relationship(ZKGroup, secondary=user_groups, backref='users') 

class ZKGroup(Base, ZKTableAudit): 
    __tablename__ = 'groups' 

    id   = Column(Integer, primary_key=True) 
    name  = Column(String(512)) 

Użytkownicy mogą należeć do wielu grup, a grupy mogą zawierać wielu użytkowników.

Próbuję wykonać zapytanie SQLAlchemy, które zwraca tylko tych użytkowników, którzy należą do co najmniej jednej grupy z listy grup.

Grałem z funkcją in_, ale to wydaje się działać tylko przy testowaniu wartości skalarnych dla członkostwa na liście. Nie jestem pisarzem SQL, więc nawet nie wiem, jakiego rodzaju wymagałoby to oświadczenie SELECT.

Odpowiedz

29

OK, po wielu badań, zdałem sobie sprawę, że to była moja własna niewiedza SQL terminologii trzymał mnie z powrotem. Moje poszukiwania rozwiązania polegającego na znalezieniu użytkowników należących do "co najmniej jednej" listy grup powinny być znalezieniem użytkowników należących do "dowolnej" listy grup. Funkcja any ORM z SQLAlchemy robi dokładnie to, co potrzebne, tak jak poniżej:

session.query(ZKUser).filter(ZKUser.groups.any(ZKGroup.id.in_([1,2,3]))) 

Ten kod emituje ten SQL (na MySQL 5.1):

SELECT * FROM users 
WHERE EXISTS (
    SELECT 1 FROM user_groups, groups 
    WHERE users.id = user_groups.contact_id 
     AND groups.id = user_groups.group_id 
     AND groups.id IN (%s, %s, %s) 
    ) 
+0

Jak filtrować według członkostwa we wszystkich tabelach powiązanych wiele do wielu? – WebPal

+1

Twoja odpowiedź odnosi się do 'ZKContact' i' contacts', ale nie znajdują się one w twoim kodzie w pytaniu. Miałeś na myśli ZKUser? –

+0

W odpowiedzi na pytanie o oddanie głos używają różnych słów kluczowych niż pytań, a trudno jest określić, jakie jest możliwe rozwiązanie. – Drachenfels

0

Można użyć in_:

session.query(ZKUser).filter(ZKGroup.id.in_([1,2])).all() 
+0

dam ten strzał, kiedy wrócę do biura po weekend. To wygląda obiecująco. Podoba mi się również zbieg moich nazw klas i nazwa użytkownika. :) Mój "ZK" oznacza Zookeeper, nazwę mojej aplikacji. Łączy się z interfejsem API MailChimp. – CoreDumpError

+0

Niestety, to nie zadziałało. Myślę, że dzieje się tak dlatego, że nie robi to rzeczywistego sprzężenia, więc SQL, który jest emitowany przez to, nie odfiltrowuje niepasujących użytkowników. Sprawdziłem dokumenty na temat SQLAlchemy, dotyczące łączenia przy stołach od wielu do wielu, ale jestem całkowicie zagubiony. – CoreDumpError

+1

Możesz spróbować zrobić 'session.query (ZKUser) .join (ZKGroup) .filter (..)'. Powinien działać w wersji 0.7.2, afaik. – zeekay

3

Według the docs for any, zapytanie będzie działać szybciej, jeśli zamiast tego używa się jawnie join:

Ponieważ dowolne() używa skorelowanego podzapytania, jego wydajność nie jest tak dobra w porównaniu z dużymi tabelami docelowymi jako to z użyciem sprzężenia.

W twoim przypadku, można zrobić coś takiego:

users = (
    session.query(ZKUser) 
    .join(user_groups) 
    .filter(user_groups.columns.group_id.in_([1, 2, 3])) 
) 

ten emituje SQL jak:

SELECT * 
FROM users 
JOIN user_groups ON users.id = user_groups.user_id 
WHERE user_groups.group_id IN (1, 2, 3) 
Powiązane problemy