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:
@@ -598,44 +598,52 @@ export default function setupAssociations() {
|
||||
|
||||
Learning.belongsTo(LearnRecipient, {
|
||||
foreignKey: 'learningRecipientId',
|
||||
as: 'recipient'
|
||||
as: 'recipient',
|
||||
constraints: false
|
||||
}
|
||||
);
|
||||
|
||||
LearnRecipient.hasMany(Learning, {
|
||||
foreignKey: 'learningRecipientId',
|
||||
as: 'learnings'
|
||||
as: 'learnings',
|
||||
constraints: false
|
||||
});
|
||||
|
||||
Learning.belongsTo(FalukantUser, {
|
||||
foreignKey: 'associatedFalukantUserId',
|
||||
as: 'learner'
|
||||
as: 'learner',
|
||||
constraints: false
|
||||
}
|
||||
);
|
||||
|
||||
FalukantUser.hasMany(Learning, {
|
||||
foreignKey: 'associatedFalukantUserId',
|
||||
as: 'learnings'
|
||||
as: 'learnings',
|
||||
constraints: false
|
||||
});
|
||||
|
||||
Learning.belongsTo(ProductType, {
|
||||
foreignKey: 'productId',
|
||||
as: 'productType'
|
||||
as: 'productType',
|
||||
constraints: false
|
||||
});
|
||||
|
||||
ProductType.hasMany(Learning, {
|
||||
foreignKey: 'productId',
|
||||
as: 'learnings'
|
||||
as: 'learnings',
|
||||
constraints: false
|
||||
});
|
||||
|
||||
Learning.belongsTo(FalukantCharacter, {
|
||||
foreignKey: 'associatedLearningCharacterId',
|
||||
as: 'learningCharacter'
|
||||
as: 'learningCharacter',
|
||||
constraints: false
|
||||
});
|
||||
|
||||
FalukantCharacter.hasMany(Learning, {
|
||||
foreignKey: 'associatedLearningCharacterId',
|
||||
as: 'learningsCharacter'
|
||||
as: 'learningsCharacter',
|
||||
constraints: false
|
||||
});
|
||||
|
||||
FalukantUser.hasMany(Credit, {
|
||||
|
||||
@@ -475,13 +475,14 @@ const syncModelsAlways = async (models) => {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
// 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 {
|
||||
// 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
|
||||
});
|
||||
|
||||
// Stelle die Associations wieder her
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
Reference in New Issue
Block a user