回答

收藏

使用SqlAlchemy关联属性过滤association_proxy

技术问答 技术问答 201 人阅读 | 0 人回复 | 2023-09-14

我有一个使用SA的 association_proxy 建模 的多对多( @2 z( m& E6 j8 U3 W! i6 k
(开发人员和项目)关系。集合(每个项目的开发人员和每个开发人员的项目)工作正常,但我需要过滤关联本身的属性(状态)。像这样的东西(不起作用): __+ C. B; G. M) {9 E9 y; [5 M* ^
activeDevelopers = s.query(Developer).filter_by(Developer.developerProjects.status == 'active').all()% C# H/ U) _1 f( e& p& F
我想念什么?这是完整的测试代码:
% R& p3 j9 ^9 V# J( S( i! q' p$ _import logging' z3 }7 x% I) W" v9 F, P. L
from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, ForeignKey
+ [# K6 n: p: Q5 Vfrom sqlalchemy.orm import relation, mapper, sessionmaker, U4 `2 o! a; `" g  w0 G- w
from sqlalchemy.sql import *
( E  ^! ?9 z+ i/ `from sqlalchemy.ext.associationproxy import association_proxy, }% `* ~6 \  l) U
log = logging.getLogger('nm_test')
, y5 m1 _7 q" f6 L" M! {logging.basicConfig(level=logging.DEBUG,
* L9 b/ D* I. c/ D! L0 H/ ?        format='%(asctime)s,%(msecs)03d %(levelname)s [%(filename)s.%(funcName)s @ %(lineno)d.%(thread)d] %(message)s')9 p; {7 c9 ~/ Z8 m+ R1 e8 l/ k, M
engine = create_engine('sqlite:///:memory:', echo = False, echo_pool = False). D* }6 u  H$ g# Z+ j5 t
meta = MetaData()
* _6 j  d) \& _% [) I# emeta.bind = engine: I2 m5 w7 ^8 w9 r5 R: \% m  q
developer_table = Table('developer', meta,/ D% u( l  \2 t# J
    Column('id', Integer, primary_key=True, autoincrement = False),/ {- t; @: e8 D, E! o" L* P
    Column('name', String),3 J6 L6 @8 h8 J+ E" s; l' x% G0 @
)
6 c( ?% P) K7 \1 \: ]; |) a  Nproject_table = Table('project', meta,' D8 [/ x0 C1 J, [7 B8 g
    Column('id', Integer, primary_key=True, autoincrement = True),0 d! e* N. v  v/ K9 }8 {: t! _/ N
    Column('name', String)
3 x) v; z" Y9 H0 a)
# {5 P- K9 c2 Vdeveloper_project_table = Table('developer_project', meta,6 w6 f* J+ x# z$ f
    Column('developer_id', Integer, ForeignKey('developer.id'), primary_key = True),# Q6 M! T5 D& j. o/ B: N
    Column('project_id', Integer, ForeignKey('project.id'), primary_key = True),5 ^/ o' a8 q! X8 y" N' s
    Column('status', String)
3 v* ]/ W" b3 h)
& j. e4 _1 d& [, }! oclass Developer(object):
' U( l0 {: h3 d) w; ]& k    projects = association_proxy('developerProjects', 'projects'), `- v# C+ w: H" X
    def __str__(self):
' _* d. f# u% \. m& U9 h        return 'Developer id:%i, name:%s' % (self.id, self.name)* W6 ]6 i0 }* V4 L0 w. \
class Project(object):1 P/ N+ Z0 A% f* }: Z) M
    developers = association_proxy('developerProjects', 'developers')
$ n* F6 U' p9 _: M6 O2 J$ d4 _    def __str__(self):4 ]: K/ L+ k7 E# l5 U
        return 'Project id:%i, name:%s' % (self.id, self.name)/ @' s6 H6 Y+ _5 p; h( i
class DeveloperProject(object):0 u$ Y! j$ T5 E+ i, z& m- l4 w1 o
    def __str__(self):
* w& {: H! e; ^3 ?2 w% c; Z3 n6 }        return 'DeveloperProject developer:%s, project:%s, status:%s' % (self.developer_id, self.project_id, self.status)
) K) m; R* X$ p( ~mapper(Developer, developer_table, properties = {" {' }7 J/ [" E# B1 n
    'developerProjects':relation(DeveloperProject, backref = "developers")! t# ?$ |; E" u
})
- \' S. Q4 i0 u* {mapper(Project, project_table, properties = {
: x) N* w9 P7 P9 l    'developerProjects':relation(DeveloperProject, backref = "projects")3 D" c9 T" E- ~
})
# j! v6 S, {  _: j1 e9 Imapper(DeveloperProject, developer_project_table)
# F; Z: _' n' G0 @, b5 `% w4 Pmeta.create_all(engine)
: v" `# g- u  a& X& L* dconn = engine.connect()% v% J) D% J/ ]9 h8 u; |0 L
conn.execute(project_table.insert(),[
: L3 ^: _3 r3 V$ N    {'name':'stackoverflow'},
1 y) [; o: Z1 p2 S' I: n7 `4 A    {'name':'superuser'},. j, n9 {- B$ g. P% j
])
3 a( s  D" u9 _5 lconn.execute(developer_table.insert(),[
- {( R$ u* P9 T1 w/ s% o    {'name':'John'},7 y1 O5 _" H" X: h# B/ u  V/ M
    {'name': 'TerryJ'},7 q$ x$ m/ i, V4 R( K% D& n
    {'name': 'TerryG'},( t2 r3 K* k( j' U  m
    {'name': 'Eric'},( Y* o/ V: m4 V, i/ z
    {'name': 'Graham'},# @3 M, F+ l! }/ R& M) \
])  i5 K* u" Z( q  t
conn.execute(developer_project_table.insert(),[5 s- }$ g& C3 y" r. h+ w! i
    {'developer_id':1, 'project_id':1, 'status':'active'},* o: Y# n/ n- b1 t  m
    {'developer_id':2, 'project_id':2, 'status':'inactive'},' q$ C/ @' ]" N, X
    {'developer_id':3, 'project_id':2, 'status':'active'},
  G5 a" Z8 r5 w$ j" k# ?    {'developer_id':4, 'project_id':1, 'status':'active'},/ f6 o2 `# v9 n+ T2 o/ w
    {'developer_id':4, 'project_id':2, 'status':'active'},/ c' C2 [2 m, R+ p$ q* {6 |8 z" Q
    {'developer_id':5, 'project_id':1, 'status':'active'},) u9 P$ k/ i8 {! H3 i* Q8 U
    {'developer_id':5, 'project_id':2, 'status':'inactive'},/ x" D' s: x8 O  L8 t3 P) b
])# G& f  r" S/ u' C: X! n
Session = sessionmaker(bind=engine)
4 Y9 ~1 w% @9 i/ ^7 F0 @, is = Session()
: v8 O$ g1 M8 p$ C/ T/ ldevelopers = s.query(Developer).all()
  E/ K9 E" d. w1 b! _projects = s.query(Project).all(); a7 V; V9 k# P! `0 c" p
for d in developers:
: C# g% ~* J2 n( J" i: K2 b+ `    log.debug(d)% [$ ^$ J% x' G6 U+ t
    for p in d.projects:' `+ x& J. e0 J7 E3 }; D) h
        log.debug('    %s' % p)
' L4 `+ N  h, m( n9 Y* w0 w" Xfor p in projects:4 y$ u2 y7 W$ H* _0 Z
    log.debug(p)
. |( _6 L. M: ~; M7 [9 Z  W    for d in p.developers:. K& |. R7 @1 N" C3 h! @
        log.debug('    %s' % d)
5 t+ U2 Y5 V6 k* H# does not work% z+ ~) m5 E1 ]% k
activeDevelopers = s.query(Developer).filter_by(Developer.developerProjects.status == 'active').all()& z, _6 d4 F/ b5 b% v) q
# AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object has an attribute 'status'
$ {2 q$ `! M6 S                - T' D; R: H' d& Q/ p5 Q" e" _
解决方案:& o* Y/ J& K5 y0 Q
                + }- m( z1 R$ L- x) _+ v1 o

0 s9 n+ p2 }2 Q& y6 V
) E6 j; |- ?; h9 O1 p) }3 F                关联代理的使用any()方法:
5 j$ C4 n4 J- B4 gs.query(Developer).filter(Developer.developerProjects.any(status='active'))
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则