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, {
|
Learning.belongsTo(LearnRecipient, {
|
||||||
foreignKey: 'learningRecipientId',
|
foreignKey: 'learningRecipientId',
|
||||||
as: 'recipient'
|
as: 'recipient',
|
||||||
|
constraints: false
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
LearnRecipient.hasMany(Learning, {
|
LearnRecipient.hasMany(Learning, {
|
||||||
foreignKey: 'learningRecipientId',
|
foreignKey: 'learningRecipientId',
|
||||||
as: 'learnings'
|
as: 'learnings',
|
||||||
|
constraints: false
|
||||||
});
|
});
|
||||||
|
|
||||||
Learning.belongsTo(FalukantUser, {
|
Learning.belongsTo(FalukantUser, {
|
||||||
foreignKey: 'associatedFalukantUserId',
|
foreignKey: 'associatedFalukantUserId',
|
||||||
as: 'learner'
|
as: 'learner',
|
||||||
|
constraints: false
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
FalukantUser.hasMany(Learning, {
|
FalukantUser.hasMany(Learning, {
|
||||||
foreignKey: 'associatedFalukantUserId',
|
foreignKey: 'associatedFalukantUserId',
|
||||||
as: 'learnings'
|
as: 'learnings',
|
||||||
|
constraints: false
|
||||||
});
|
});
|
||||||
|
|
||||||
Learning.belongsTo(ProductType, {
|
Learning.belongsTo(ProductType, {
|
||||||
foreignKey: 'productId',
|
foreignKey: 'productId',
|
||||||
as: 'productType'
|
as: 'productType',
|
||||||
|
constraints: false
|
||||||
});
|
});
|
||||||
|
|
||||||
ProductType.hasMany(Learning, {
|
ProductType.hasMany(Learning, {
|
||||||
foreignKey: 'productId',
|
foreignKey: 'productId',
|
||||||
as: 'learnings'
|
as: 'learnings',
|
||||||
|
constraints: false
|
||||||
});
|
});
|
||||||
|
|
||||||
Learning.belongsTo(FalukantCharacter, {
|
Learning.belongsTo(FalukantCharacter, {
|
||||||
foreignKey: 'associatedLearningCharacterId',
|
foreignKey: 'associatedLearningCharacterId',
|
||||||
as: 'learningCharacter'
|
as: 'learningCharacter',
|
||||||
|
constraints: false
|
||||||
});
|
});
|
||||||
|
|
||||||
FalukantCharacter.hasMany(Learning, {
|
FalukantCharacter.hasMany(Learning, {
|
||||||
foreignKey: 'associatedLearningCharacterId',
|
foreignKey: 'associatedLearningCharacterId',
|
||||||
as: 'learningsCharacter'
|
as: 'learningsCharacter',
|
||||||
|
constraints: false
|
||||||
});
|
});
|
||||||
|
|
||||||
FalukantUser.hasMany(Credit, {
|
FalukantUser.hasMany(Credit, {
|
||||||
|
|||||||
@@ -475,13 +475,14 @@ const syncModelsAlways = async (models) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
// constraints: false wird von Sequelize ignoriert wenn Associations vorhanden sind
|
// constraints: false wird von Sequelize ignoriert wenn Associations vorhanden sind
|
||||||
// Wir müssen die Associations temporär entfernen, um Foreign Keys zu verhindern
|
// Wir müssen die Associations temporär entfernen, um Foreign Keys zu verhindern
|
||||||
const originalAssociations = model.associations ? { ...model.associations } : {};
|
const originalAssociations = model.associations ? { ...model.associations } : {};
|
||||||
const associationKeys = Object.keys(originalAssociations);
|
const associationKeys = Object.keys(originalAssociations);
|
||||||
|
|
||||||
|
try {
|
||||||
// Entferne temporär alle Associations, damit Sequelize keine Foreign Keys erstellt
|
// 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) {
|
if (associationKeys.length > 0) {
|
||||||
console.log(` ⚠️ Temporarily removing ${associationKeys.length} associations from ${model.name} to prevent FK creation`);
|
console.log(` ⚠️ Temporarily removing ${associationKeys.length} associations from ${model.name} to prevent FK creation`);
|
||||||
// Lösche alle Associations temporär
|
// Lösche alle Associations temporär
|
||||||
@@ -528,14 +529,85 @@ const syncModelsAlways = async (models) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log(` 🔄 Syncing model ${model.name} with constraints: false`);
|
console.log(` 🔄 Syncing model ${model.name} with constraints: false`);
|
||||||
|
try {
|
||||||
await model.sync({ alter: true, force: false, constraints: false });
|
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) {
|
if (associationKeys.length > 0) {
|
||||||
console.log(` ✅ Restoring ${associationKeys.length} associations for ${model.name}`);
|
console.log(` ✅ Restoring ${associationKeys.length} associations for ${model.name}`);
|
||||||
model.associations = originalAssociations;
|
model.associations = originalAssociations;
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
// Restore VIRTUAL fields after sync
|
// Restore VIRTUAL fields after sync
|
||||||
for (const [key, attr] of Object.entries(virtualFields)) {
|
for (const [key, attr] of Object.entries(virtualFields)) {
|
||||||
model.rawAttributes[key] = attr;
|
model.rawAttributes[key] = attr;
|
||||||
|
|||||||
Reference in New Issue
Block a user