{"record":{"id":"f652e2082c803aea","repo":"beego/beego","slug":"one-model-must-have-one-pk-field-only","errorCode":null,"errorMessage":"one model must have one pk field only","messagePattern":"one model must have one pk field only","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/orm/internal/models/models_info_m.go","lineNumber":88,"sourceCode":"\t\tfi, err = NewFieldInfo(mi, field, sf, mName)\n\t\tif err == errSkipField {\n\t\t\terr = nil\n\t\t\tcontinue\n\t\t} else if err != nil {\n\t\t\tbreak\n\t\t}\n\t\t// record current field index\n\t\tfi.FieldIndex = append(fi.FieldIndex, index...)\n\t\tfi.FieldIndex = append(fi.FieldIndex, i)\n\t\tfi.Mi = mi\n\t\tfi.InModel = true\n\t\tif !mi.Fields.Add(fi) {\n\t\t\terr = fmt.Errorf(\"duplicate column name: %s\", fi.Column)\n\t\t\tbreak\n\t\t}\n\t\tif fi.Pk {\n\t\t\tif mi.Fields.Pk != nil {\n\t\t\t\terr = fmt.Errorf(\"one model must have one pk field only\")\n\t\t\t\tbreak\n\t\t\t} else {\n\t\t\t\tmi.Fields.Pk = fi\n\t\t\t}\n\t\t}\n\t}\n\n\tif err != nil {\n\t\tfmt.Println(fmt.Errorf(\"field: %s.%s, %s\", ind.Type(), sf.Name, err))\n\t\tos.Exit(2)\n\t}\n}\n\n// NewM2MModelInfo combine related model info to new model info.\n// prepare for relation models query.\nfunc NewM2MModelInfo(m1, m2 *ModelInfo) (mi *ModelInfo) {\n\tmi = new(ModelInfo)\n\tmi.Fields = NewFields()","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/beego/beego/blob/939cfde380bb9f15844ad633b84f037f7da21584/client/orm/internal/models/models_info_m.go#L70-L106","documentation":"A model was registered with more than one field marked `pk`. While collecting fields, beego assigns mi.Fields.Pk to the first pk field; a second field with fi.Pk set triggers 'one model must have one pk field only' and registration aborts (fatal via the 'field: ...' wrapper and os.Exit).","triggerScenarios":"Two fields with `orm:\"pk\"` in one struct; a struct embedding a base model that already declares a pk while the child declares another; `orm:\"pk;auto\"` on Id plus `orm:\"pk\"` on a natural key like Email.","commonSituations":"Embedding a shared BaseModel (with Id pk) and redeclaring a pk in the child; composite-key habits from other ORMs — beego does not support composite primary keys via multiple pk tags.","solutions":["Keep exactly one pk per model, typically `Id int `orm:\"pk;auto\"``","When embedding a base struct that already has the pk, do not declare another pk in the child","For secondary unique keys use `unique` instead of `pk`","For join tables, let beego auto-generate them (rel(m2m)) instead of hand-modeling composite keys"],"exampleFix":"// before\nId    int    `orm:\"pk;auto\"`\nEmail string `orm:\"pk\"`\n// after\nId    int    `orm:\"pk;auto\"`\nEmail string `orm:\"unique\"`","handlingStrategy":"validation","validationCode":"func countPk(t reflect.Type) int {\n    n := 0\n    for i := 0; i < t.NumField(); i++ {\n        if strings.Contains(t.Field(i).Tag.Get(\"orm\"), \"pk\") { n++ }\n    }\n    return n\n}\n// assert countPk == 1 for every registered model in a startup test","typeGuard":null,"tryCatchPattern":null,"preventionTips":["One pk per struct; convention: Id int with pk;auto","When embedding a BaseModel with pk, never add another","Use `unique` for secondary keys"],"tags":["go","beego","orm","primary-key","model-registration"],"backgroundTag":null,"analyzedSha":"939cfde380bb9f15844ad633b84f037f7da21584","analyzedAt":"2026-08-15T17:22:06.535Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}