J'ai beaucoup de classes de modèles avec des relations entre elles avec une interface CRUD à éditer. Le problème est que certains objets ne peuvent pas être supprimés car d'autres objets y font référence. Parfois, je peux configurer la règle ON DELETE pour gérer ce cas, mais dans la plupart des cas, je ne souhaite pas la suppression automatique des objets associés tant qu'ils ne sont pas liés manuellement. Quoi qu'il en soit, j'aimerais présenter à l'éditeur une liste d'objets faisant référence à l'objet actuellement consulté et mettre en évidence ceux qui empêchent sa suppression en raison de la contrainte FOREIGN KEY. Existe-t-il une solution prête à découvrir automatiquement les référents ?
Mettre à jour
La tâche semble être assez courante (par exemple, django ORM affiche toutes les dépendances), donc je me demande s'il n'y a pas encore de solution.
Deux directions sont suggérées :
- Énumérez toutes les relations de l'objet courant et parcourez leurs
backref
. Mais il n'y a aucune garantie que toutes les relations aientbackref
définies. De plus, il y a des cas oùbackref
n'a pas de sens. Bien que je puisse le définir partout, je n'aime pas faire de cette façon et ce n'est pas fiable. - (Suggéré par van et stephan) Vérifiez toutes les tables de l'objet
MetaData
et collectez les dépendances de leur propriétéforeign_keys
(le code de sqlalchemy_schemadisplay peut être utilisé comme exemple, grâce aux commentaires de stephan). Cela permettra d'attraper toutes les dépendances entre les tables, mais ce dont j'ai besoin, ce sont les dépendances entre les classes de modèle. Certaines clés étrangères sont définies dans des tables intermédiaires et n'ont pas de modèles qui leur correspondent (utilisées commesecondary
dans les relations). Bien sûr, je peux aller plus loin et trouver un modèle associé (je dois encore trouver un moyen de le faire), mais cela semble trop compliqué.
Solution
Vous trouverez ci-dessous une méthode de classe de modèle de base (conçue pour l'extension déclarative) que j'utilise comme solution. Ce n'est pas parfait et ne répond pas à toutes mes exigences, mais cela fonctionne pour l'état actuel de mon projet. Le résultat est collecté sous forme de dictionnaire de dictionnaires, je peux donc les montrer regroupés par objets et leurs propriétés. Je n'ai pas encore décidé si c'est une bonne idée, car la liste de référents est parfois énorme et je suis obligé de la limiter à un nombre raisonnable.
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
Merci à tous ceux qui m'ont aidé, en particulier stephan et van.
3 réponses
SQL : je suis absolument en désaccord avec S.Lott' répondez. Je ne connais pas de solution prête à l'emploi, mais il est tout à fait possible de découvrir toutes les tables qui ont des contraintes ForeignKey pour une table donnée. Il faut utiliser correctement les vues INFORMATION_SCHEMA
telles que REFERENTIAL_CONSTRAINTS
, KEY_COLUMN_USAGE
, TABLE_CONSTRAINTS
, etc. Voir Exemple SQL Server. Avec quelques limitations et extensions, la plupart des versions des nouvelles bases de données relationnelles prennent en charge la norme INFORMATION_SCHEMA
. Lorsque vous avez toutes les informations FK et l'objet (ligne) dans la table, il s'agit d'exécuter quelques instructions SELECT
pour obtenir toutes les autres lignes dans d'autres tables qui font référence à une ligne donnée et empêcher sa suppression.
SqlAlchemy : Comme l'a noté stephan dans son commentaire, si vous utilisez orm
avec backref
pour les relations, il devrait être assez facile pour vous de obtenir la liste des objets parents qui conservent la référence à l'objet que vous essayez de supprimer, car ces objets sont essentiellement des propriétés mappées de votre objet (child1.Parent
).
Si vous travaillez avec des objets Table
de l'alchimie SQL (ou n'utilisez pas toujours backref
pour les relations), alors vous devrez obtenir les valeurs de foreign_keys
pour toutes les tables, puis pour toutes celles ForeignKey
appelle la méthode references(...)
, en fournissant votre table en paramètre. De cette façon, vous trouverez tous les FK (et tables) qui font référence à la table à laquelle votre objet est mappé. Ensuite, vous pouvez interroger tous les objets qui conservent une référence à votre objet en construisant la requête pour chacun de ces FK.
foreign_keys
pour toutes les tables, jusqu'à présent, cela semble être le moyen le plus prometteur/fiable. Je n'utilise presque jamais backref
car il définit une relation en dehors de la définition du modèle : vous ne pouvez pas voir toutes les propriétés disponibles à partir de la définition de classe, le code n'est pas auto-documenté.
En général, il n'y a aucun moyen de "découvrir" toutes les références dans une base de données relationnelle.
Dans certaines bases de données, ils peuvent utiliser l'intégrité référentielle déclarative sous la forme de contraintes explicites de clé étrangère ou de vérification.
Mais il n'y a aucune obligation de le faire. Il peut être incomplet ou incohérent.
Toute requête peut inclure une relation FK qui n'est pas déclarée. Sans l'univers de toutes les requêtes, vous ne pouvez pas connaître les relations qui sont utilisées mais non déclarées.
Pour trouver des « référents » en général, vous devez réellement connaître la conception de la base de données et disposer de toutes les requêtes.
Pour chaque classe de modèle, vous pouvez facilement voir si toutes ses relations un-à-plusieurs sont vides simplement en demandant la liste dans chaque cas et en voyant combien d'entrées elle contient. (Il existe probablement un moyen plus efficace d'implémenter également COUNT.) S'il existe des clés étrangères relatives à l'objet et que vos relations d'objet sont correctement configurées, alors au moins une de ces listes sera différente de zéro. en longueur.
Questions connexes
De nouvelles questions
python
Python est un langage de programmation multi-paradigme, typé dynamiquement et polyvalent. Il est conçu pour être rapide à apprendre, comprendre, utiliser et appliquer une syntaxe propre et uniforme. Veuillez noter que Python 2 est officiellement hors support à partir du 01-01-2020. Néanmoins, pour les questions Python spécifiques à la version, ajoutez la balise [python-2.7] ou [python-3.x]. Lorsque vous utilisez une variante Python (par exemple, Jython, PyPy) ou une bibliothèque (par exemple, Pandas et NumPy), veuillez l'inclure dans les balises.
foreign_keys
qui doit être définie correctement si vous avez obtenu les métadonnées par réflexion. Les classes mappées ORM ont des relations et des backrefs dans le cadre du mappeur. Peux-tu élaborer?class_mapper()
ouobject_mapper()
. Cela vous donne la table mappée. Ensuite, vous pouvez parcourir lesforeign_keys
de ces tables pour obtenir toutes les tables associées. Pour ceux-ci, vous vérifiez à quelles classes ils sont mappés. Voir le deuxième exemple dans sqlalchemy.org/trac/wiki/UsageRecipes/SchemaDisplay pour quelque chose de similaire. Je ne connais pas de meilleur moyen.foreign_keys
renvoient aux tables du modèle actuel. Et faites particulièrement attention lorsque l'héritage de table jointe est utilisé.