SQLAlchemy нетерпеливо загружает рекурсивную модель - PullRequest
0 голосов
/ 10 января 2019

Как вы можете написать модель, которую она жаждет, рекурсивно загружает родителей и детей определенной роли. Так что не только ребенок той роли, которую вы сейчас получаете, но и дети.

Вы рискуете закончить бесконечным циклом или у SQLAlchemy есть логика для их обнаружения?

Модель SQLAlchemy выглядит следующим образом:

from sqlalchemy.ext.declarative import declarative_base


roles_parents = Table(
'roles_parents', Base.metadata,
Column('role_id', Integer, ForeignKey('roles.id')),
Column('parent_id', Integer, ForeignKey('roles.id'))
)


Base = declarative_base()
class Role(Base):

    __tablename__ = 'roles'

id = Column(Integer, primary_key=True)
name = Column(String(20))
parents = relationship(
    'Role',
    secondary=roles_parents,
    primaryjoin=(id == roles_parents.c.role_id),
    secondaryjoin=(id == roles_parents.c.parent_id),
    backref=backref('children', lazy='joined'),
    lazy='joined'
)

def get_children(self):
    logger.log_dbg("get_children(self) with name: "  + self.name)
    for child in self.children:
        yield child
        for grandchild in child.get_children():
            yield grandchild

@staticmethod
def get_by_name(name):
    logger.log_dbg("get_by_name( " + name + " )")
    with DBManager().session_scope() as session:
        role = session.query(Role).options(joinedload(
            Role.children).joinedload(Role.parents)).filter_by(
            name=name).first()
        # role = session.query(Role).filter_by(name=name).first()
        session.expunge_all()
        return role

Вы можете видеть, что я пытался включить активную загрузку в отношении родителей через атрибут в отношении и через параметры в запросе, где я получаю роль.

Причиной необходимости этой энергичной загрузки (и session.expunge_all ()) является то, что сеанс теряется при попытке получить дочерний элемент с помощью get_children ().

Из-за активной загрузки get_children больше не завершается ошибкой при доступе к дочерней роли этой роли. Однако, это все еще терпит неудачу при попытке вызвать внука. Таким образом, готовая загрузка, кажется, работает для дочерней роли, но не стремится загружать ее дочерних элементов.

Ответы [ 2 ]

0 голосов
/ 05 апреля 2019

Решение на основе этого поста :

def recursive_expunge(obj, dbSession):
    def _recursive_expunge(_obj):
        _instance_state = sqlalchemy.inspection.inspect(_obj)
        if _instance_state.detached or _instance_state.transient:
          return
        _mapper = _instance_state.mapper
        try:
            dbSession.expunge(_obj)
        except sqlalchemy.orm.exc.UnmappedInstanceError:
            pass
        except sqlalchemy.exc.InvalidRequestError:
            pass
        if _mapper:
            _loaded_rels = [i for i in _mapper.relationships.items() if i[0] not in _instance_state.unloaded]
            for (_name, _rel) in _loaded_rels:
                _loaded_rel_data = getattr(_obj, _name)
                if _loaded_rel_data:
                    if not _rel.uselist:
                        _recursive_expunge(_loaded_rel_data)
                    else:
                        for _i in _loaded_rel_data:
                            _recursive_expunge(_i)
    _recursive_expunge(obj)
0 голосов
/ 22 января 2019

Мне любопытно, почему вы хотите загружать внуков так же: каков вариант использования?

Причина, по которой я спрашиваю это, заключается в том, что я предполагаю, что вы сможете получить доступ к внукам, когда это необходимо, путем доступа к свойству .children на текущих дочерних узлах. Это должно снизить нагрузку на память (не рекурсивно загружая все дочерние элементы), а также упростить процесс обработки текущего узла (роли).

...