gormcngen: Provides a Columns() Function to Retrieve Column Names from GORM Models
Like MyBatis Plus in the Java ecosystem, which allows developers to get column names using expressions like Example::getName.
Like SQLAlchemy in the Python ecosystem, which allows developers to access column names using a class function, like Example.name.
gormcngen also brings type-safe column referencing to Go models.
| Language | ORM | Type-Safe Columns | Example |
|---|---|---|---|
| Java | MyBatis Plus | Example::getName |
wrapper.eq(Example::getName, "alice") |
| Python | SQLAlchemy | Example.name |
query.filter(Example.name == "alice") |
| Go | GORMCNGEN | cls.Name.Eq() |
db.Where(cls.Name.Eq("alice")) |
go get github.com/yyle88/gormcngengormcngen is a Go package, not a CLI application. It requires a test-code-driven generation workflow.
Set up the basic project structure and create dedicated DIR to hold models and generated code:
# Create models DIR
mkdir -p internal/modelsDefine data models - gormcngen generates column access methods from these models:
Create internal/models/models.go:
package models
type Account struct {
ID uint `gorm:"primaryKey"`
Username string `gorm:"uniqueIndex;size:100"`
Mailbox string `gorm:"index;size:255"`
Age int `gorm:"column:age"`
IsActive bool `gorm:"default:true"`
}Create the target file to hold generated code and the test file containing generation logic:
# Create target file to hold generated code with package declaration
echo "package models" > internal/models/ngen.go
# Create test file containing generation logic with package declaration
echo "package models" > internal/models/ngen_test.goWrite the code generation logic in the test file, configure generation options and set the models to process.
Note: In Go, using test files to generate source code is a common practice.
Create internal/models/ngen_test.go:
package models
import (
"testing"
"github.com/yyle88/gormcngen"
"github.com/yyle88/osexistpath/osmustexist"
"github.com/yyle88/runpath/runtestpath"
)
//go:generate go test -run ^TestGenerate$
func TestGenerate(t *testing.T) {
// Get absolute path to target file (ngen.go)
absPath := osmustexist.FILE(runtestpath.SrcPath(t))
t.Log(absPath)
// Configure generation options
options := gormcngen.NewOptions().
WithColumnClassExportable(true).
WithColumnsMethodRecvName("c").
WithColumnsCheckFieldType(true)
// Define models to process
models := []interface{}{
&Account{},
}
// Create config and generate
cfg := gormcngen.NewConfigs(models, options, absPath)
cfg.WithIsGenPreventEdit(true) // Add "DO NOT EDIT" warning headers (default: true)
cfg.WithGeneratedFromPos(gormcngen.GetGenPosFuncMark(0)) // Show generation source location (default: show)
cfg.Gen()
}Run the test to initiate code generation - the generated code gets auto written to the target file:
# Clean up dependencies
go mod tidy
# Run generation test
cd internal/models
go test -v ./...🎉 Generation Complete! The ngen.go file now contains the generated column access methods.
The generated ngen.go contains:
// Code generated using gormcngen. DO NOT EDIT.
// This file was auto generated via github.com/yyle88/gormcngen
// Generated from: ngen_test.go:20 -> models.TestGenerate
// ========== GORMCNGEN:DO-NOT-EDIT-MARKER:END ==========
func (c *Account) Columns() *AccountColumns {
return &AccountColumns{
ID: gormcnm.Cnm(c.ID, "id"),
Username: gormcnm.Cnm(c.Username, "username"),
Mailbox: gormcnm.Cnm(c.Mailbox, "mailbox"),
Age: gormcnm.Cnm(c.Age, "age"),
IsActive: gormcnm.Cnm(c.IsActive, "is_active"),
}
}
type AccountColumns struct {
gormcnm.ColumnOperationClass
ID gormcnm.ColumnName[uint]
Username gormcnm.ColumnName[string]
Mailbox gormcnm.ColumnName[string]
Age gormcnm.ColumnName[int]
IsActive gormcnm.ColumnName[bool]
}🚀 Setup Complete! You now have type-safe column access methods to work with models.
Now when writing business code, you can use the generated type-safe column methods to build database queries:
var account Account
cls := account.Columns()
// Perfect type protection with zero boilerplate
err := db.Where(cls.Username.Eq("alice")).
Where(cls.Age.Gte(18)).
Where(cls.IsActive.Eq(true)).
First(&account).Error// Basic configuration (matches internal examples)
options := gormcngen.NewOptions().
WithColumnClassExportable(true). // Generate exported ExampleColumns struct
WithEmbedColumnOperations(false) // Don't embed operation methods
// Chinese field name support
chineseOptions := gormcngen.NewOptions().
WithUseTagName(true). // Use cnm tag values as field names
WithTagKeyName("cnm"). // Set 'cnm' as the tag name
WithColumnClassExportable(true)
// Advanced features (from example6)
advancedOptions := gormcngen.NewOptions().
WithColumnClassExportable(true). // Exported struct names
WithColumnsMethodRecvName("one"). // Custom method argument
WithColumnsCheckFieldType(true). // Type checking (recommended)
WithIsGenFuncTableColumns(true) // Generate TableColumns function
// Batch processing multiple models
allModels := []interface{}{&Account{}, &Product{}, &Item{}, &Client{}}
configs := gormcngen.NewConfigs(allModels, options, "models_gen.go")
configs.WithIsGenPreventEdit(true) // Add "DO NOT EDIT" headers to generated files
configs.WithGeneratedFromPos(gormcngen.GetGenPosFuncMark(0)) // Show generation source location (default: show)
configs.Gen()The cnm tag lets you define Chinese aliases to use as field names, which are generated as extra struct fields:
type Product struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:255;not null" cnm:"V产品名称"`
Price decimal.Decimal `gorm:"type:decimal(10,2)"`
CategoryID uint `gorm:"index"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
}Generated Result:
type ProductColumns struct {
gormcnm.ColumnOperationClass
// The column names and types of the model's columns
ID gormcnm.ColumnName[uint]
Name gormcnm.ColumnName[string] // Maps to "name"
V产品名称 gormcnm.ColumnName[string] // Chinese alias mapping to Name field
Price gormcnm.ColumnName[decimal.Decimal]
CategoryID gormcnm.ColumnName[uint]
CreatedAt gormcnm.ColumnName[time.Time]
UpdatedAt gormcnm.ColumnName[time.Time]
}
func (*Product) Columns() *ProductColumns {
return &ProductColumns{
ID: "id",
Name: "name",
V产品名称: "name", // Chinese alias pointing to same column
Price: "price",
CategoryID: "category_id",
CreatedAt: "created_at",
UpdatedAt: "updated_at",
}
}Using Chinese Field Names in Queries:
With the generated Chinese aliases, you can write queries using native language:
var product Product
var cls = product.Columns()
// Query using Chinese field names - same database column, different Go field name
if err := db.Where(cls.V产品名称.Eq("iPhone")).
Where(cls.Price.Gte(5000.00)).
First(&product).Error; err != nil {
panic(errors.WithMessage(err, "product not found"))
}
fmt.Println("Found product:", product.Name)This allows developers to write more readable code in native language while maintaining complete type protection and database support.
Create a generation script:
scripts/generate_columns.go:
package main
import (
"github.com/yyle88/gormcngen"
"project-name/models"
)
func main() {
models := []interface{}{&models.Account{}}
options := gormcngen.NewOptions()
configs := gormcngen.NewConfigs(models, options, "models/account_columns_gen.go")
configs.Gen()
}Then use in the target files:
//go:generate go run scripts/generate_columns.go
type Account struct {
ID uint `gorm:"primaryKey"`
Username string `gorm:"uniqueIndex"`
Mailbox string `gorm:"index"`
}Combine gormcngen with gormrepo to get type-safe CRUD operations.
// Create repo with columns
repo := gormrepo.NewRepo(&Account{}, (&Account{}).Columns())
// Concise approach with gormrepo/gormclass
repo := gormrepo.NewRepo(gormclass.Use(&Account{}))
// Type-safe queries
account, err := repo.With(ctx, db).First(func(db *gorm.DB, cls *AccountColumns) *gorm.DB {
return db.Where(cls.Username.Eq("alice"))
})
// Find with conditions
accounts, err := repo.With(ctx, db).Find(func(db *gorm.DB, cls *AccountColumns) *gorm.DB {
return db.Where(cls.Age.Gte(18)).Where(cls.Age.Lte(65))
})
// Type-safe updates
err := repo.With(ctx, db).Updates(
func(db *gorm.DB, cls *AccountColumns) *gorm.DB {
return db.Where(cls.ID.Eq(1))
},
func(cls *AccountColumns) map[string]interface{} {
return cls.Kw(cls.Age.Kv(26)).Kw(cls.Nickname.Kv("NewNick")).AsMap()
},
)👉 See gormrepo to get complete documentation and more examples.
See examples and demos directories.
Explore the complete GORM ecosystem with these integrated packages:
- gormcnm - GORM foundation providing type-safe column operations and query builders
- gormcngen - Code generation using AST enabling type-safe GORM operations (this project)
- gormrepo - Repository pattern implementation with GORM best practices
- gormmom - Native language GORM tag generation engine with smart column naming
- gormzhcn - Complete Chinese programming interface with GORM
Each package targets different aspects of GORM development, from localization to type safety and code generation.
MIT License - see LICENSE.
Contributions are welcome! Report bugs, suggest features, and contribute code:
- 🐛 Mistake reports? Open an issue on GitHub with reproduction steps
- 💡 Fresh ideas? Create an issue to discuss
- 📖 Documentation confusing? Report it so we can improve
- 🚀 Need new features? Share the use cases to help us understand requirements
- ⚡ Performance issue? Help us optimize through reporting slow operations
- 🔧 Configuration problem? Ask questions about complex setups
- 📢 Follow project progress? Watch the repo to get new releases and features
- 🌟 Success stories? Share how this package improved the workflow
- 💬 Feedback? We welcome suggestions and comments
New code contributions, follow this process:
- Fork: Fork the repo on GitHub (using the webpage UI).
- Clone: Clone the forked project (
git clone https://github.com/yourname/repo-name.git). - Navigate: Navigate to the cloned project (
cd repo-name) - Branch: Create a feature branch (
git checkout -b feature/xxx). - Code: Implement the changes with comprehensive tests
- Testing: (Golang project) Ensure tests pass (
go test ./...) and follow Go code style conventions - Documentation: Update documentation to support client-facing changes
- Stage: Stage changes (
git add .) - Commit: Commit changes (
git commit -m "Add feature xxx") ensuring backward compatible code - Push: Push to the branch (
git push origin feature/xxx). - PR: Open a merge request on GitHub (on the GitHub webpage) with detailed description.
Please ensure tests pass and include relevant documentation updates.
Welcome to contribute to this project via submitting merge requests and reporting issues.
Project Support:
- ⭐ Give GitHub stars if this project helps you
- 🤝 Share with teammates and (golang) programming friends
- 📝 Write tech blogs about development tools and workflows - we provide content writing support
- 🌟 Join the ecosystem - committed to supporting open source and the (golang) development scene
Have Fun Coding with this package! 🎉🎉🎉