Estoy tratando de averiguar la combinación correcta configuración de consultas dentro de SQLAlchemy, pero me parece que no puede conseguir mi cabeza alrededor de ella.

Tengo la siguiente configuración de tabla (simplificado, me fui de la no-esencial campos):

class Group(db.Model):
    id            = db.Column(db.Integer, primary_key = True)
    number        = db.Column(db.SmallInteger, index = True, unique = True)
    member        = db.relationship('Member', backref = 'groups', lazy = 'dynamic')

class Member(db.Model):
    id            = db.Column(db.Integer, primary_key = True)
    number        = db.Column(db.SmallInteger, index = True)
    groupid       = db.Column(db.Integer, db.ForeignKey('group.id'))
    item          = db.relationship('Item', backref = 'members', lazy = 'dynamic')

class Version(db.Model):
    id           = db.Column(db.Integer, primary_key = True)
    name         = db.Column(db.String(80), index = True)
    items        = db.relationship('Item', backref='versions', lazy='dynamic')  

class Item(db.Model):
    id           = db.Column(db.Integer, primary_key = True)
    member       = db.Column(db.Integer, db.ForeignKey('member.id'))
    version      = db.Column(db.Integer, db.ForeignKey('version.id'))

Así que las relaciones son las siguientes:

  • 1:n Miembro del Grupo
  • 1:n Elemento de Miembro
  • 1:n Elemento de Versión

Me gustaría construir una consulta mediante la selección de todos los elementos-las Filas de la base de datos, que tiene una versión determinada. A continuación, me gustaría pedir por Grupo y, a continuación, por parte de los Estados. La salida utilizando un Matraz/WTForm debería ser algo como esto:

* GroupA
  * MemberA
     * ItemA (version = selected by user)
     * ItemB ( dito )
  * Member B
     * ItemC ( dito )
  ....

He venido para arriba con algo como la siguiente consulta, pero estoy bastante seguro de que no es correcto (e ineficiente)

   session.query(Item,Member,Group,Version)
    .join(Member).filter(version.id==1)
    .order_by(Group).order_by(Member).all()

Mi primer intuitiva, habría sido crear algo como

Item.query.join(Member, Item.member==Member.id)
    .filter(Member.versions.name=='MySelection')
    .order_by(Member.number).order_by(Group.number)

pero obviamente, esto no funciona en absoluto. La operación de combinación en la tabla de Versión no parece producir el tipo de unión entre las dos tablas que yo esperaba. Tal vez estoy totalmente de malentendido el concepto, pero después de leer los tutoriales de esto tendría sentido para mí.

InformationsquelleAutor user3347953 | 2014-02-24

1 Comentario

  1. 35

    Siguiente le dará los objetos que necesitas en una sola consulta:

    q = (session.query(Group, Member, Item, Version)
            .join(Member)
            .join(Item)
            .join(Version)
            .filter(Version.name == my_version)
            .order_by(Group.number)
            .order_by(Member.number)
            ).all()
    print_tree(q)

    Sin embargo, el resultado que se obtiene será una lista de tuplas (Group, Member, Item, Version). Ahora está levantado a usted para que se muestre en forma de un árbol. El código siguiente puede resultar útil, aunque:

    def print_tree(rows):
        def get_level_diff(row1, row2):
            """ Returns tuple: (from, to) of different item positions.  """
            if row1 is None: # first row handling
                return (0, len(row2))
            assert len(row1) == len(row2)
            for col in range(len(row1)):
                if row1[col] != row2[col]:
                    return (col, len(row2))
            assert False, "should not have duplicates"
    
        prev_row = None
        for row in rows:
            level = get_level_diff(prev_row, row)
            for l in range(*level):
                print 2 * l * " ", row[l]
                prev_row = row

    De actualización-1: Si usted está dispuesto a renunciar a lazy = 'dynamic' para las dos primeras relaciones, puede una consulta para cargar un conjunto object network (en oposición a las tuplas de arriba) con el código:

    q = (session.query(Group)
            .join(Member)
            .join(Item)
            .join(Version)
            # @note: here we are tricking sqlalchemy to think that we loaded all these relationships,
            # even though we filter them out by version. Please use this only to get data and display,
            # but not to continue working with it as if it were a regular UnitOfWork
            .options(
                contains_eager(Group.member).
                contains_eager(Member.items).
                contains_eager(Item.version)
                )
            .filter(Version.name == my_version)
            .order_by(Group.number)
            .order_by(Member.number)
            ).all()
    
    # print tree: easy navigation of relationships
    for g in q:
        print "", g
        for m in g.member:
            print 2 * " ", m
            for i in m.items:
                print 4 * " ", i
    • Gracias mucho. He experimentado y vino para arriba con una consulta similar a la solución. Yo sólo tenía la sensación de que no parecía elegante, como el que yo tenía todas esas relaciones definidas entre las tablas en las definiciones de clase. El print_tree código es genial!!!
    • Yo sé de una forma más elegante manera de conseguir lo que usted desea, pero no iba a trabajar con lazy = 'dynamic' relaciones. Es esto algo que usted insiste en tener?
    • No, en absoluto.
    • Ok, actualizado la respuesta con otra opción

Dejar respuesta

Please enter your comment!
Please enter your name here