¿Por qué Sqlalchemy solo carga una instancia de mi relación de muchos a muchos?
-
23-12-2019 - |
Pregunta
Estoy tratando de agregar atributos a una relación de muchos a muchos en mi modelo, y ahora cuando carga un objeto, solo estoy obteniendo un objeto relacionado de mi consulta.
Específicamente estoy tratando de obtener todas las aplicaciones asociadas con una copia de seguridad dada, y también la columna BackupApp.Status.
Perdón por el info-volcado:
Sé que tengo cientos de relaciones para cargar:
$ sqlite3 dev.db "select count(*) from backups_apps where backup_id = 3 and app_id is not null;"
415
pero solo estoy obteniendo uno:
{
"children": [
{
"children": [
{
"bundle_id": "com.iojoe.10",
"compressed": 13151750,
"name": "10",
"size": 14458882
}
],
"name": "Games"
}
],
"name": "root"
}
Los modelos:
# -*- coding: utf-8 -*-
from app_sizer.database import db, CRUDMixin, TimestampMixin, jsonify_plist
from sqlalchemy.ext.associationproxy import association_proxy
class App(db.Model, CRUDMixin, TimestampMixin):
__tablename__ = 'apps'
bundle_id = db.Column(db.String, unique=True, index=True)
app_id = db.Column(db.Integer, unique=True, index=True)
path = db.Column(db.String, unique=True, index=True)
name = db.Column(db.String, index=True)
main_genre = db.Column(db.String, index=True)
compressed_size = db.Column(db.Integer)
full_size = db.Column(db.Integer)
plist = db.Column(db.PickleType)
plist_json = db.Column(db.Text, default=jsonify_plist, onupdate=jsonify_plist)
subgenre_1 = db.Column(db.String, index=True)
subgenre_2 = db.Column(db.String, index=True)
@classmethod
def get_by_bundle_id(cls, bundle_id):
return db.session.query(cls).filter(App.bundle_id == bundle_id).first()
@classmethod
def get_by_path(cls, path):
return db.session.query(cls).filter(App.path == path).first()
@classmethod
def get_all(cls):
return db.session.query(cls).all()
"""
class AppTag(CRUDMixin):
app_id = db.Column(db.Integer, foreign_key='app.id')
user_id = db.Column(db.Integer, foreign_key='user.id')
tag = db.Column(db.String)
"""
# db.Index('idx_tag_user', 'app_tag.user_id', 'app_tag.tag')
class Backup(db.Model, CRUDMixin, TimestampMixin):
__tablename__ = 'backups'
path = db.Column(db.String, unique=True)
name = db.Column(db.String)
notes = db.Column(db.Text)
# Springboard plist
plist = db.Column(db.PickleType)
plist_json = db.Column(db.Text, default=jsonify_plist, onupdate=jsonify_plist)
@classmethod
def get_by_path(cls, path):
return db.session.query(Backup).filter(Backup.path == path).first()
@classmethod
def get_all(cls):
return db.session.query(Backup).all()
# Why does this work but a declarative class doesn't? No idea.
# http://xsnippet.org/359350/
# http://stackoverflow.com/q/5756559/25625
class BackupApp(db.Model, CRUDMixin):
__tablename__ = 'backups_apps'
backup_id = db.Column(db.Integer, db.ForeignKey('backups.id'), nullable=False, default=1)
app_id = db.Column(db.Integer, db.ForeignKey('apps.id'), nullable=False, default=1)
status = db.Column(db.String)
app = db.relationship(App, backref="installed_apps")
backup = db.relationship(Backup, backref="installed_apps")
def __init__(self, app=None, backup=None, status=None):
self.app = app
self.backup = backup
self.status = status
Backup.apps = association_proxy("installed_apps", "app")
App.backups = association_proxy("installed_apps", "backup")
La acción:
@blueprint.route("/backup/<int:backup_id>/apps.json")
def apps_json_for_backup(backup_id):
print "in apps_json_for_backup({})".format(backup_id)
backup = Backup.get_by_id(backup_id)
root = to_treemap(backup.apps)
return jsonify(root)
def to_treemap(apps):
root = {"name": "root", "children": list()}
genres = dict()
for app in apps:
if app.main_genre not in genres:
genres[app.main_genre] = list()
leaf = dict(name=app.name, size=app.full_size, compressed=app.compressed_size, bundle_id=app.bundle_id)
genres[app.main_genre].append(leaf)
for genre, leaves in genres.items():
root['children'].append(dict(name=genre, children=leaves))
return root
Solución
OK, así que aquí está lo que pasó:
Cuando primero hice la tabla de copia de seguridad, no estaba usando crudmixin, por lo que no tenía una columna de identificación.Cuando migró la columna de identificación, todos los valores inicializados en 0. Sqlalchemy ignoró silenciosamente todas esas filas múltiples con la misma ID.