2010-02-17 12 views
8

Tengo muchas clases de modelos con ralaciones entre ellos con una interfaz CRUD para editar. El problema es que algunos objetos no se pueden eliminar ya que hay otros objetos que se refieren a ellos. A veces puedo configurar la regla ON DELETE para manejar este caso, pero en la mayoría de los casos no quiero que se eliminen automáticamente los objetos relacionados hasta que se desactiven manualmente. De todos modos, me gustaría presentarle al editor una lista de objetos que se refieren a la vista actual y resaltar aquellos que impiden su eliminación debido a la restricción FOREIGN KEY. ¿Hay una solución lista para descubrir automáticamente los referers?Descubriendo referers al objeto SQLAlchemy

actualización

la tarea parece ser bastante común (por ejemplo, Django ORM muestra todas las dependencias), por lo que se preguntan que no hay solución a ella todavía.

hay dos direcciones sugeridas:

  1. Enumerar todas las relaciones de objeto actual e ir a través de su backref. Pero no hay garantía de que todas las relaciones se hayan definido en backref. Además, hay algunos casos en los que backref no tiene sentido. Aunque puedo definirlo en todas partes, no me gusta hacerlo de esta manera y no es confiable.
  2. (Sugerido por van y stephan) Compruebe todas las tablas del objeto MetaData y recopile dependencias de su propiedad foreign_keys (el código de sqlalchemy_schemadisplay se puede usar como ejemplo, gracias a los comentarios de stephan). Esto permitirá detectar todas las dependencias entre tablas, pero lo que necesito son dependencias entre clases de modelo. Algunas claves externas se definen en tablas intermedias y no tienen modelos correspondientes (se usan como secondary en las relaciones). Claro, puedo ir más lejos y encontrar un modelo relacionado (tengo que encontrar la manera de hacerlo todavía), pero parece demasiado complicado.

Solución

continuación es un método de clase de modelo de base (diseñado para la extensión declarativa) que utilizo como solución. No es perfecto y no cumple con todos mis requisitos, pero funciona para el estado actual de mi proyecto. El resultado se recopila como diccionario de diccionarios, por lo que puedo mostrarlos agrupados por objetos y sus propiedades. Aún no he decidido si es una buena idea, ya que la lista de referers a veces es enorme y me veo obligado a limitarla a un número razonable.

def _get_referers(self): 
    db = object_session(self) 
    cls, ident = identity_key(instance=self) 
    medatada = cls.__table__.metadata 
    result = {} 
    # _mapped_models is my extension. It is collected by metaclass, so I didn't 
    # look for other ways to find all model classes. 
    for other_class in medatada._mapped_models: 
     queries = {} 
     for prop in class_mapper(other_class).iterate_properties: 
      if not (isinstance(prop, PropertyLoader) and \ 
        issubclass(cls, prop.mapper.class_)): 
       continue 
      query = db.query(prop.parent) 
      comp = prop.comparator 
      if prop.uselist: 
       query = query.filter(comp.contains(self)) 
      else: 
       query = query.filter(comp==self) 
      count = query.count() 
      if count: 
       queries[prop] = (count, query) 
     if queries: 
      result[other_class] = queries 
    return result 

Gracias a todos los que me ayudaron, especialmente Stephan y Van.

+0

No estoy seguro de entender la pregunta. Los objetos de tabla tienen una propiedad 'foreign_keys' que se debe establecer correctamente si obtuviste los metadatos por reflexión. Las clases mapeadas de ORM tienen relaciones y restituciones como parte del mapeador. ¿Puedes elaborar? – stephan

+0

@stephan: He actualizado la pregunta, con suerte ahora está claro. –

+0

@Denis: sí, mucho más claro ahora, y creo que van ya ha explicado la mayor parte. Dada una clase de modelo, puede obtener el asignador con la función 'class_mapper()' o 'object_mapper()'. Esto te da la tabla mapeada. Luego puede recorrer las 'foreign_keys' de estas tablas para obtener todas las tablas relacionadas. Para estos, comprueba a qué clases están asignados. Vea el segundo ejemplo en http://www.sqlalchemy.org/trac/wiki/UsageRecipes/SchemaDisplay para algo similar. No sé de ninguna mejor manera. – stephan

Respuesta

6

SQL: Tengo que estar absolutamente en desacuerdo con S.Lott 'answer. No conozco la solución lista para usar, pero es definitivamente posible para descubrir todas las tablas que tienen restricciones ForeignKey para una tabla determinada. Es necesario utilizar correctamente las vistas INFORMATION_SCHEMA como REFERENTIAL_CONSTRAINTS, KEY_COLUMN_USAGE, TABLE_CONSTRAINTS, etc. Ver SQL Server example. Con algunas limitaciones y extensiones, la mayoría de las versiones de las nuevas bases de datos relacionales admiten el estándar INFORMATION_SCHEMA. Cuando tiene toda la información FK y el objeto (fila) en la tabla, se trata de ejecutar pocas declaraciones SELECT para obtener todas las otras filas en otras tablas que hacen referencia a una fila dada y evitar que se elimine.

sqlalchemy: Como señaló Stephan en su comentario, si se utiliza orm con backref para las relaciones, entonces debería ser bastante fácil para que usted pueda obtener la lista de padres objetos que guardan relación con el objeto Está intentando eliminar, porque esos objetos son básicamente propiedades mapeadas de su objeto (child1.Parent).

Si trabaja con Table objetos de la alquimia SQL (o no siempre utilizar backref para las relaciones), entonces usted tendría que obtener valores de foreign_keys para todas las tablas, y luego para todos aquellos ForeignKey s método llamado references(...), siempre que su tabla como un parámetro. De esta forma, encontrará todos los FK (y tablas) que hacen referencia a la tabla a la que se asigna su objeto. Luego puede consultar todos los objetos que mantienen referencia a su objeto construyendo la consulta para cada uno de esos FK.

+1

Gracias (+1) por la idea de comprobar 'foreign_keys' para todas las tablas, hasta ahora parece ser la forma más promisoria/confiable. Casi nunca uso 'backref' ya que define la relación fuera de la definición del modelo: no se pueden ver todas las propiedades disponibles de la definición de la clase, el código no está auto-documentado. –

+1

Puede descubrir todas las restricciones de FK. Nunca dije que no pudieras. Estamos de acuerdo en encontrar restricciones FK. Sin embargo, no puede descubrir todas las consultas que se romperán porque dependen de una relación * no declarada * FK. –

1

En general, no hay manera de "descubrir" toda de las referencias en una base de datos relacional.

En algunas bases de datos, pueden usar integridad referencial declarativa en forma de restricciones explícitas de clave externa o verificación.

Pero no hay ningún requisito para hacer esto. Puede ser incompleto o inconsistente.

Cualquier consulta puede incluir una relación FK que no está declarada. Sin el universo de todas las consultas, no puede conocer las relaciones que se usan pero no se declaran.

Para encontrar "referers" en general, debe conocer el diseño de la base de datos y tener todas las consultas.

+2

information_schema proporciona la información necesaria para descubrir las referencias. y Denis solo está interesado en aquellos que realmente impiden que la fila se elimine, lo que significa que es una restricción real de la CLAVE EXTRAÑA. – van

+0

La pregunta era sobre SQLAlchemy, que ya tiene una definición de esquema como alguna estructura en la memoria (definida en el código o cargada automáticamente desde la base de datos). –

+0

@van: El problema es que todavía hay reglas informales. Si una consulta espera una relación FK no declarada, una eliminación permitida aún romperá la aplicación. De ahí la parte "en general" de la respuesta. Las relaciones declaradas no son toda la tienda en general. La pregunta no refleja esto, lo que, en general, generará problemas. El esquema específico puede ser realmente, realmente completo y puede tener todos los FK declarados. Pero, en general, no hay forma de saberlo a menos que realice una ingeniería inversa de cada consulta. –

0

Para cada clase de modelo, puede ver fácilmente si todas sus relaciones de uno a muchos están vacías simplemente preguntando por la lista en cada caso y viendo cuántas entradas contiene. (Probablemente también se implemente una forma más eficiente en términos de COUNT.) Si hay claves externas relacionadas con el objeto y sus relaciones de objeto están configuradas correctamente, al menos una de estas listas será distinta de cero. en longitud.

+0

Esto no siempre es cierto. El objeto actual no puede tener relaciones, mientras que hay un objeto que se refiere a él a través de una clave externa y tiene una relación sin retorno. También tenga en cuenta que la comprobación de todos los objetos en todas las relaciones de objeto no es una opción, ya que podría ser una gran colección (lazy = 'dynamic'). –

+0

Enorme o no, sin leer al menos 1 fila, no puede saber si hay referencias. En cuanto a la falta de backrefs, eso es lo que quise decir con "configurar correctamente", aunque reconozco que no es incorrecto carecer de respaldo, simplemente inconveniente. – Kylotan

Cuestiones relacionadas