Hi,

I'm attempting to do some universal filtering using a custom Query class. 
 In this case, I'm trying to filter out all items marked as archived in two 
related classes.  I'm having some trouble adding the required filters to 
the query at all the right levels.

I'm using Flask 0.9, SQLAlchemy 0.8 and PostgreSQL 9.1.5

Let's call the two classes Parent and Child, which are inheriting from 
Archivable:

class Archivable(object):
  @declared_attr
  def is_archived(cls):
    return Column('is_archived', types.Boolean, nullable=False, 
default=False, index=True)

class Parent(base, Archivable):
  __tablename__ = 'parent'
  id = Column('id', types.BigInteger, primary_key=True, nullable=False)
  is_archived = 

class Child(base, Archivable):
  __tablename__ = 'child'
  id = Column('id', types.BigInteger, primary_key=True, nullable=False)
  parent_id = Column('id', types.BigInteger, ForeignKey('parent.id'), 
nullable=False)
  parent = relationship('Parent', primaryjoin='Child.parent_id==Parent.id',
                        backref='children')

Somewhere in my code I am calling:
  parent = db.session.query(Parent).filter(Parent.id == 
1234).options(joinedload('children')).first()

This is resulting in a query of the form:

SELECT anon_1.*, child.*
FROM (
  SELECT parent.*
  FROM parent
  WHERE parent.id = 1234
  LIMIT 1) AS anon_1
LEFT OUTER JOIN child ON child.parent_id = parent.id

which is fine.

When I try and use a custom query class to access the query and filter it 
however, I only seem to be able to access elements of the inner subquery. 
 self._entities for instance only shows a single _MapperEntity 
Mapper|Parent|parent, self.whereclause is a BooleanClauseList of parent.id 
= 1234.  If I try and inject my filters at this stage using the following:

class NoArchivesQuery(Query):
  def __iter__(self):
    return Query.__iter__(self._add_archive_filter())

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

  def _add_archive_filter(self):
    entities = self._entities

    for entity in entities:
      if entity.entity_zero and hasattr(entity.entity_zero, 'class_'):
        
tables_involved_in_the_query.add(entity.entity_zero.class_.__table__)

    filter_crits = []
    for table in tables_involved_in_the_query:
    if hasattr(table.c, "is_archived"):
      filter_crits.append(or_(table.c.is_archived == None, 
table.c.is_archived == False))

    if filter_crits:
      return self.enable_assertions(False).filter(*filter_crits)

 I can get as far as 

SELECT anon_1.*, child.*
FROM (
  SELECT parent.*
  FROM parent
  WHERE parent.id = 1234 AND (parent.is_archived IS NULL OR 
parent.is_archived = false)
  LIMIT 1) AS anon_1
LEFT OUTER JOIN child ON child.parent_id = parent.id

But this does not filter the children, and so I get all archived children 
back.  What I would like to get back is more along the lines of:

SELECT anon_1.*, child.*
FROM (
  SELECT parent.*
  FROM parent
  WHERE parent.id = 1234 AND (parent.is_archived IS NULL OR 
parent.is_archived = false)
  LIMIT 1) AS anon_1
LEFT OUTER JOIN child ON child.parent_id = parent.id AND (child.is_archived 
IS NULL OR child.is_archived = false)

Is that sort of manipulation of a joinedload possible at this level?  Do I 
need to look somewhere else in the query processing pipeline?  Or is it 
really not feasible?

Thanks,

Mick


-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to