fix(recursive_crud): 🐛 prevent overwriting primary key in recursive relationships

* Ensure foreign key assignment does not overwrite primary key in recursive relationships.
* Added logging for skipped assignments to improve debugging.
This commit is contained in:
Hein
2026-01-14 10:19:04 +02:00
parent 292306b608
commit 012acf1767
2 changed files with 42 additions and 8 deletions

View File

@@ -400,13 +400,25 @@ func (p *NestedCUDProcessor) processChildRelations(
logger.Debug("Using foreign key field for direct assignment: %s (from FK %s)", foreignKeyFieldName, relInfo.ForeignKey)
}
// Get the primary key name for the child model to avoid overwriting it in recursive relationships
childPKName := reflection.GetPrimaryKeyName(relatedModel)
childPKFieldName := reflection.GetJSONNameForField(relatedModelType, childPKName)
if childPKFieldName == "" {
childPKFieldName = strings.ToLower(childPKName)
}
logger.Debug("Processing relation with foreignKeyField=%s, childPK=%s", foreignKeyFieldName, childPKFieldName)
// Process based on relation type and data structure
switch v := relationValue.(type) {
case map[string]interface{}:
// Single related object - directly set foreign key if specified
if parentID != nil && foreignKeyFieldName != "" {
// IMPORTANT: In recursive relationships, don't overwrite the primary key
if parentID != nil && foreignKeyFieldName != "" && foreignKeyFieldName != childPKFieldName {
v[foreignKeyFieldName] = parentID
logger.Debug("Set foreign key in single relation: %s=%v", foreignKeyFieldName, parentID)
} else if foreignKeyFieldName == childPKFieldName {
logger.Debug("Skipping foreign key assignment - same as primary key (recursive relationship): %s", foreignKeyFieldName)
}
_, err := p.ProcessNestedCUD(ctx, operation, v, relatedModel, parentIDs, relatedTableName)
if err != nil {
@@ -420,9 +432,12 @@ func (p *NestedCUDProcessor) processChildRelations(
for i, item := range v {
if itemMap, ok := item.(map[string]interface{}); ok {
// Directly set foreign key if specified
if parentID != nil && foreignKeyFieldName != "" {
// IMPORTANT: In recursive relationships, don't overwrite the primary key
if parentID != nil && foreignKeyFieldName != "" && foreignKeyFieldName != childPKFieldName {
itemMap[foreignKeyFieldName] = parentID
logger.Debug("Set foreign key in relation array[%d]: %s=%v", i, foreignKeyFieldName, parentID)
} else if foreignKeyFieldName == childPKFieldName {
logger.Debug("Skipping foreign key assignment in array[%d] - same as primary key (recursive relationship): %s", i, foreignKeyFieldName)
}
_, err := p.ProcessNestedCUD(ctx, operation, itemMap, relatedModel, parentIDs, relatedTableName)
if err != nil {
@@ -439,9 +454,12 @@ func (p *NestedCUDProcessor) processChildRelations(
// Multiple related objects (typed slice)
for i, itemMap := range v {
// Directly set foreign key if specified
if parentID != nil && foreignKeyFieldName != "" {
// IMPORTANT: In recursive relationships, don't overwrite the primary key
if parentID != nil && foreignKeyFieldName != "" && foreignKeyFieldName != childPKFieldName {
itemMap[foreignKeyFieldName] = parentID
logger.Debug("Set foreign key in relation typed array[%d]: %s=%v", i, foreignKeyFieldName, parentID)
} else if foreignKeyFieldName == childPKFieldName {
logger.Debug("Skipping foreign key assignment in typed array[%d] - same as primary key (recursive relationship): %s", i, foreignKeyFieldName)
}
_, err := p.ProcessNestedCUD(ctx, operation, itemMap, relatedModel, parentIDs, relatedTableName)
if err != nil {

View File

@@ -1813,16 +1813,26 @@ func (h *Handler) processChildRelationsForField(
}
}
logger.Debug("Setting parent ID in child data: foreignKeyField=%s, parentID=%v, relForeignKey=%s",
foreignKeyFieldName, parentID, relInfo.ForeignKey)
// Get the primary key name for the child model to avoid overwriting it in recursive relationships
childPKName := reflection.GetPrimaryKeyName(relatedModel)
childPKFieldName := reflection.GetJSONNameForField(relatedModelType, childPKName)
if childPKFieldName == "" {
childPKFieldName = strings.ToLower(childPKName)
}
logger.Debug("Setting parent ID in child data: foreignKeyField=%s, parentID=%v, relForeignKey=%s, childPK=%s",
foreignKeyFieldName, parentID, relInfo.ForeignKey, childPKFieldName)
// Process based on relation type and data structure
switch v := relationValue.(type) {
case map[string]interface{}:
// Single related object - add parent ID to foreign key field
if parentID != nil && foreignKeyFieldName != "" {
// IMPORTANT: In recursive relationships, don't overwrite the primary key
if parentID != nil && foreignKeyFieldName != "" && foreignKeyFieldName != childPKFieldName {
v[foreignKeyFieldName] = parentID
logger.Debug("Set foreign key in single relation: %s=%v", foreignKeyFieldName, parentID)
} else if foreignKeyFieldName == childPKFieldName {
logger.Debug("Skipping foreign key assignment - same as primary key (recursive relationship): %s", foreignKeyFieldName)
}
_, err := processor.ProcessNestedCUD(ctx, operation, v, relatedModel, parentIDs, relatedTableName)
if err != nil {
@@ -1834,9 +1844,12 @@ func (h *Handler) processChildRelationsForField(
for i, item := range v {
if itemMap, ok := item.(map[string]interface{}); ok {
// Add parent ID to foreign key field
if parentID != nil && foreignKeyFieldName != "" {
// IMPORTANT: In recursive relationships, don't overwrite the primary key
if parentID != nil && foreignKeyFieldName != "" && foreignKeyFieldName != childPKFieldName {
itemMap[foreignKeyFieldName] = parentID
logger.Debug("Set foreign key in relation array[%d]: %s=%v", i, foreignKeyFieldName, parentID)
} else if foreignKeyFieldName == childPKFieldName {
logger.Debug("Skipping foreign key assignment in array[%d] - same as primary key (recursive relationship): %s", i, foreignKeyFieldName)
}
_, err := processor.ProcessNestedCUD(ctx, operation, itemMap, relatedModel, parentIDs, relatedTableName)
if err != nil {
@@ -1849,9 +1862,12 @@ func (h *Handler) processChildRelationsForField(
// Multiple related objects (typed slice)
for i, itemMap := range v {
// Add parent ID to foreign key field
if parentID != nil && foreignKeyFieldName != "" {
// IMPORTANT: In recursive relationships, don't overwrite the primary key
if parentID != nil && foreignKeyFieldName != "" && foreignKeyFieldName != childPKFieldName {
itemMap[foreignKeyFieldName] = parentID
logger.Debug("Set foreign key in relation typed array[%d]: %s=%v", i, foreignKeyFieldName, parentID)
} else if foreignKeyFieldName == childPKFieldName {
logger.Debug("Skipping foreign key assignment in typed array[%d] - same as primary key (recursive relationship): %s", i, foreignKeyFieldName)
}
_, err := processor.ProcessNestedCUD(ctx, operation, itemMap, relatedModel, parentIDs, relatedTableName)
if err != nil {