Skip to content

lawrencewoodman/reform

 
 

Repository files navigation

reform GoDoc Build Status Coverage Status Go Report Card

A better ORM for Go.

It uses non-empty interfaces, code generation (go generate) and initialization-time reflection as opposed to interface{} and runtime reflection. It will be kept simple.

Quickstart

  1. Install it: go get github.com/AlekSi/reform/reform (see about versioning below)

  2. Define your first model in file person.go:

    //go:generate reform
    
    //reform:people
    Person struct {
    	ID        int32      `reform:"id,pk"`
    	Name      string     `reform:"name"`
    	Email     *string    `reform:"email"`
    	CreatedAt time.Time  `reform:"created_at"`
    	UpdatedAt *time.Time `reform:"updated_at"`
    }

    Magic comment //reform:people links this model to people table or view in SQL database. First value in reform tag is a column name. pk marks primary key. Use pointers for nullable fields.

  3. Run reform [package or directory] or go generate [package or file]. This will create person_reform.go in the same package with type PersonTable and methods on Person.

  4. See documentation how to use it. Simple example:

    // save record (performs INSERT or UPDATE)
    person := &Person{
    	Name:  "Alexey Palazhchenko",
    	Email: pointer.ToString("alexey.palazhchenko@gmail.com"),
    }
    if err := DB.Save(person); err != nil {
    	log.Fatal(err)
    }
    
    // ID is filled by Save
    person2, err := DB.FindByPrimaryKeyFrom(PersonTable, person.ID)
    if err != nil {
    	log.Fatal(err)
    }
    fmt.Println(person2.(*Person).Name)
    
    // delete record
    if err = DB.Delete(person); err != nil {
    	log.Fatal(err)
    }
    
    // find records by IDs
    persons, err := DB.FindAllFrom(PersonTable, "id", 1, 2)
    if err != nil {
    	log.Fatal(err)
    }
    for _, p := range persons {
    	fmt.Println(p)
    }
    
    // Output:
    // Alexey Palazhchenko
    // ID: 1 (int32), Name: `Denis Mills` (string), Email: <nil> (*string), CreatedAt: 2009-11-10 23:00:00 +0000 UTC (time.Time), UpdatedAt: <nil> (*time.Time)
    // ID: 2 (int32), Name: `Garrick Muller` (string), Email: `muller_garrick@example.com` (*string), CreatedAt: 2009-12-12 12:34:56 +0000 UTC (time.Time), UpdatedAt: <nil> (*time.Time)

Background

reform was born during summer 2014 out of frustrations with existing Go ORMs. All of them have a method Save(record interface{}) which can be used like this:

orm.Save(User{Name: "gopher"})
orm.Save(&User{Name: "gopher"})
orm.Save(nil)
orm.Save("Batman!!")

Now you can say that last invocation is obviously invalid, and that it's not hard to make an ORM to accept both first and second versions. But there are two problems:

  1. Compiler can't check it. Method's signature in godoc will not tell us how to use it. We are essentially working against them.
  2. First version is still invalid, since one would expect Save() method to set record's primary key after INSERT, but this change will be lost due to passing by value.

First proprietary version of reform was used in production even before go generate announcement. This free and open-source version is the fourth iteration on the road to better and idiomatic API.

Versioning

We will switch to proper versioning via gopkg.in at June 2016. Before that moment breaking changes MAY be applied, but are not expected.

Additional packages

Caveats

  • There should be zero pk fields for Struct and exactly one pk field for Record.
  • pk field can't be a pointer (== nil doesn't work).
  • Database row can't have a Go's zero value (0, empty string, etc.) in primary key column.

About

A better ORM for Go, based on non-empty interfaces and code generation.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Go 96.1%
  • Makefile 3.9%