2014-04-21 324 views
2

我在我的Flask應用程序中使用SQLAlchemy(0.9.4)。在應用程序中有兩個具有軟刪除支持的表。SQLAlchemy - 在自定義主連接關係的查詢中使用'aliased'

class A(SoftDeleteMixin, db.Model): 
    id = db.Column(db.BigInteger, primary_key=True) 

    b_id = db.Column(db.BigInteger, db.ForeignKey('b.id'), nullable=False) 
    b = soft_delete_relationship('B.id', 'A.b_id') 

class B(SoftDeleteMixin, db.Model): 
    id = db.Column(db.BigInteger, primary_key=True) 
    parent_id = db.Column(db.BigInteger, db.ForeignKey('b.id')) 

    parent = soft_delete_relationship(remote(id), parent_id, 'B.id', 'B.parent_id') 
    children = soft_delete_relationship(remote(parent_id), id, 'B.parent_id', 'B.id') 

SoftDeleteMixin基於LimitingQuery(https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/PreFilteredQuery

from sqlalchemy.orm.query import Query 


class NonDeletedQuery(Query): 
    def get(self, ident): 
     return Query.get(self.populate_existing(), ident) 

    def __iter__(self): 
     return Query.__iter__(self.private()) 

    def from_self(self, *ent): 
     return Query.from_self(self.private(), *ent) 

    def private(self): 
     mzero = self._mapper_zero() 
     if mzero is not None and hasattr(mzero, 'class_'): 
      soft_deleted = getattr(mzero.class_, 'soft_deleted', None) 
      return self.enable_assertions(False).filter(soft_deleted.is_(False)) if soft_deleted else self 
     else: 
      return self 

而且soft_delete_relationship定製primaryjoin結構關係(加入非soft_deleted)。

b_parent = aliased(B) 
A.query.join(A.b).outerjoin(b_parent, B.parent) 

我獲得以下SQL:

SELECT ... FROM a JOIN b ON b.id = a.b_id LEFT OUTER JOIN b AS b_1 ON b_1.id = b.parent_id AND *b*.soft_deleted IS False 

我期待下面的可是:

SELECT ... FROM a JOIN b ON b.id = a.b_id LEFT OUTER JOIN b AS b_1 ON b_1.id = b.parent_id AND *b_1*.soft_deleted IS False 

當我寫有別名b查詢

def soft_delete_relationship(first, second, *args, **kwargs): 
    if isinstance(first, str) and isinstance(second, str): 
     other, other_column = first.split('.') 
     _this, this_column = second.split('.') 

     primaryjoin = ' & '.join(['({} == {})'.format(first, second), '{}.soft_deleted.is_(False)'.format(other)]) 

    else: 
     other, other_column = args[0].split('.') 
     _this, this_column = args[1].split('.') 

     primaryjoin = lambda: (first == second) & getattr(second.table.c, 'soft_deleted').is_(False) 

    kwargs['primaryjoin'] = primaryjoin 
    return relationship(other, **kwargs) 

出現的問題我明確寫下:

A.query.join(A.b).outerjoin(b_parent, (b_parent.id == B.parent_id) & b_parent.soft_deleted.is_(False)) 

我得到了正確的查詢。

如何在沒有顯式連接條件的情況下在查詢中獲得b_1的正確別名? 順便說一句,預計在SQLAlchemy 0.7.9中的SQL。

回答

0

好的,我想通了。

getattr(second.table.c, 'soft_deleted')也必須使用remote()註釋。

換言之在B.parentrelationshipprimaryjoin應該看起來像:

(遠程(B.id)== B.parent_id)&遠程(B.soft_deleted).is_(假)