Refactor associations in models to include constraints: false, preventing automatic foreign key creation. Update sequelize.js to enhance foreign key management during model synchronization, ensuring associations are restored correctly after sync operations.

This commit is contained in:
Torsten Schulz (local)
2025-12-18 17:44:17 +01:00
parent c28f8b1384
commit 86f753c745
2 changed files with 96 additions and 16 deletions

View File

@@ -475,13 +475,14 @@ const syncModelsAlways = async (models) => {
}
}
// constraints: false wird von Sequelize ignoriert wenn Associations vorhanden sind
// Wir müssen die Associations temporär entfernen, um Foreign Keys zu verhindern
const originalAssociations = model.associations ? { ...model.associations } : {};
const associationKeys = Object.keys(originalAssociations);
try {
// constraints: false wird von Sequelize ignoriert wenn Associations vorhanden sind
// Wir müssen die Associations temporär entfernen, um Foreign Keys zu verhindern
const originalAssociations = model.associations ? { ...model.associations } : {};
const associationKeys = Object.keys(originalAssociations);
// Entferne temporär alle Associations, damit Sequelize keine Foreign Keys erstellt
// Dies muss innerhalb des try Blocks sein, damit die Wiederherstellung im finally Block garantiert ist
if (associationKeys.length > 0) {
console.log(` ⚠️ Temporarily removing ${associationKeys.length} associations from ${model.name} to prevent FK creation`);
// Lösche alle Associations temporär
@@ -528,14 +529,85 @@ const syncModelsAlways = async (models) => {
}
console.log(` 🔄 Syncing model ${model.name} with constraints: false`);
await model.sync({ alter: true, force: false, constraints: false });
try {
await model.sync({ alter: true, force: false, constraints: false });
} catch (syncError) {
// Wenn Sequelize versucht, Foreign Keys zu erstellen, entferne sie nach dem Fehler
if (syncError.message && syncError.message.includes('REFERENCES')) {
console.log(` ⚠️ Sequelize tried to create FK despite constraints: false, removing any created FKs...`);
try {
const tableName = model.tableName;
const schema = model.options?.schema || 'public';
const foreignKeys = await sequelize.query(`
SELECT tc.constraint_name
FROM information_schema.table_constraints AS tc
WHERE tc.constraint_type = 'FOREIGN KEY'
AND tc.table_name = :tableName
AND tc.table_schema = :schema
`, {
replacements: { tableName, schema },
type: sequelize.QueryTypes.SELECT
});
if (foreignKeys && foreignKeys.length > 0) {
for (const fk of foreignKeys) {
await sequelize.query(`
ALTER TABLE "${schema}"."${tableName}"
DROP CONSTRAINT IF EXISTS "${fk.constraint_name}" CASCADE
`);
}
}
// Versuche Sync erneut ohne Foreign Keys
console.log(` 🔄 Retrying sync without foreign keys...`);
await model.sync({ alter: true, force: false, constraints: false });
} catch (retryError) {
console.error(` ❌ Retry failed:`, retryError.message);
console.error(` ❌ Original sync error:`, syncError.message);
// Kombiniere beide Fehler für besseres Debugging
const combinedError = new Error(`Sync failed: ${syncError.message}. Retry also failed: ${retryError.message}`);
combinedError.originalError = syncError;
combinedError.retryError = retryError;
throw combinedError;
}
} else {
throw syncError;
}
}
// Stelle die Associations wieder her
// Entferne alle Foreign Keys, die Sequelize möglicherweise trotzdem erstellt hat
try {
const tableName = model.tableName;
const schema = model.options?.schema || 'public';
const foreignKeys = await sequelize.query(`
SELECT tc.constraint_name
FROM information_schema.table_constraints AS tc
WHERE tc.constraint_type = 'FOREIGN KEY'
AND tc.table_name = :tableName
AND tc.table_schema = :schema
`, {
replacements: { tableName, schema },
type: sequelize.QueryTypes.SELECT
});
if (foreignKeys && foreignKeys.length > 0) {
console.log(` ⚠️ Sequelize created ${foreignKeys.length} foreign keys despite constraints: false, removing them...`);
for (const fk of foreignKeys) {
await sequelize.query(`
ALTER TABLE "${schema}"."${tableName}"
DROP CONSTRAINT IF EXISTS "${fk.constraint_name}" CASCADE
`);
}
}
} catch (fkError) {
console.warn(` ⚠️ Could not check/remove foreign keys after sync:`, fkError.message);
}
} finally {
// Stelle die Associations wieder her (IMMER, auch bei Fehlern)
if (associationKeys.length > 0) {
console.log(` ✅ Restoring ${associationKeys.length} associations for ${model.name}`);
model.associations = originalAssociations;
}
} finally {
// Restore VIRTUAL fields after sync
for (const [key, attr] of Object.entries(virtualFields)) {
model.rawAttributes[key] = attr;