From d4d6618e7bd4fb36c89f7e92b5789c98c7862ebb Mon Sep 17 00:00:00 2001 From: Jinzhu Date: Tue, 28 Jan 2014 02:46:54 -0800 Subject: [PATCH] Create gh-pages branch via GitHub --- images/arrow-down.png | Bin 0 -> 423 bytes images/octocat-small.png | Bin 0 -> 570 bytes index.html | 1104 ++++++++++++++++++++++++++++++++++ javascripts/scale.fix.js | 20 + params.json | 1 + stylesheets/pygment_trac.css | 69 +++ stylesheets/styles.css | 421 +++++++++++++ 7 files changed, 1615 insertions(+) create mode 100644 images/arrow-down.png create mode 100644 images/octocat-small.png create mode 100644 index.html create mode 100644 javascripts/scale.fix.js create mode 100644 params.json create mode 100644 stylesheets/pygment_trac.css create mode 100644 stylesheets/styles.css diff --git a/images/arrow-down.png b/images/arrow-down.png new file mode 100644 index 0000000000000000000000000000000000000000..585b0bddba878b95acc961fc5c0c55c3ea2e75db GIT binary patch literal 423 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eW!3HFke0{SLNU;<^miKrK zR4D>d>0FeWSdy8arx22vo62CJZ=!E#Q zuUEhHFKq}eVcf!fOXk2252o^`A0Pfx?pl-`?tS^o=dZa}n)o-rOj#-_e`l7jy7u+r zhj0Hd|L1+)z+f_A|GJISy4qrPpME*#k;S{#M4)d)HS2xXctpM~nN-ejV$=G%1)}zh z6_&;B=NTB*Rz&o)EN^QNw)=Nauj9or^J@~D*Th}TXM8@bL&NZxPHmc!SKl9%dr^gl zqh@m|zG6sNdzVY`kauM4^m$&|dG9N?-~61nSXA6TxH5R=Pxc4Kow9qMI-LOq1%s!n KpUXO@geCw=gQOS$ literal 0 HcmV?d00001 diff --git a/images/octocat-small.png b/images/octocat-small.png new file mode 100644 index 0000000000000000000000000000000000000000..66c25398dd9090905e37aa2d48bb2d77a0ac6255 GIT binary patch literal 570 zcmV-A0>%A_P)V>IRB3Hx05~r+FEKJgdgKHE00ELo zL_t(Ijg^xed`v89bwq|>- zBcAI>wNjQ~{V)H%tlWtR3ZPgl_WI*s7zQut?CiWv)3hNC$Q&OX?xs?y7lFshPE4M* zWwYbCv9ZzqmMOqA&6xTyFpvlX0a(^MlwlaPupofy>3N$E3)SoOTg`Kw1aKY(ER{+J z8i40IIbbma`(6R7dL#-k0u){W3czrO-f82x&g$~grz@IyWqNvQc6)p4Nm@33tb3m8 zqya>Phsa&m{#?w>&fMT<@_Edvm9hja12DK_rqO6@Dy6#qH=`bjY5@o|v#Lj;wod}b>JrdoE#olbS0=7RdB$LTJ@R`4#*!KNI_tZAjO+JC<^Z)<=07*qo IM6N<$f|}m$2LJ#7 literal 0 HcmV?d00001 diff --git a/index.html b/index.html new file mode 100644 index 00000000..a2cc3f10 --- /dev/null +++ b/index.html @@ -0,0 +1,1104 @@ + + + + + + Gorm by jinzhu + + + + + + + + +
+
+

Gorm

+

The fantastic ORM library for Golang, aims to be developer friendly.

+ + + +

This project is maintained by jinzhu

+ + +
+
+

+GORM

+ +

The fantastic ORM library for Golang, aims to be developer friendly.

+ +

+Install

+ +
go get github.com/jinzhu/gorm
+
+ +

+Overview

+ +
    +
  • Chainable API
  • +
  • Relations
  • +
  • Callbacks (before/after create/save/update/delete/find)
  • +
  • Soft Deletes
  • +
  • Auto Migrations
  • +
  • Transactions
  • +
  • Logger Support
  • +
  • Bind struct with tag
  • +
  • Iteration Support via Rows +
  • +
  • Scopes
  • +
  • sql.Scanner support
  • +
  • Every feature comes with tests
  • +
  • Convention Over Configuration
  • +
  • Developer Friendly
  • +

+Conventions

+ +
    +
  • Table name is the plural of struct name's snake case. +Disable pluralization with db.SingularTable(true), or specify your table name +
  • +
  • Column name is the snake case of field's name.
  • +
  • Use Id int64 field as primary key.
  • +
  • Use tag sql to change field's property, change the tag name with db.SetTagIdentifier(new_name).
  • +
  • Use CreatedAt to store record's created time if field exists.
  • +
  • Use UpdatedAt to store record's updated time if field exists.
  • +
  • Use DeletedAt to store record's deleted time if field exists. Soft Delete +
  • +
  • Gorm uses reflection to know which tables to work with:
  • +
// E.g Finding an existing User
+var user User
+// Gorm will now know to use table "users" ("user" if pluralisation has been disabled) for all operations.
+db.First(&user)
+
+// E.g creating a new User
+DB.Save(&User{Name: "xxx"}) // table "users"
+
+ +

+Getting Started

+ +
import (
+    "database/sql"
+    "time"
+)
+
+type User struct {
+    Id           int64
+    Birthday     time.Time
+    Age          int64
+    Name         string  `sql:"size:255"`
+    CreatedAt    time.Time
+    UpdatedAt    time.Time
+    DeletedAt    time.Time
+
+    Emails            []Email         // Embedded structs
+    BillingAddress    Address         // Embedded struct
+    BillingAddressId  sql.NullInt64   // BillingAddress's foreign key
+    ShippingAddress   Address         // Another Embedded struct with same type
+    ShippingAddressId int64           // ShippingAddress's foreign key
+    IgnoreMe          int64 `sql:"-"` // Ignore this field
+}
+
+type Email struct {
+    Id         int64
+    UserId     int64   // Foreign key for User
+    Email      string  `sql:"type:varchar(100);"` // Set this field's type
+    Subscribed bool
+}
+
+type Address struct {
+    Id       int64
+    Address1 string         `sql:"not null;unique"` // Set this field as not nullable and unique in database
+    Address2 string         `sql:"type:varchar(100);unique"`
+    Post     sql.NullString `sql:not null`
+    // FYI, "NOT NULL" will only work well with NullXXX Scanner, because golang will initalize a default value for most type...
+}
+
+ +

+Opening a Database

+ +
+import "github.com/jinzhu/gorm"
+import _ "github.com/lib/pq"
+// import _ "github.com/go-sql-driver/mysql"
+// import _ "github.com/mattn/go-sqlite3"
+
+db, err := Open("postgres", "user=gorm dbname=gorm sslmode=disable")
+// db, err = Open("mysql", "gorm:gorm@/gorm?charset=utf8&parseTime=True")
+// db, err = Open("sqlite3", "/tmp/gorm.db")
+
+// Get database connection handle [*sql.DB](http://golang.org/pkg/database/sql/#DB)
+d := db.DB()
+
+// With it you could use package `database/sql`'s builtin methods
+db.DB().SetMaxIdleConns(10)
+db.DB().SetMaxOpenConns(100)
+db.DB().Ping()
+
+// By default, table name is plural of struct type, you can use struct type as table name with:
+db.SingularTable(true)
+
+ +

Gorm is goroutines friendly, so you can create a global variable to keep the connection and use it everywhere in your project.

+ +
// db.go
+package db
+
+import (
+    "fmt"
+    "github.com/jinzhu/gorm"
+    _ "github.com/lib/pq"
+)
+
+var DB gorm.DB
+func init() {
+    var err error
+    DB, err = gorm.Open("postgres", "user=gorm dbname=gorm sslmode=disable")
+
+    // Connection string parameters for Postgres - http://godoc.org/github.com/lib/pq, if you are using another
+    // database refer to the relevant driver's documentation.
+
+    // * dbname - The name of the database to connect to
+    // * user - The user to sign in as
+    // * password - The user's password
+    // * host - The host to connect to. Values that start with / are for unix domain sockets.
+    //   (default is localhost)
+    // * port - The port to bind to. (default is 5432)
+    // * sslmode - Whether or not to use SSL (default is require, this is not the default for libpq)
+    //   Valid SSL modes:
+    //    * disable - No SSL
+    //    * require - Always SSL (skip verification)
+    //    * verify-full - Always SSL (require verification)
+
+    if err != nil {
+        panic(fmt.Sprintf("Got error when connect database, the error is '%v'", err))
+    }
+}
+
+// user.go
+package user
+import . "db"
+...
+DB.Save(&User{Name: "xxx"})
+...
+
+ +

+Struct & Database Mapping

+ +
// Create table from struct
+db.CreateTable(User{})
+
+// Drop table
+db.DropTable(User{})
+
+ +

+Automating Migrations

+ +

Feel free to update your struct, AutoMigrate will keep your database update to date.

+ +

FYI, AutoMigrate will only add new columns, it won't change the current columns' types or delete unused columns, to make sure your data is safe.

+ +

If the table doesn't exist when AutoMigrate, gorm will run create the table automatically. +(the database first needs to be created manually though...).

+ +

(only postgres and mysql supported)

+ +
db.AutoMigrate(User{})
+
+ +

+Gorm API

+ +

+Create

+ +
user := User{Name: "jinzhu", Age: 18, Birthday: time.Now()}
+db.Save(&user)
+
+ +

+NewRecord

+ +

Returns true if object hasn’t been saved yet (Id is blank)

+ +
user := User{Name: "jinzhu", Age: 18, Birthday: time.Now()}
+db.NewRecord(user) // => true
+
+db.Save(&user)
+db.NewRecord(user) // => false
+
+ +

+Create With SubStruct

+ +

Refer to Query With Related for how to find associations

+ +
user := User{
+        Name:            "jinzhu",
+        BillingAddress:  Address{Address1: "Billing Address - Address 1"},
+        ShippingAddress: Address{Address1: "Shipping Address - Address 1"},
+        Emails:          []Email{{Email: "jinzhu@example.com"}, {Email: "jinzhu-2@example@example.com"}},
+}
+
+db.Save(&user)
+//// BEGIN TRANSACTION;
+//// INSERT INTO "addresses" (address1) VALUES ("Billing Address - Address 1");
+//// INSERT INTO "addresses" (address1) VALUES ("Shipping Address - Address 1");
+//// INSERT INTO "users" (name,billing_address_id,shipping_address_id) VALUES ("jinzhu", 1, 2);
+//// INSERT INTO "emails" (user_id,email) VALUES (111, "jinzhu@example.com");
+//// INSERT INTO "emails" (user_id,email) VALUES (111, "jinzhu-2@example.com");
+//// COMMIT;
+
+ +

+Query

+ +
// Get the first record
+db.First(&user)
+//// SELECT * FROM users ORDER BY id LIMIT 1;
+// Search table `users` is guessed from struct's type
+
+// Get the last record
+db.Last(&user)
+//// SELECT * FROM users ORDER BY id DESC LIMIT 1;
+
+// Get All records
+db.Find(&users)
+//// SELECT * FROM users;
+
+// Get record with primary key
+db.First(&user, 10)
+//// SELECT * FROM users WHERE id = 10;
+
+ +

+Query With Where (SQL)

+ +
// Get the first matched record
+db.Where("name = ?", "jinzhu").First(&user)
+//// SELECT * FROM users WHERE name = 'jinzhu' limit 1;
+
+// Get all matched records
+db.Where("name = ?", "jinzhu").Find(&users)
+//// SELECT * FROM users WHERE name = 'jinzhu';
+
+db.Where("name <> ?", "jinzhu").Find(&users)
+//// SELECT * FROM users WHERE name <> 'jinzhu';
+
+// IN
+db.Where("name in (?)", []string{"jinzhu", "jinzhu 2"}).Find(&users)
+//// SELECT * FROM users WHERE name IN ('jinzhu', 'jinzhu 2');
+
+// LIKE
+db.Where("name LIKE ?", "%jin%").Find(&users)
+//// SELECT * FROM users WHERE name LIKE "%jin%";
+
+// Multiple Conditions
+db.Where("name = ? and age >= ?", "jinzhu", "22").Find(&users)
+//// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;
+
+ +

+Query With Where (Struct & Map)

+ +
// Search with struct
+db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
+//// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 LIMIT 1;
+
+// Search with map
+db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
+//// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;
+
+// IN for primary Keys
+db.Where([]int64{20, 21, 22}).Find(&users)
+//// SELECT * FROM users WHERE id IN (20, 21, 22);
+
+ +

+Query With Not

+ +
// Attribute Not Equal
+db.Not("name", "jinzhu").First(&user)
+//// SELECT * FROM users WHERE name <> "jinzhu" LIMIT 1;
+
+// Not In
+db.Not("name", []string{"jinzhu", "jinzhu 2"}).Find(&users)
+//// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");
+
+// Not In for primary keys
+db.Not([]int64{1,2,3}).First(&user)
+//// SELECT * FROM users WHERE id NOT IN (1,2,3);
+
+db.Not([]int64{}).First(&user)
+//// SELECT * FROM users;
+
+// SQL string
+db.Not("name = ?", "jinzhu").First(&user)
+//// SELECT * FROM users WHERE NOT(name = "jinzhu");
+
+// Not with struct
+db.Not(User{Name: "jinzhu"}).First(&user)
+//// SELECT * FROM users WHERE name <> "jinzhu";
+
+ +

+Query With Inline Condition

+ +
// Find with primary key
+db.First(&user, 23)
+//// SELECT * FROM users WHERE id = 23 LIMIT 1;
+
+// SQL string
+db.Find(&user, "name = ?", "jinzhu")
+//// SELECT * FROM users WHERE name = "jinzhu";
+
+// Multiple Conditions
+db.Find(&users, "name <> ? and age > ?", "jinzhu", 20)
+//// SELECT * FROM users WHERE name <> "jinzhu" AND age > 20;
+
+// Inline search with struct
+db.Find(&users, User{Age: 20})
+//// SELECT * FROM users WHERE age = 20;
+
+// Inline search with map
+db.Find(&users, map[string]interface{}{"age": 20})
+//// SELECT * FROM users WHERE age = 20;
+
+ +

+Query With Or

+ +
db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
+//// SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';
+
+// Or With Struct
+db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2"}).Find(&users)
+//// SELECT * FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2';
+
+// Or With Map
+db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2"}).Find(&users)
+
+ +

+Query With Related

+ +
// Find user's emails with guessed foreign key
+db.Model(&user).Related(&emails)
+//// SELECT * FROM emails WHERE user_id = 111;
+
+// Find user's billing address with specified foreign key 'BillingAddressId'
+db.Model(&user).Related(&address1, "BillingAddressId")
+//// SELECT * FROM addresses WHERE id = 123; // 123 is user's BillingAddressId
+
+// Find user with guessed primary key value from email
+db.Model(&email).Related(&user)
+//// SELECT * FROM users WHERE id = 111; // 111 is email's UserId
+
+ +

+Query Chains

+ +

Gorm has a chainable API, so you could query like this

+ +
db.Where("name <> ?","jinzhu").Where("age >= ? and role <> ?",20,"admin").Find(&users)
+//// SELECT * FROM users WHERE name <> 'jinzhu' AND age >= 20 AND role <> 'admin';
+
+db.Where("role = ?", "admin").Or("role = ?", "super_admin").Not("name = ?", "jinzhu").Find(&users)
+
+ +

+Update

+ +

+Update an existing struct

+ +
user.Name = "jinzhu 2"
+user.Age = 100
+db.Save(&user)
+//// UPDATE users SET name='jinzhu 2', age=100, updated_at = '2013-11-17 21:34:10' WHERE id=111;
+
+ +

+Update one attribute with Update +

+ +
// Update existing user's name if it is changed
+db.Model(&user).Update("name", "hello")
+//// UPDATE users SET name='hello', updated_at = '2013-11-17 21:34:10' WHERE id=111;
+
+// Find out a user, and update the name if it is changed
+db.First(&user, 111).Update("name", "hello")
+//// SELECT * FROM users LIMIT 1;
+//// UPDATE users SET name='hello', updated_at = '2013-11-17 21:34:10' WHERE id=111;
+
+// Update name with search condiation and specified table name
+db.Table("users").Where(10).Update("name", "hello")
+//// UPDATE users SET name='hello' WHERE id = 10;
+
+ +

+Update multiple attributes with Updates +

+ +
// Update user's name and age if they are changed
+db.Model(&user).Updates(User{Name: "hello", Age: 18})
+//// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;
+
+// Updates with Map
+db.Table("users").Where(10).Updates(map[string]interface{}{"name": "hello", "age": 18})
+//// UPDATE users SET name='hello', age=18 WHERE id = 10;
+
+// Updates with Struct
+db.Model(User{}).Updates(User{Name: "hello", Age: 18})
+//// UPDATE users SET name='hello', age=18;
+
+ +

+Update attributes without callbacks

+ +
db.Model(&user).UpdateColumn("name", "hello")
+//// UPDATE users SET name='hello' WHERE id = 111;
+
+db.Model(&user).UpdateColumns(User{Name: "hello", Age: 18})
+//// UPDATE users SET name='hello', age=18 WHERE id = 111;
+
+ +

+Delete

+ +

+Delete an existing struct

+ +
db.Delete(&email)
+// DELETE from emails where id=10;
+
+ +

+Batch Delete with search

+ +
db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
+// DELETE from emails where email LIKE "%jinhu%";
+
+ +

+Soft Delete

+ +

If a struct has a DeletedAt field, it will get soft delete ability automatically!

+ +

Structs that don't have a DeletedAt field will be deleted from the database permanently

+ +
db.Delete(&user)
+//// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;
+
+// Delete with search condition
+db.Where("age = ?", 20).Delete(&User{})
+//// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;
+
+// Soft deleted records will be ignored when searched
+db.Where("age = 20").Find(&user)
+//// SELECT * FROM users WHERE age = 100 AND (deleted_at IS NULL AND deleted_at <= '0001-01-02');
+
+// Find soft deleted records with Unscoped
+db.Unscoped().Where("age = 20").Find(&users)
+//// SELECT * FROM users WHERE age = 20;
+
+// Delete record permanently with Unscoped
+db.Unscoped().Delete(&order)
+// DELETE FROM orders WHERE id=10;
+
+ +

+FirstOrInit

+ +

Try to get the first record, if failed, will initialize the struct with search conditions.

+ +

(only supports search conditions map and struct)

+ +
db.FirstOrInit(&user, User{Name: "non_existing"})
+//// User{Name: "non_existing"}
+
+db.Where(User{Name: "Jinzhu"}).FirstOrInit(&user)
+//// User{Id: 111, Name: "Jinzhu", Age: 20}
+
+db.FirstOrInit(&user, map[string]interface{}{"name": "jinzhu"})
+//// User{Id: 111, Name: "Jinzhu", Age: 20}
+
+ +

+FirstOrInit With Attrs

+ +

Ignore Attrs's arguments when searching, but use them to initialize the struct if no record is found.

+ +
db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrInit(&user)
+//// SELECT * FROM USERS WHERE name = 'non_existing';
+//// User{Name: "non_existing", Age: 20}
+
+// Or write it like this if has only one attribute
+db.Where(User{Name: "noexisting_user"}).Attrs("age", 20).FirstOrInit(&user)
+
+// If a record found, Attrs would be ignored
+db.Where(User{Name: "Jinzhu"}).Attrs(User{Age: 30}).FirstOrInit(&user)
+//// SELECT * FROM USERS WHERE name = jinzhu';
+//// User{Id: 111, Name: "Jinzhu", Age: 20}
+
+ +

+FirstOrInit With Assign

+ +

Ignore Assign's arguments when searching, but use them to fill the struct regardless, whether the record is found or not.

+ +
db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrInit(&user)
+//// User{Name: "non_existing", Age: 20}
+
+db.Where(User{Name: "Jinzhu"}).Assign(User{Age: 30}).FirstOrInit(&user)
+//// User{Id: 111, Name: "Jinzhu", Age: 30}
+
+ +

+FirstOrCreate

+ +

Try to get the first record, if failed, will initialize the struct with the search conditions and insert it in the database.

+ +
db.FirstOrCreate(&user, User{Name: "non_existing"})
+//// User{Id: 112, Name: "non_existing"}
+
+db.Where(User{Name: "Jinzhu"}).FirstOrCreate(&user)
+//// User{Id: 111, Name: "Jinzhu"}
+
+db.FirstOrCreate(&user, map[string]interface{}{"name": "jinzhu", "age": 30})
+//// user -> User{Id: 111, Name: "jinzhu", Age: 20}
+
+ +

+FirstOrCreate With Attrs

+ +

Ignore Attrs's arguments when searching, but use them to initialize the struct if no record is found.

+ +
db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrCreate(&user)
+//// SELECT * FROM users WHERE name = 'non_existing';
+//// User{Id: 112, Name: "non_existing", Age: 20}
+
+db.Where(User{Name: "jinzhu"}).Attrs(User{Age: 30}).FirstOrCreate(&user)
+//// User{Id: 111, Name: "jinzhu", Age: 20}
+
+ +

+FirstOrCreate With Assign

+ +

Ignore Assign's arguments when searching, but use them to fill the struct regardless, whether the record is found or not, then save it back to the database.

+ +
db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrCreate(&user)
+//// user -> User{Id: 112, Name: "non_existing", Age: 20}
+
+db.Where(User{Name: "jinzhu"}).Assign(User{Age: 30}).FirstOrCreate(&user)
+//// SELECT * FROM users WHERE name = 'jinzhu';
+//// UPDATE users SET age=30 WHERE id = 111;
+//// User{Id: 111, Name: "jinzhu", Age: 30}
+
+ +

+Select

+ +
db.Select("name, age").Find(&users)
+//// SELECT name, age FROM users;
+
+ +

+Order

+ +
db.Order("age desc, name").Find(&users)
+//// SELECT * FROM users ORDER BY age desc, name;
+
+// Multiple orders
+db.Order("age desc").Order("name").Find(&users)
+//// SELECT * FROM users ORDER BY age desc, name;
+
+// ReOrder
+db.Order("age desc").Find(&users1).Order("age", true).Find(&users2)
+//// SELECT * FROM users ORDER BY age desc; (users1)
+//// SELECT * FROM users ORDER BY age; (users2)
+
+ +

+Limit

+ +
db.Limit(3).Find(&users)
+//// SELECT * FROM users LIMIT 3;
+
+// Remove limit with -1
+db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
+//// SELECT * FROM users LIMIT 10; (users1)
+//// SELECT * FROM users; (users2)
+
+ +

+Offset

+ +
db.Offset(3).Find(&users)
+//// SELECT * FROM users OFFSET 3;
+
+// Remove offset with -1
+db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
+//// SELECT * FROM users OFFSET 10; (users1)
+//// SELECT * FROM users; (users2)
+
+ +

+Count

+ +
db.Where("name = ?", "jinzhu").Or("name = ?", "jinzhu 2").Find(&users).Count(&count)
+//// SELECT * from USERS WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (users)
+//// SELECT count(*) FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (count)
+
+// Set table name with Model
+db.Model(User{}).Where("name = ?", "jinzhu").Count(&count)
+//// SELECT count(*) FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (count)
+
+// Set table name with Table
+db.Table("deleted_users").Count(&count)
+//// SELECT count(*) FROM deleted_users;
+
+ +

+Pluck

+ +

Get struct's selected attributes as a map

+ +
var ages []int64
+db.Find(&users).Pluck("age", &ages)
+
+// Set Table With Model
+var names []string
+db.Model(&User{}).Pluck("name", &names)
+//// SELECT name FROM users;
+
+// Set Table With Table
+db.Table("deleted_users").Pluck("name", &names)
+//// SELECT name FROM deleted_users;
+
+// Plucking more than one column? Do it like this:
+db.Select("name, age").Find(&users)
+
+ +

+Transactions

+ +

All individual save and delete operations are run in a transaction by default.

+ +
tx := db.Begin()
+
+user := User{Name: "transcation"}
+
+tx.Save(&u)
+tx.Update("age": 90)
+// do whatever
+
+// rollback
+tx.Rollback()
+
+// commit
+tx.Commit()
+
+ +

+Callbacks

+ +

Callbacks are methods defined on the struct's pointer. +If any callback returns an error, gorm will stop future operations and rollback all changes.

+ +

Here is a list of all available callbacks, +listed in the same order in which they will get called during the respective operations.

+ +

+Creating an Object

+ +
BeforeSave
+BeforeCreate
+// save before associations
+// save self
+// save after associations
+AfterCreate
+AfterSave
+
+ +

+Updating an Object

+ +
BeforeSave
+BeforeUpdate
+// save before associations
+// save self
+// save after associations
+AfterUpdate
+AfterSave
+
+ +

+Destroying an Object

+ +
BeforeDelete
+// delete self
+AfterDelete
+
+ +

+After Find

+ +
// load record/records from database
+AfterFind
+
+ +

Here is an example:

+ +
func (u *User) BeforeUpdate() (err error) {
+    if u.readonly() {
+        err = errors.New("Read Only User!")
+    }
+    return
+}
+
+// Rollback the insertion if there are more than 1000 users (hypothetical example)
+func (u *User) AfterCreate() (err error) {
+    if (u.Id > 1000) { // Just as an example, don't use Id to count users!
+        err = errors.New("Only 1000 users allowed!")
+    }
+    return
+}
+
+ +
// As you know, the save/delete operations are running in a transaction
+// This is means that all your changes will be rolled back if there are any errors
+// If you want your changes in callbacks be run in the same transaction
+// you have to pass the transaction as argument to the function
+func (u *User) AfterCreate(tx *gorm.DB) (err error) {
+    tx.Model(u).Update("role", "admin")
+    return
+}
+
+ +

+Specifying the Table Name

+ +
// Create `deleted_users` table with User's fields
+db.Table("deleted_users").CreateTable(&User{})
+
+// Search from table `deleted_users`
+var deleted_users []User
+db.Table("deleted_users").Find(&deleted_users)
+//// SELECT * FROM deleted_users;
+
+// Delete results from table `deleted_users` with search conditions
+db.Table("deleted_users").Where("name = ?", "jinzhu").Delete()
+//// DELETE FROM deleted_users WHERE name = 'jinzhu';
+
+ +

+Specifying the Table Name for Struct permanently with TableName

+ +
type Cart struct {
+}
+
+func (c Cart) TableName() string {
+    return "shopping_cart"
+}
+
+func (u User) TableName() string {
+    if u.Role == "admin" {
+        return "admin_users"
+    } else {
+        return "users"
+    }
+}
+
+ +

+Scopes

+ +
func AmountGreaterThan1000(d *gorm.DB) *gorm.DB {
+  d.Where("amount > ?", 1000)
+}
+
+func PaidWithCreditCard(d *gorm.DB) *gorm.DB {
+  d.Where("pay_mode_sign = ?", "C")
+}
+
+func PaidWithCod(d *gorm.DB) *gorm.DB {
+  d.Where("pay_mode_sign = ?", "C")
+}
+
+func OrderStatus(status []string) func (d *gorm.DB) *gorm.DB {
+  return func (d *gorm.DB) *gorm.DB {
+     return d.Scopes(AmountGreaterThan1000).Where("status in (?)", status)
+  }
+}
+
+db.Scopes(AmountGreaterThan1000, PaidWithCreditCard).Find(&orders)
+// Find all credit card orders and amount greater than 1000
+
+db.Scopes(AmountGreaterThan1000, PaidWithCod).Find(&orders)
+// Find all COD orders and amount greater than 1000
+
+db.Scopes(OrderStatus([]string{"paid", "shipped"})).Find(&orders)
+// Find all paid, shipped orders and amount greater than 1000
+
+ +

+Logger

+ +

Gorm has built-in logger support, enable it with:

+ +
db.LogMode(true)
+
+ +

logger

+ +
// Use your own logger
+// Refer to gorm's default logger for how to format messages: https://github.com/jinzhu/gorm/blob/master/logger.go#files
+db.SetLogger(log.New(os.Stdout, "\r\n", 0))
+
+// If you want to use gorm's default log format, then you could just do it like this
+db.SetLogger(gorm.Logger{revel.TRACE})
+
+// Disable logging
+db.LogMode(false)
+
+// Enable logging for a single operation, to make debugging easy
+db.Debug().Where("name = ?", "jinzhu").First(&User{})
+
+ +

+Row & Rows

+ +

Row & Rows is not chainable, it works just like QueryRow and Query

+ +
row := db.Table("users").Where("name = ?", "jinzhu").select("name, age").Row() // (*sql.Row)
+row.Scan(&name, &age)
+
+rows, err := db.Model(User{}).Where("name = ?", "jinzhu").select("name, age, email").Rows() // (*sql.Rows, error)
+defer rows.Close()
+for rows.Next() {
+  ...
+  rows.Scan(&name, &age, &email)
+  ...
+}
+
+// Rows() with raw sql
+rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows() // (*sql.Rows, error)
+defer rows.Close()
+for rows.Next() {
+  ...
+  rows.Scan(&name, &age, &email)
+  ...
+}
+
+ +

+Scan

+ +

Scan sql results into strcut

+ +
type Result struct {
+    Name string
+    Age  int
+}
+
+var result Result
+db.Table("users").Select("name, age").Where("name = ?", 3).Scan(&result)
+
+// Scan raw sql
+db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)
+
+ +

+Group & Having

+ +
rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
+for rows.Next() {
+  ...
+}
+
+rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Rows()
+for rows.Next() {
+  ...
+}
+
+type Result struct {
+    Date  time.Time
+    Total int64
+}
+db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Scan(&results)
+
+ +

+Joins

+ +
rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
+for rows.Next() {
+  ...
+}
+
+db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)
+
+ +

+Run Raw SQL

+ +
// Raw SQL
+db.Exec("DROP TABLE users;")
+
+// Raw SQL with arguments
+db.Exec("UPDATE orders SET shipped_at=? WHERE id IN (?)", time.Now, []int64{11,22,33})
+
+ +

+Error Handling

+ +
query := db.Where("name = ?", "jinzhu").First(&user)
+query := db.First(&user).Limit(10).Find(&users)
+//// query.Error returns the last error
+//// query.Errors returns all errors that have taken place
+//// If an error has taken place, gorm will stop all following operations
+
+// I often use some code like below to do error handling when writing applications
+if err := db.Where("name = ?", "jinzhu").First(&user).Error; err != nil {
+  // ...
+}
+
+// If no record is found, gorm will return RecordNotFound error, you could check it with
+db.Where("name = ?", "hello world").First(&User{}).Error == gorm.RecordNotFound
+
+// Or use shortcut method
+if db.Where("name = ?", "hello world").First(&user).RecordNotFound() {
+  panic("no record found")
+} else {
+  user.Blalala()
+}
+
+if db.Model(&user).Related(&credit_card).RecordNotFound() {
+  panic("no credit card found")
+}
+
+ +

+Advanced Usage With Query Chaining

+ +

Already excited with what gorm has to offer? Let's see some magic!

+ +
db.First(&first_article).Count(&total_count).Limit(10).Find(&first_page_articles).Offset(10).Find(&second_page_articles)
+//// SELECT * FROM articles LIMIT 1; (first_article)
+//// SELECT count(*) FROM articles; (total_count)
+//// SELECT * FROM articles LIMIT 10; (first_page_articles)
+//// SELECT * FROM articles LIMIT 10 OFFSET 10; (second_page_articles)
+
+
+// Mix where conditions with inline conditions
+db.Where("created_at > ?", "2013-10-10").Find(&cancelled_orders, "state = ?", "cancelled").Find(&shipped_orders, "state = ?", "shipped")
+//// SELECT * FROM orders WHERE created_at > '2013/10/10' AND state = 'cancelled'; (cancelled_orders)
+//// SELECT * FROM orders WHERE created_at > '2013/10/10' AND state = 'shipped'; (shipped_orders)
+
+
+// Use variables to keep query chain
+todays_orders := db.Where("created_at > ?", "2013-10-29")
+cancelled_orders := todays_orders.Where("state = ?", "cancelled")
+shipped_orders := todays_orders.Where("state = ?", "shipped")
+
+
+// Search with shared conditions from different tables
+db.Where("product_name = ?", "fancy_product").Find(&orders).Find(&shopping_carts)
+//// SELECT * FROM orders WHERE product_name = 'fancy_product'; (orders)
+//// SELECT * FROM carts WHERE product_name = 'fancy_product'; (shopping_carts)
+
+
+// Search with shared conditions from different tables with specified table
+db.Where("mail_type = ?", "TEXT").Find(&users1).Table("deleted_users").Find(&users2)
+//// SELECT * FROM users WHERE mail_type = 'TEXT'; (users1)
+//// SELECT * FROM deleted_users WHERE mail_type = 'TEXT'; (users2)
+
+
+// An example on how to use FirstOrCreate
+db.Where("email = ?", "x@example.org").Attrs(User{RegisteredIp: "111.111.111.111"}).FirstOrCreate(&user)
+//// SELECT * FROM users WHERE email = 'x@example.org';
+//// INSERT INTO "users" (email,registered_ip) VALUES ("x@example.org", "111.111.111.111")  // if no record found
+
+ +

+TODO

+ +
    +
  • +

    Support plugin +BeforeQuery +BeforeSave +BeforeCreate +BeforeUpdate +BeforeDelete +AfterQuery +AfterSave +AfterCreate +AfterUpdate +SoftDelete + BeforeQuery + BeforeSave + BeforeDelete

    + +

    db.RegisterPlugin("xxx") +db.RegisterCallback("BeforeQuery", func() {}) +db.RegisterCallback("BeforeSave", func() {}) +db.RegisterFuncation("Search", func() {}) +db.Model(&[]User{}).Limit(10).Do("Search", "vip", "china") +db.Mode(&User{}).Do("EditForm").Get("edit_form_html")

    + +

    DefaultValue, DefaultTimeZone, R/W Splitting, Validation

    +
  • +
  • Getter/Setter +share or not? transaction?
  • +
  • Github Pages
  • +
  • Includes
  • +
  • AlertColumn, DropColumn, AddIndex, RemoveIndex
  • +

+Author

+ +

jinzhu

+ +

+License

+ +

Released under the MIT License.

+ +

GoDoc

+
+ +
+ + + + + + diff --git a/javascripts/scale.fix.js b/javascripts/scale.fix.js new file mode 100644 index 00000000..08716c00 --- /dev/null +++ b/javascripts/scale.fix.js @@ -0,0 +1,20 @@ +fixScale = function(doc) { + + var addEvent = 'addEventListener', + type = 'gesturestart', + qsa = 'querySelectorAll', + scales = [1, 1], + meta = qsa in doc ? doc[qsa]('meta[name=viewport]') : []; + + function fix() { + meta.content = 'width=device-width,minimum-scale=' + scales[0] + ',maximum-scale=' + scales[1]; + doc.removeEventListener(type, fix, true); + } + + if ((meta = meta[meta.length - 1]) && addEvent in doc) { + fix(); + scales = [.25, 1.6]; + doc[addEvent](type, fix, true); + } + +}; \ No newline at end of file diff --git a/params.json b/params.json new file mode 100644 index 00000000..8cdfa1a7 --- /dev/null +++ b/params.json @@ -0,0 +1 @@ +{"name":"Gorm","tagline":"The fantastic ORM library for Golang, aims to be developer friendly.","body":"# GORM\r\n\r\nThe fantastic ORM library for Golang, aims to be developer friendly.\r\n\r\n## Install\r\n\r\n```\r\ngo get github.com/jinzhu/gorm\r\n```\r\n\r\n## Overview\r\n\r\n* Chainable API\r\n* Relations\r\n* Callbacks (before/after create/save/update/delete/find)\r\n* Soft Deletes\r\n* Auto Migrations\r\n* Transactions\r\n* Logger Support\r\n* Bind struct with tag\r\n* Iteration Support via [Rows](#row--rows)\r\n* Scopes\r\n* sql.Scanner support\r\n* Every feature comes with tests\r\n* Convention Over Configuration\r\n* Developer Friendly\r\n\r\n## Conventions\r\n\r\n* Table name is the plural of struct name's snake case.\r\n Disable pluralization with `db.SingularTable(true)`, or [specify your table name](#specify-table-name)\r\n* Column name is the snake case of field's name.\r\n* Use `Id int64` field as primary key.\r\n* Use tag `sql` to change field's property, change the tag name with `db.SetTagIdentifier(new_name)`.\r\n* Use `CreatedAt` to store record's created time if field exists.\r\n* Use `UpdatedAt` to store record's updated time if field exists.\r\n* Use `DeletedAt` to store record's deleted time if field exists. [Soft Delete](#soft-delete)\r\n* Gorm uses reflection to know which tables to work with:\r\n\r\n```go\r\n// E.g Finding an existing User\r\nvar user User\r\n// Gorm will now know to use table \"users\" (\"user\" if pluralisation has been disabled) for all operations.\r\ndb.First(&user)\r\n\r\n// E.g creating a new User\r\nDB.Save(&User{Name: \"xxx\"}) // table \"users\"\r\n```\r\n\r\n# Getting Started\r\n\r\n```go\r\nimport (\r\n \"database/sql\"\r\n \"time\"\r\n)\r\n\r\ntype User struct {\r\n Id int64\r\n Birthday time.Time\r\n Age int64\r\n Name string `sql:\"size:255\"`\r\n CreatedAt time.Time\r\n UpdatedAt time.Time\r\n DeletedAt time.Time\r\n\r\n Emails []Email // Embedded structs\r\n BillingAddress Address // Embedded struct\r\n BillingAddressId sql.NullInt64 // BillingAddress's foreign key\r\n ShippingAddress Address // Another Embedded struct with same type\r\n ShippingAddressId int64 // ShippingAddress's foreign key\r\n IgnoreMe int64 `sql:\"-\"` // Ignore this field\r\n}\r\n\r\ntype Email struct {\r\n Id int64\r\n UserId int64 // Foreign key for User\r\n Email string `sql:\"type:varchar(100);\"` // Set this field's type\r\n Subscribed bool\r\n}\r\n\r\ntype Address struct {\r\n Id int64\r\n Address1 string `sql:\"not null;unique\"` // Set this field as not nullable and unique in database\r\n Address2 string `sql:\"type:varchar(100);unique\"`\r\n Post sql.NullString `sql:not null`\r\n // FYI, \"NOT NULL\" will only work well with NullXXX Scanner, because golang will initalize a default value for most type...\r\n}\r\n```\r\n\r\n## Opening a Database\r\n\r\n```go\r\n\r\nimport \"github.com/jinzhu/gorm\"\r\nimport _ \"github.com/lib/pq\"\r\n// import _ \"github.com/go-sql-driver/mysql\"\r\n// import _ \"github.com/mattn/go-sqlite3\"\r\n\r\ndb, err := Open(\"postgres\", \"user=gorm dbname=gorm sslmode=disable\")\r\n// db, err = Open(\"mysql\", \"gorm:gorm@/gorm?charset=utf8&parseTime=True\")\r\n// db, err = Open(\"sqlite3\", \"/tmp/gorm.db\")\r\n\r\n// Get database connection handle [*sql.DB](http://golang.org/pkg/database/sql/#DB)\r\nd := db.DB()\r\n\r\n// With it you could use package `database/sql`'s builtin methods\r\ndb.DB().SetMaxIdleConns(10)\r\ndb.DB().SetMaxOpenConns(100)\r\ndb.DB().Ping()\r\n\r\n// By default, table name is plural of struct type, you can use struct type as table name with:\r\ndb.SingularTable(true)\r\n```\r\n\r\nGorm is goroutines friendly, so you can create a global variable to keep the connection and use it everywhere in your project.\r\n```go\r\n// db.go\r\npackage db\r\n\r\nimport (\r\n \"fmt\"\r\n \"github.com/jinzhu/gorm\"\r\n _ \"github.com/lib/pq\"\r\n)\r\n\r\nvar DB gorm.DB\r\nfunc init() {\r\n var err error\r\n DB, err = gorm.Open(\"postgres\", \"user=gorm dbname=gorm sslmode=disable\")\r\n\r\n // Connection string parameters for Postgres - http://godoc.org/github.com/lib/pq, if you are using another\r\n // database refer to the relevant driver's documentation.\r\n\r\n // * dbname - The name of the database to connect to\r\n // * user - The user to sign in as\r\n // * password - The user's password\r\n // * host - The host to connect to. Values that start with / are for unix domain sockets.\r\n // (default is localhost)\r\n // * port - The port to bind to. (default is 5432)\r\n // * sslmode - Whether or not to use SSL (default is require, this is not the default for libpq)\r\n // Valid SSL modes:\r\n // * disable - No SSL\r\n // * require - Always SSL (skip verification)\r\n // * verify-full - Always SSL (require verification)\r\n\r\n if err != nil {\r\n panic(fmt.Sprintf(\"Got error when connect database, the error is '%v'\", err))\r\n }\r\n}\r\n\r\n// user.go\r\npackage user\r\nimport . \"db\"\r\n...\r\nDB.Save(&User{Name: \"xxx\"})\r\n...\r\n```\r\n\r\n## Struct & Database Mapping\r\n\r\n```go\r\n// Create table from struct\r\ndb.CreateTable(User{})\r\n\r\n// Drop table\r\ndb.DropTable(User{})\r\n```\r\n\r\n### Automating Migrations\r\n\r\nFeel free to update your struct, AutoMigrate will keep your database update to date.\r\n\r\nFYI, AutoMigrate will only add new columns, it won't change the current columns' types or delete unused columns, to make sure your data is safe.\r\n\r\nIf the table doesn't exist when AutoMigrate, gorm will run create the table automatically.\r\n(the database first needs to be created manually though...).\r\n\r\n(only postgres and mysql supported)\r\n\r\n```go\r\ndb.AutoMigrate(User{})\r\n```\r\n\r\n# Gorm API\r\n\r\n## Create\r\n\r\n```go\r\nuser := User{Name: \"jinzhu\", Age: 18, Birthday: time.Now()}\r\ndb.Save(&user)\r\n```\r\n\r\n### NewRecord\r\n\r\nReturns true if object hasn’t been saved yet (`Id` is blank)\r\n\r\n```go\r\nuser := User{Name: \"jinzhu\", Age: 18, Birthday: time.Now()}\r\ndb.NewRecord(user) // => true\r\n\r\ndb.Save(&user)\r\ndb.NewRecord(user) // => false\r\n```\r\n\r\n### Create With SubStruct\r\n\r\nRefer to [Query With Related](#query-with-related) for how to find associations\r\n\r\n```go\r\nuser := User{\r\n Name: \"jinzhu\",\r\n BillingAddress: Address{Address1: \"Billing Address - Address 1\"},\r\n ShippingAddress: Address{Address1: \"Shipping Address - Address 1\"},\r\n Emails: []Email{{Email: \"jinzhu@example.com\"}, {Email: \"jinzhu-2@example@example.com\"}},\r\n}\r\n\r\ndb.Save(&user)\r\n//// BEGIN TRANSACTION;\r\n//// INSERT INTO \"addresses\" (address1) VALUES (\"Billing Address - Address 1\");\r\n//// INSERT INTO \"addresses\" (address1) VALUES (\"Shipping Address - Address 1\");\r\n//// INSERT INTO \"users\" (name,billing_address_id,shipping_address_id) VALUES (\"jinzhu\", 1, 2);\r\n//// INSERT INTO \"emails\" (user_id,email) VALUES (111, \"jinzhu@example.com\");\r\n//// INSERT INTO \"emails\" (user_id,email) VALUES (111, \"jinzhu-2@example.com\");\r\n//// COMMIT;\r\n```\r\n\r\n## Query\r\n\r\n```go\r\n// Get the first record\r\ndb.First(&user)\r\n//// SELECT * FROM users ORDER BY id LIMIT 1;\r\n// Search table `users` is guessed from struct's type\r\n\r\n// Get the last record\r\ndb.Last(&user)\r\n//// SELECT * FROM users ORDER BY id DESC LIMIT 1;\r\n\r\n// Get All records\r\ndb.Find(&users)\r\n//// SELECT * FROM users;\r\n\r\n// Get record with primary key\r\ndb.First(&user, 10)\r\n//// SELECT * FROM users WHERE id = 10;\r\n```\r\n\r\n### Query With Where (SQL)\r\n\r\n```go\r\n// Get the first matched record\r\ndb.Where(\"name = ?\", \"jinzhu\").First(&user)\r\n//// SELECT * FROM users WHERE name = 'jinzhu' limit 1;\r\n\r\n// Get all matched records\r\ndb.Where(\"name = ?\", \"jinzhu\").Find(&users)\r\n//// SELECT * FROM users WHERE name = 'jinzhu';\r\n\r\ndb.Where(\"name <> ?\", \"jinzhu\").Find(&users)\r\n//// SELECT * FROM users WHERE name <> 'jinzhu';\r\n\r\n// IN\r\ndb.Where(\"name in (?)\", []string{\"jinzhu\", \"jinzhu 2\"}).Find(&users)\r\n//// SELECT * FROM users WHERE name IN ('jinzhu', 'jinzhu 2');\r\n\r\n// LIKE\r\ndb.Where(\"name LIKE ?\", \"%jin%\").Find(&users)\r\n//// SELECT * FROM users WHERE name LIKE \"%jin%\";\r\n\r\n// Multiple Conditions\r\ndb.Where(\"name = ? and age >= ?\", \"jinzhu\", \"22\").Find(&users)\r\n//// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;\r\n```\r\n\r\n### Query With Where (Struct & Map)\r\n\r\n```go\r\n// Search with struct\r\ndb.Where(&User{Name: \"jinzhu\", Age: 20}).First(&user)\r\n//// SELECT * FROM users WHERE name = \"jinzhu\" AND age = 20 LIMIT 1;\r\n\r\n// Search with map\r\ndb.Where(map[string]interface{}{\"name\": \"jinzhu\", \"age\": 20}).Find(&users)\r\n//// SELECT * FROM users WHERE name = \"jinzhu\" AND age = 20;\r\n\r\n// IN for primary Keys\r\ndb.Where([]int64{20, 21, 22}).Find(&users)\r\n//// SELECT * FROM users WHERE id IN (20, 21, 22);\r\n```\r\n\r\n### Query With Not\r\n\r\n```go\r\n// Attribute Not Equal\r\ndb.Not(\"name\", \"jinzhu\").First(&user)\r\n//// SELECT * FROM users WHERE name <> \"jinzhu\" LIMIT 1;\r\n\r\n// Not In\r\ndb.Not(\"name\", []string{\"jinzhu\", \"jinzhu 2\"}).Find(&users)\r\n//// SELECT * FROM users WHERE name NOT IN (\"jinzhu\", \"jinzhu 2\");\r\n\r\n// Not In for primary keys\r\ndb.Not([]int64{1,2,3}).First(&user)\r\n//// SELECT * FROM users WHERE id NOT IN (1,2,3);\r\n\r\ndb.Not([]int64{}).First(&user)\r\n//// SELECT * FROM users;\r\n\r\n// SQL string\r\ndb.Not(\"name = ?\", \"jinzhu\").First(&user)\r\n//// SELECT * FROM users WHERE NOT(name = \"jinzhu\");\r\n\r\n// Not with struct\r\ndb.Not(User{Name: \"jinzhu\"}).First(&user)\r\n//// SELECT * FROM users WHERE name <> \"jinzhu\";\r\n```\r\n\r\n### Query With Inline Condition\r\n\r\n```go\r\n// Find with primary key\r\ndb.First(&user, 23)\r\n//// SELECT * FROM users WHERE id = 23 LIMIT 1;\r\n\r\n// SQL string\r\ndb.Find(&user, \"name = ?\", \"jinzhu\")\r\n//// SELECT * FROM users WHERE name = \"jinzhu\";\r\n\r\n// Multiple Conditions\r\ndb.Find(&users, \"name <> ? and age > ?\", \"jinzhu\", 20)\r\n//// SELECT * FROM users WHERE name <> \"jinzhu\" AND age > 20;\r\n\r\n// Inline search with struct\r\ndb.Find(&users, User{Age: 20})\r\n//// SELECT * FROM users WHERE age = 20;\r\n\r\n// Inline search with map\r\ndb.Find(&users, map[string]interface{}{\"age\": 20})\r\n//// SELECT * FROM users WHERE age = 20;\r\n```\r\n\r\n### Query With Or\r\n\r\n```go\r\ndb.Where(\"role = ?\", \"admin\").Or(\"role = ?\", \"super_admin\").Find(&users)\r\n//// SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';\r\n\r\n// Or With Struct\r\ndb.Where(\"name = 'jinzhu'\").Or(User{Name: \"jinzhu 2\"}).Find(&users)\r\n//// SELECT * FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2';\r\n\r\n// Or With Map\r\ndb.Where(\"name = 'jinzhu'\").Or(map[string]interface{}{\"name\": \"jinzhu 2\"}).Find(&users)\r\n```\r\n\r\n### Query With Related\r\n\r\n```go\r\n// Find user's emails with guessed foreign key\r\ndb.Model(&user).Related(&emails)\r\n//// SELECT * FROM emails WHERE user_id = 111;\r\n\r\n// Find user's billing address with specified foreign key 'BillingAddressId'\r\ndb.Model(&user).Related(&address1, \"BillingAddressId\")\r\n//// SELECT * FROM addresses WHERE id = 123; // 123 is user's BillingAddressId\r\n\r\n// Find user with guessed primary key value from email\r\ndb.Model(&email).Related(&user)\r\n//// SELECT * FROM users WHERE id = 111; // 111 is email's UserId\r\n```\r\n\r\n### Query Chains\r\n\r\nGorm has a chainable API, so you could query like this\r\n\r\n```go\r\ndb.Where(\"name <> ?\",\"jinzhu\").Where(\"age >= ? and role <> ?\",20,\"admin\").Find(&users)\r\n//// SELECT * FROM users WHERE name <> 'jinzhu' AND age >= 20 AND role <> 'admin';\r\n\r\ndb.Where(\"role = ?\", \"admin\").Or(\"role = ?\", \"super_admin\").Not(\"name = ?\", \"jinzhu\").Find(&users)\r\n```\r\n\r\n## Update\r\n\r\n### Update an existing struct\r\n\r\n```go\r\nuser.Name = \"jinzhu 2\"\r\nuser.Age = 100\r\ndb.Save(&user)\r\n//// UPDATE users SET name='jinzhu 2', age=100, updated_at = '2013-11-17 21:34:10' WHERE id=111;\r\n```\r\n\r\n### Update one attribute with `Update`\r\n\r\n```go\r\n// Update existing user's name if it is changed\r\ndb.Model(&user).Update(\"name\", \"hello\")\r\n//// UPDATE users SET name='hello', updated_at = '2013-11-17 21:34:10' WHERE id=111;\r\n\r\n// Find out a user, and update the name if it is changed\r\ndb.First(&user, 111).Update(\"name\", \"hello\")\r\n//// SELECT * FROM users LIMIT 1;\r\n//// UPDATE users SET name='hello', updated_at = '2013-11-17 21:34:10' WHERE id=111;\r\n\r\n// Update name with search condiation and specified table name\r\ndb.Table(\"users\").Where(10).Update(\"name\", \"hello\")\r\n//// UPDATE users SET name='hello' WHERE id = 10;\r\n```\r\n\r\n### Update multiple attributes with `Updates`\r\n\r\n```go\r\n// Update user's name and age if they are changed\r\ndb.Model(&user).Updates(User{Name: \"hello\", Age: 18})\r\n//// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;\r\n\r\n// Updates with Map\r\ndb.Table(\"users\").Where(10).Updates(map[string]interface{}{\"name\": \"hello\", \"age\": 18})\r\n//// UPDATE users SET name='hello', age=18 WHERE id = 10;\r\n\r\n// Updates with Struct\r\ndb.Model(User{}).Updates(User{Name: \"hello\", Age: 18})\r\n//// UPDATE users SET name='hello', age=18;\r\n```\r\n\r\n### Update attributes without callbacks\r\n\r\n```go\r\ndb.Model(&user).UpdateColumn(\"name\", \"hello\")\r\n//// UPDATE users SET name='hello' WHERE id = 111;\r\n\r\ndb.Model(&user).UpdateColumns(User{Name: \"hello\", Age: 18})\r\n//// UPDATE users SET name='hello', age=18 WHERE id = 111;\r\n```\r\n\r\n## Delete\r\n\r\n### Delete an existing struct\r\n\r\n```go\r\ndb.Delete(&email)\r\n// DELETE from emails where id=10;\r\n```\r\n\r\n### Batch Delete with search\r\n\r\n```go\r\ndb.Where(\"email LIKE ?\", \"%jinzhu%\").Delete(Email{})\r\n// DELETE from emails where email LIKE \"%jinhu%\";\r\n```\r\n\r\n### Soft Delete\r\n\r\nIf a struct has a DeletedAt field, it will get soft delete ability automatically!\r\n\r\nStructs that don't have a DeletedAt field will be deleted from the database permanently\r\n\r\n```go\r\ndb.Delete(&user)\r\n//// UPDATE users SET deleted_at=\"2013-10-29 10:23\" WHERE id = 111;\r\n\r\n// Delete with search condition\r\ndb.Where(\"age = ?\", 20).Delete(&User{})\r\n//// UPDATE users SET deleted_at=\"2013-10-29 10:23\" WHERE age = 20;\r\n\r\n// Soft deleted records will be ignored when searched\r\ndb.Where(\"age = 20\").Find(&user)\r\n//// SELECT * FROM users WHERE age = 100 AND (deleted_at IS NULL AND deleted_at <= '0001-01-02');\r\n\r\n// Find soft deleted records with Unscoped\r\ndb.Unscoped().Where(\"age = 20\").Find(&users)\r\n//// SELECT * FROM users WHERE age = 20;\r\n\r\n// Delete record permanently with Unscoped\r\ndb.Unscoped().Delete(&order)\r\n// DELETE FROM orders WHERE id=10;\r\n```\r\n\r\n## FirstOrInit\r\n\r\nTry to get the first record, if failed, will initialize the struct with search conditions.\r\n\r\n(only supports search conditions map and struct)\r\n\r\n```go\r\ndb.FirstOrInit(&user, User{Name: \"non_existing\"})\r\n//// User{Name: \"non_existing\"}\r\n\r\ndb.Where(User{Name: \"Jinzhu\"}).FirstOrInit(&user)\r\n//// User{Id: 111, Name: \"Jinzhu\", Age: 20}\r\n\r\ndb.FirstOrInit(&user, map[string]interface{}{\"name\": \"jinzhu\"})\r\n//// User{Id: 111, Name: \"Jinzhu\", Age: 20}\r\n```\r\n\r\n### FirstOrInit With Attrs\r\n\r\nIgnore Attrs's arguments when searching, but use them to initialize the struct if no record is found.\r\n\r\n```go\r\ndb.Where(User{Name: \"non_existing\"}).Attrs(User{Age: 20}).FirstOrInit(&user)\r\n//// SELECT * FROM USERS WHERE name = 'non_existing';\r\n//// User{Name: \"non_existing\", Age: 20}\r\n\r\n// Or write it like this if has only one attribute\r\ndb.Where(User{Name: \"noexisting_user\"}).Attrs(\"age\", 20).FirstOrInit(&user)\r\n\r\n// If a record found, Attrs would be ignored\r\ndb.Where(User{Name: \"Jinzhu\"}).Attrs(User{Age: 30}).FirstOrInit(&user)\r\n//// SELECT * FROM USERS WHERE name = jinzhu';\r\n//// User{Id: 111, Name: \"Jinzhu\", Age: 20}\r\n```\r\n\r\n### FirstOrInit With Assign\r\n\r\nIgnore Assign's arguments when searching, but use them to fill the struct regardless, whether the record is found or not.\r\n\r\n```go\r\ndb.Where(User{Name: \"non_existing\"}).Assign(User{Age: 20}).FirstOrInit(&user)\r\n//// User{Name: \"non_existing\", Age: 20}\r\n\r\ndb.Where(User{Name: \"Jinzhu\"}).Assign(User{Age: 30}).FirstOrInit(&user)\r\n//// User{Id: 111, Name: \"Jinzhu\", Age: 30}\r\n```\r\n\r\n## FirstOrCreate\r\n\r\nTry to get the first record, if failed, will initialize the struct with the search conditions and insert it in the database.\r\n\r\n```go\r\ndb.FirstOrCreate(&user, User{Name: \"non_existing\"})\r\n//// User{Id: 112, Name: \"non_existing\"}\r\n\r\ndb.Where(User{Name: \"Jinzhu\"}).FirstOrCreate(&user)\r\n//// User{Id: 111, Name: \"Jinzhu\"}\r\n\r\ndb.FirstOrCreate(&user, map[string]interface{}{\"name\": \"jinzhu\", \"age\": 30})\r\n//// user -> User{Id: 111, Name: \"jinzhu\", Age: 20}\r\n```\r\n\r\n### FirstOrCreate With Attrs\r\n\r\nIgnore Attrs's arguments when searching, but use them to initialize the struct if no record is found.\r\n\r\n```go\r\ndb.Where(User{Name: \"non_existing\"}).Attrs(User{Age: 20}).FirstOrCreate(&user)\r\n//// SELECT * FROM users WHERE name = 'non_existing';\r\n//// User{Id: 112, Name: \"non_existing\", Age: 20}\r\n\r\ndb.Where(User{Name: \"jinzhu\"}).Attrs(User{Age: 30}).FirstOrCreate(&user)\r\n//// User{Id: 111, Name: \"jinzhu\", Age: 20}\r\n```\r\n\r\n### FirstOrCreate With Assign\r\n\r\nIgnore Assign's arguments when searching, but use them to fill the struct regardless, whether the record is found or not, then save it back to the database.\r\n\r\n```go\r\ndb.Where(User{Name: \"non_existing\"}).Assign(User{Age: 20}).FirstOrCreate(&user)\r\n//// user -> User{Id: 112, Name: \"non_existing\", Age: 20}\r\n\r\ndb.Where(User{Name: \"jinzhu\"}).Assign(User{Age: 30}).FirstOrCreate(&user)\r\n//// SELECT * FROM users WHERE name = 'jinzhu';\r\n//// UPDATE users SET age=30 WHERE id = 111;\r\n//// User{Id: 111, Name: \"jinzhu\", Age: 30}\r\n```\r\n\r\n## Select\r\n\r\n```go\r\ndb.Select(\"name, age\").Find(&users)\r\n//// SELECT name, age FROM users;\r\n```\r\n\r\n## Order\r\n\r\n```go\r\ndb.Order(\"age desc, name\").Find(&users)\r\n//// SELECT * FROM users ORDER BY age desc, name;\r\n\r\n// Multiple orders\r\ndb.Order(\"age desc\").Order(\"name\").Find(&users)\r\n//// SELECT * FROM users ORDER BY age desc, name;\r\n\r\n// ReOrder\r\ndb.Order(\"age desc\").Find(&users1).Order(\"age\", true).Find(&users2)\r\n//// SELECT * FROM users ORDER BY age desc; (users1)\r\n//// SELECT * FROM users ORDER BY age; (users2)\r\n```\r\n\r\n## Limit\r\n\r\n```go\r\ndb.Limit(3).Find(&users)\r\n//// SELECT * FROM users LIMIT 3;\r\n\r\n// Remove limit with -1\r\ndb.Limit(10).Find(&users1).Limit(-1).Find(&users2)\r\n//// SELECT * FROM users LIMIT 10; (users1)\r\n//// SELECT * FROM users; (users2)\r\n```\r\n\r\n## Offset\r\n\r\n```go\r\ndb.Offset(3).Find(&users)\r\n//// SELECT * FROM users OFFSET 3;\r\n\r\n// Remove offset with -1\r\ndb.Offset(10).Find(&users1).Offset(-1).Find(&users2)\r\n//// SELECT * FROM users OFFSET 10; (users1)\r\n//// SELECT * FROM users; (users2)\r\n```\r\n\r\n## Count\r\n\r\n```go\r\ndb.Where(\"name = ?\", \"jinzhu\").Or(\"name = ?\", \"jinzhu 2\").Find(&users).Count(&count)\r\n//// SELECT * from USERS WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (users)\r\n//// SELECT count(*) FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (count)\r\n\r\n// Set table name with Model\r\ndb.Model(User{}).Where(\"name = ?\", \"jinzhu\").Count(&count)\r\n//// SELECT count(*) FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (count)\r\n\r\n// Set table name with Table\r\ndb.Table(\"deleted_users\").Count(&count)\r\n//// SELECT count(*) FROM deleted_users;\r\n```\r\n\r\n## Pluck\r\n\r\nGet struct's selected attributes as a map\r\n\r\n```go\r\nvar ages []int64\r\ndb.Find(&users).Pluck(\"age\", &ages)\r\n\r\n// Set Table With Model\r\nvar names []string\r\ndb.Model(&User{}).Pluck(\"name\", &names)\r\n//// SELECT name FROM users;\r\n\r\n// Set Table With Table\r\ndb.Table(\"deleted_users\").Pluck(\"name\", &names)\r\n//// SELECT name FROM deleted_users;\r\n\r\n// Plucking more than one column? Do it like this:\r\ndb.Select(\"name, age\").Find(&users)\r\n```\r\n\r\n## Transactions\r\nAll individual save and delete operations are run in a transaction by default.\r\n\r\n```go\r\ntx := db.Begin()\r\n\r\nuser := User{Name: \"transcation\"}\r\n\r\ntx.Save(&u)\r\ntx.Update(\"age\": 90)\r\n// do whatever\r\n\r\n// rollback\r\ntx.Rollback()\r\n\r\n// commit\r\ntx.Commit()\r\n```\r\n\r\n## Callbacks\r\n\r\nCallbacks are methods defined on the struct's pointer.\r\nIf any callback returns an error, gorm will stop future operations and rollback all changes.\r\n\r\nHere is a list of all available callbacks,\r\nlisted in the same order in which they will get called during the respective operations.\r\n\r\n### Creating an Object\r\n\r\n```go\r\nBeforeSave\r\nBeforeCreate\r\n// save before associations\r\n// save self\r\n// save after associations\r\nAfterCreate\r\nAfterSave\r\n```\r\n### Updating an Object\r\n\r\n```go\r\nBeforeSave\r\nBeforeUpdate\r\n// save before associations\r\n// save self\r\n// save after associations\r\nAfterUpdate\r\nAfterSave\r\n```\r\n\r\n### Destroying an Object\r\n\r\n```go\r\nBeforeDelete\r\n// delete self\r\nAfterDelete\r\n```\r\n\r\n### After Find\r\n\r\n```go\r\n// load record/records from database\r\nAfterFind\r\n```\r\n\r\nHere is an example:\r\n\r\n```go\r\nfunc (u *User) BeforeUpdate() (err error) {\r\n if u.readonly() {\r\n err = errors.New(\"Read Only User!\")\r\n }\r\n return\r\n}\r\n\r\n// Rollback the insertion if there are more than 1000 users (hypothetical example)\r\nfunc (u *User) AfterCreate() (err error) {\r\n if (u.Id > 1000) { // Just as an example, don't use Id to count users!\r\n err = errors.New(\"Only 1000 users allowed!\")\r\n }\r\n return\r\n}\r\n```\r\n\r\n```go\r\n// As you know, the save/delete operations are running in a transaction\r\n// This is means that all your changes will be rolled back if there are any errors\r\n// If you want your changes in callbacks be run in the same transaction\r\n// you have to pass the transaction as argument to the function\r\nfunc (u *User) AfterCreate(tx *gorm.DB) (err error) {\r\n tx.Model(u).Update(\"role\", \"admin\")\r\n return\r\n}\r\n```\r\n\r\n## Specifying the Table Name\r\n\r\n```go\r\n// Create `deleted_users` table with User's fields\r\ndb.Table(\"deleted_users\").CreateTable(&User{})\r\n\r\n// Search from table `deleted_users`\r\nvar deleted_users []User\r\ndb.Table(\"deleted_users\").Find(&deleted_users)\r\n//// SELECT * FROM deleted_users;\r\n\r\n// Delete results from table `deleted_users` with search conditions\r\ndb.Table(\"deleted_users\").Where(\"name = ?\", \"jinzhu\").Delete()\r\n//// DELETE FROM deleted_users WHERE name = 'jinzhu';\r\n```\r\n\r\n### Specifying the Table Name for Struct permanently with TableName\r\n\r\n```go\r\ntype Cart struct {\r\n}\r\n\r\nfunc (c Cart) TableName() string {\r\n return \"shopping_cart\"\r\n}\r\n\r\nfunc (u User) TableName() string {\r\n if u.Role == \"admin\" {\r\n return \"admin_users\"\r\n } else {\r\n return \"users\"\r\n }\r\n}\r\n```\r\n\r\n## Scopes\r\n\r\n```go\r\nfunc AmountGreaterThan1000(d *gorm.DB) *gorm.DB {\r\n d.Where(\"amount > ?\", 1000)\r\n}\r\n\r\nfunc PaidWithCreditCard(d *gorm.DB) *gorm.DB {\r\n d.Where(\"pay_mode_sign = ?\", \"C\")\r\n}\r\n\r\nfunc PaidWithCod(d *gorm.DB) *gorm.DB {\r\n d.Where(\"pay_mode_sign = ?\", \"C\")\r\n}\r\n\r\nfunc OrderStatus(status []string) func (d *gorm.DB) *gorm.DB {\r\n return func (d *gorm.DB) *gorm.DB {\r\n return d.Scopes(AmountGreaterThan1000).Where(\"status in (?)\", status)\r\n }\r\n}\r\n\r\ndb.Scopes(AmountGreaterThan1000, PaidWithCreditCard).Find(&orders)\r\n// Find all credit card orders and amount greater than 1000\r\n\r\ndb.Scopes(AmountGreaterThan1000, PaidWithCod).Find(&orders)\r\n// Find all COD orders and amount greater than 1000\r\n\r\ndb.Scopes(OrderStatus([]string{\"paid\", \"shipped\"})).Find(&orders)\r\n// Find all paid, shipped orders and amount greater than 1000\r\n```\r\n\r\n## Logger\r\n\r\nGorm has built-in logger support, enable it with:\r\n\r\n```go\r\ndb.LogMode(true)\r\n```\r\n\r\n![logger](https://raw.github.com/jinzhu/gorm/master/images/logger.png)\r\n\r\n```go\r\n// Use your own logger\r\n// Refer to gorm's default logger for how to format messages: https://github.com/jinzhu/gorm/blob/master/logger.go#files\r\ndb.SetLogger(log.New(os.Stdout, \"\\r\\n\", 0))\r\n\r\n// If you want to use gorm's default log format, then you could just do it like this\r\ndb.SetLogger(gorm.Logger{revel.TRACE})\r\n\r\n// Disable logging\r\ndb.LogMode(false)\r\n\r\n// Enable logging for a single operation, to make debugging easy\r\ndb.Debug().Where(\"name = ?\", \"jinzhu\").First(&User{})\r\n```\r\n\r\n## Row & Rows\r\n\r\nRow & Rows is not chainable, it works just like `QueryRow` and `Query`\r\n\r\n```go\r\nrow := db.Table(\"users\").Where(\"name = ?\", \"jinzhu\").select(\"name, age\").Row() // (*sql.Row)\r\nrow.Scan(&name, &age)\r\n\r\nrows, err := db.Model(User{}).Where(\"name = ?\", \"jinzhu\").select(\"name, age, email\").Rows() // (*sql.Rows, error)\r\ndefer rows.Close()\r\nfor rows.Next() {\r\n ...\r\n rows.Scan(&name, &age, &email)\r\n ...\r\n}\r\n\r\n// Rows() with raw sql\r\nrows, err := db.Raw(\"select name, age, email from users where name = ?\", \"jinzhu\").Rows() // (*sql.Rows, error)\r\ndefer rows.Close()\r\nfor rows.Next() {\r\n ...\r\n rows.Scan(&name, &age, &email)\r\n ...\r\n}\r\n```\r\n\r\n## Scan\r\n\r\nScan sql results into strcut\r\n\r\n```go\r\ntype Result struct {\r\n\tName string\r\n\tAge int\r\n}\r\n\r\nvar result Result\r\ndb.Table(\"users\").Select(\"name, age\").Where(\"name = ?\", 3).Scan(&result)\r\n\r\n// Scan raw sql\r\ndb.Raw(\"SELECT name, age FROM users WHERE name = ?\", 3).Scan(&result)\r\n```\r\n\r\n## Group & Having\r\n\r\n```go\r\nrows, err := db.Table(\"orders\").Select(\"date(created_at) as date, sum(amount) as total\").Group(\"date(created_at)\").Rows()\r\nfor rows.Next() {\r\n ...\r\n}\r\n\r\nrows, err := db.Table(\"orders\").Select(\"date(created_at) as date, sum(amount) as total\").Group(\"date(created_at)\").Having(\"sum(amount) > ?\", 100).Rows()\r\nfor rows.Next() {\r\n ...\r\n}\r\n\r\ntype Result struct {\r\n\tDate time.Time\r\n\tTotal int64\r\n}\r\ndb.Table(\"orders\").Select(\"date(created_at) as date, sum(amount) as total\").Group(\"date(created_at)\").Having(\"sum(amount) > ?\", 100).Scan(&results)\r\n```\r\n\r\n## Joins\r\n\r\n```go\r\nrows, err := db.Table(\"users\").Select(\"users.name, emails.email\").Joins(\"left join emails on emails.user_id = users.id\").Rows()\r\nfor rows.Next() {\r\n ...\r\n}\r\n\r\ndb.Table(\"users\").Select(\"users.name, emails.email\").Joins(\"left join emails on emails.user_id = users.id\").Scan(&results)\r\n```\r\n\r\n## Run Raw SQL\r\n\r\n```go\r\n// Raw SQL\r\ndb.Exec(\"DROP TABLE users;\")\r\n\r\n// Raw SQL with arguments\r\ndb.Exec(\"UPDATE orders SET shipped_at=? WHERE id IN (?)\", time.Now, []int64{11,22,33})\r\n```\r\n\r\n## Error Handling\r\n\r\n```go\r\nquery := db.Where(\"name = ?\", \"jinzhu\").First(&user)\r\nquery := db.First(&user).Limit(10).Find(&users)\r\n//// query.Error returns the last error\r\n//// query.Errors returns all errors that have taken place\r\n//// If an error has taken place, gorm will stop all following operations\r\n\r\n// I often use some code like below to do error handling when writing applications\r\nif err := db.Where(\"name = ?\", \"jinzhu\").First(&user).Error; err != nil {\r\n // ...\r\n}\r\n\r\n// If no record is found, gorm will return RecordNotFound error, you could check it with\r\ndb.Where(\"name = ?\", \"hello world\").First(&User{}).Error == gorm.RecordNotFound\r\n\r\n// Or use shortcut method\r\nif db.Where(\"name = ?\", \"hello world\").First(&user).RecordNotFound() {\r\n panic(\"no record found\")\r\n} else {\r\n user.Blalala()\r\n}\r\n\r\nif db.Model(&user).Related(&credit_card).RecordNotFound() {\r\n panic(\"no credit card found\")\r\n}\r\n```\r\n\r\n## Advanced Usage With Query Chaining\r\n\r\nAlready excited with what gorm has to offer? Let's see some magic!\r\n\r\n```go\r\ndb.First(&first_article).Count(&total_count).Limit(10).Find(&first_page_articles).Offset(10).Find(&second_page_articles)\r\n//// SELECT * FROM articles LIMIT 1; (first_article)\r\n//// SELECT count(*) FROM articles; (total_count)\r\n//// SELECT * FROM articles LIMIT 10; (first_page_articles)\r\n//// SELECT * FROM articles LIMIT 10 OFFSET 10; (second_page_articles)\r\n\r\n\r\n// Mix where conditions with inline conditions\r\ndb.Where(\"created_at > ?\", \"2013-10-10\").Find(&cancelled_orders, \"state = ?\", \"cancelled\").Find(&shipped_orders, \"state = ?\", \"shipped\")\r\n//// SELECT * FROM orders WHERE created_at > '2013/10/10' AND state = 'cancelled'; (cancelled_orders)\r\n//// SELECT * FROM orders WHERE created_at > '2013/10/10' AND state = 'shipped'; (shipped_orders)\r\n\r\n\r\n// Use variables to keep query chain\r\ntodays_orders := db.Where(\"created_at > ?\", \"2013-10-29\")\r\ncancelled_orders := todays_orders.Where(\"state = ?\", \"cancelled\")\r\nshipped_orders := todays_orders.Where(\"state = ?\", \"shipped\")\r\n\r\n\r\n// Search with shared conditions from different tables\r\ndb.Where(\"product_name = ?\", \"fancy_product\").Find(&orders).Find(&shopping_carts)\r\n//// SELECT * FROM orders WHERE product_name = 'fancy_product'; (orders)\r\n//// SELECT * FROM carts WHERE product_name = 'fancy_product'; (shopping_carts)\r\n\r\n\r\n// Search with shared conditions from different tables with specified table\r\ndb.Where(\"mail_type = ?\", \"TEXT\").Find(&users1).Table(\"deleted_users\").Find(&users2)\r\n//// SELECT * FROM users WHERE mail_type = 'TEXT'; (users1)\r\n//// SELECT * FROM deleted_users WHERE mail_type = 'TEXT'; (users2)\r\n\r\n\r\n// An example on how to use FirstOrCreate\r\ndb.Where(\"email = ?\", \"x@example.org\").Attrs(User{RegisteredIp: \"111.111.111.111\"}).FirstOrCreate(&user)\r\n//// SELECT * FROM users WHERE email = 'x@example.org';\r\n//// INSERT INTO \"users\" (email,registered_ip) VALUES (\"x@example.org\", \"111.111.111.111\") // if no record found\r\n```\r\n\r\n## TODO\r\n* Support plugin\r\n BeforeQuery\r\n BeforeSave\r\n BeforeCreate\r\n BeforeUpdate\r\n BeforeDelete\r\n AfterQuery\r\n AfterSave\r\n AfterCreate\r\n AfterUpdate\r\n SoftDelete\r\n BeforeQuery\r\n BeforeSave\r\n BeforeDelete\r\n\r\n db.RegisterPlugin(\"xxx\")\r\n db.RegisterCallback(\"BeforeQuery\", func() {})\r\n db.RegisterCallback(\"BeforeSave\", func() {})\r\n db.RegisterFuncation(\"Search\", func() {})\r\n db.Model(&[]User{}).Limit(10).Do(\"Search\", \"vip\", \"china\")\r\n db.Mode(&User{}).Do(\"EditForm\").Get(\"edit_form_html\")\r\n\r\n DefaultValue, DefaultTimeZone, R/W Splitting, Validation\r\n* Getter/Setter\r\n share or not? transaction?\r\n* Github Pages\r\n* Includes\r\n* AlertColumn, DropColumn, AddIndex, RemoveIndex\r\n\r\n# Author\r\n\r\n**jinzhu**\r\n\r\n* \r\n* \r\n* \r\n\r\n## License\r\n\r\nReleased under the [MIT License](http://www.opensource.org/licenses/MIT).\r\n\r\n[![GoDoc](https://godoc.org/github.com/jinzhu/gorm?status.png)](http://godoc.org/github.com/jinzhu/gorm)\r\n","google":"UA-23459165-4","note":"Don't delete this file! It's used internally to help with page regeneration."} \ No newline at end of file diff --git a/stylesheets/pygment_trac.css b/stylesheets/pygment_trac.css new file mode 100644 index 00000000..c6a6452d --- /dev/null +++ b/stylesheets/pygment_trac.css @@ -0,0 +1,69 @@ +.highlight { background: #ffffff; } +.highlight .c { color: #999988; font-style: italic } /* Comment */ +.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +.highlight .k { font-weight: bold } /* Keyword */ +.highlight .o { font-weight: bold } /* Operator */ +.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ +.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ +.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #aa0000 } /* Generic.Error */ +.highlight .gh { color: #999999 } /* Generic.Heading */ +.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ +.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ +.highlight .go { color: #888888 } /* Generic.Output */ +.highlight .gp { color: #555555 } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold; } /* Generic.Subheading */ +.highlight .gt { color: #aa0000 } /* Generic.Traceback */ +.highlight .kc { font-weight: bold } /* Keyword.Constant */ +.highlight .kd { font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ +.highlight .m { color: #009999 } /* Literal.Number */ +.highlight .s { color: #d14 } /* Literal.String */ +.highlight .na { color: #008080 } /* Name.Attribute */ +.highlight .nb { color: #0086B3 } /* Name.Builtin */ +.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ +.highlight .no { color: #008080 } /* Name.Constant */ +.highlight .ni { color: #800080 } /* Name.Entity */ +.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ +.highlight .nn { color: #555555 } /* Name.Namespace */ +.highlight .nt { color: #000080 } /* Name.Tag */ +.highlight .nv { color: #008080 } /* Name.Variable */ +.highlight .ow { font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #009999 } /* Literal.Number.Float */ +.highlight .mh { color: #009999 } /* Literal.Number.Hex */ +.highlight .mi { color: #009999 } /* Literal.Number.Integer */ +.highlight .mo { color: #009999 } /* Literal.Number.Oct */ +.highlight .sb { color: #d14 } /* Literal.String.Backtick */ +.highlight .sc { color: #d14 } /* Literal.String.Char */ +.highlight .sd { color: #d14 } /* Literal.String.Doc */ +.highlight .s2 { color: #d14 } /* Literal.String.Double */ +.highlight .se { color: #d14 } /* Literal.String.Escape */ +.highlight .sh { color: #d14 } /* Literal.String.Heredoc */ +.highlight .si { color: #d14 } /* Literal.String.Interpol */ +.highlight .sx { color: #d14 } /* Literal.String.Other */ +.highlight .sr { color: #009926 } /* Literal.String.Regex */ +.highlight .s1 { color: #d14 } /* Literal.String.Single */ +.highlight .ss { color: #990073 } /* Literal.String.Symbol */ +.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #008080 } /* Name.Variable.Class */ +.highlight .vg { color: #008080 } /* Name.Variable.Global */ +.highlight .vi { color: #008080 } /* Name.Variable.Instance */ +.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ + +.type-csharp .highlight .k { color: #0000FF } +.type-csharp .highlight .kt { color: #0000FF } +.type-csharp .highlight .nf { color: #000000; font-weight: normal } +.type-csharp .highlight .nc { color: #2B91AF } +.type-csharp .highlight .nn { color: #000000 } +.type-csharp .highlight .s { color: #A31515 } +.type-csharp .highlight .sc { color: #A31515 } diff --git a/stylesheets/styles.css b/stylesheets/styles.css new file mode 100644 index 00000000..c2c94b48 --- /dev/null +++ b/stylesheets/styles.css @@ -0,0 +1,421 @@ +@import url(https://fonts.googleapis.com/css?family=Arvo:400,700,400italic); + +/* MeyerWeb Reset */ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font: inherit; + vertical-align: baseline; +} + + +/* Base text styles */ + +body { + padding:10px 50px 0 0; + font-family:"Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + color: #232323; + background-color: #FBFAF7; + margin: 0; + line-height: 1.8em; + -webkit-font-smoothing: antialiased; + +} + +h1, h2, h3, h4, h5, h6 { + color:#232323; + margin:36px 0 10px; +} + +p, ul, ol, table, dl { + margin:0 0 22px; +} + +h1, h2, h3 { + font-family: Arvo, Monaco, serif; + line-height:1.3; + font-weight: normal; +} + +h1,h2, h3 { + display: block; + border-bottom: 1px solid #ccc; + padding-bottom: 5px; +} + +h1 { + font-size: 30px; +} + +h2 { + font-size: 24px; +} + +h3 { + font-size: 18px; +} + +h4, h5, h6 { + font-family: Arvo, Monaco, serif; + font-weight: 700; +} + +a { + color:#C30000; + font-weight:200; + text-decoration:none; +} + +a:hover { + text-decoration: underline; +} + +a small { + font-size: 12px; +} + +em { + font-style: italic; +} + +strong { + font-weight:700; +} + +ul { + list-style: inside; + padding-left: 25px; +} + +ol { + list-style: decimal inside; + padding-left: 20px; +} + +blockquote { + margin: 0; + padding: 0 0 0 20px; + font-style: italic; +} + +dl, dt, dd, dl p { + font-color: #444; +} + +dl dt { + font-weight: bold; +} + +dl dd { + padding-left: 20px; + font-style: italic; +} + +dl p { + padding-left: 20px; + font-style: italic; +} + +hr { + border:0; + background:#ccc; + height:1px; + margin:0 0 24px; +} + +/* Images */ + +img { + position: relative; + margin: 0 auto; + max-width: 650px; + padding: 5px; + margin: 10px 0 32px 0; + border: 1px solid #ccc; +} + +p img { + display: inline; + margin: 0; + padding: 0; + vertical-align: middle; + text-align: center; + border: none; +} + +/* Code blocks */ + +code, pre { + font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; + color:#000; + font-size:14px; +} + +pre { + padding: 4px 12px; + background: #FDFEFB; + border-radius:4px; + border:1px solid #D7D8C8; + overflow: auto; + overflow-y: hidden; + margin-bottom: 32px; +} + + +/* Tables */ + +table { + width:100%; +} + +table { + border: 1px solid #ccc; + margin-bottom: 32px; + text-align: left; + } + +th { + font-family: 'Arvo', Helvetica, Arial, sans-serif; + font-size: 18px; + font-weight: normal; + padding: 10px; + background: #232323; + color: #FDFEFB; + } + +td { + padding: 10px; + background: #ccc; + } + + +/* Wrapper */ +.wrapper { + width:960px; +} + + +/* Header */ + +header { + background-color: #171717; + color: #FDFDFB; + width:170px; + float:left; + position:fixed; + border: 1px solid #000; + -webkit-border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + padding: 34px 25px 22px 50px; + margin: 30px 25px 0 0; + -webkit-font-smoothing: antialiased; +} + +p.header { + font-size: 16px; +} + +h1.header { + font-family: Arvo, sans-serif; + font-size: 30px; + font-weight: 300; + line-height: 1.3em; + border-bottom: none; + margin-top: 0; +} + + +h1.header, a.header, a.name, header a{ + color: #fff; +} + +a.header { + text-decoration: underline; +} + +a.name { + white-space: nowrap; +} + +header ul { + list-style:none; + padding:0; +} + +header li { + list-style-type: none; + width:132px; + height:15px; + margin-bottom: 12px; + line-height: 1em; + padding: 6px 6px 6px 7px; + + background: #AF0011; + background: -moz-linear-gradient(top, #AF0011 0%, #820011 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#dddddd)); + background: -webkit-linear-gradient(top, #AF0011 0%,#820011 100%); + background: -o-linear-gradient(top, #AF0011 0%,#820011 100%); + background: -ms-linear-gradient(top, #AF0011 0%,#820011 100%); + background: linear-gradient(top, #AF0011 0%,#820011 100%); + + border-radius:4px; + border:1px solid #0D0D0D; + + -webkit-box-shadow: inset 0px 1px 1px 0 rgba(233,2,38, 1); + box-shadow: inset 0px 1px 1px 0 rgba(233,2,38, 1); + +} + +header li:hover { + background: #C3001D; + background: -moz-linear-gradient(top, #C3001D 0%, #950119 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#dddddd)); + background: -webkit-linear-gradient(top, #C3001D 0%,#950119 100%); + background: -o-linear-gradient(top, #C3001D 0%,#950119 100%); + background: -ms-linear-gradient(top, #C3001D 0%,#950119 100%); + background: linear-gradient(top, #C3001D 0%,#950119 100%); +} + +a.buttons { + -webkit-font-smoothing: antialiased; + background: url(../images/arrow-down.png) no-repeat; + font-weight: normal; + text-shadow: rgba(0, 0, 0, 0.4) 0 -1px 0; + padding: 2px 2px 2px 22px; + height: 30px; +} + +a.github { + background: url(../images/octocat-small.png) no-repeat 1px; +} + +a.buttons:hover { + color: #fff; + text-decoration: none; +} + + +/* Section - for main page content */ + +section { + width:650px; + float:right; + padding-bottom:50px; +} + + +/* Footer */ + +footer { + width:170px; + float:left; + position:fixed; + bottom:10px; + padding-left: 50px; +} + +@media print, screen and (max-width: 960px) { + + div.wrapper { + width:auto; + margin:0; + } + + header, section, footer { + float:none; + position:static; + width:auto; + } + + footer { + border-top: 1px solid #ccc; + margin:0 84px 0 50px; + padding:0; + } + + header { + padding-right:320px; + } + + section { + padding:20px 84px 20px 50px; + margin:0 0 20px; + } + + header a small { + display:inline; + } + + header ul { + position:absolute; + right:130px; + top:84px; + } +} + +@media print, screen and (max-width: 720px) { + body { + word-wrap:break-word; + } + + header { + padding:10px 20px 0; + margin-right: 0; + } + + section { + padding:10px 0 10px 20px; + margin:0 0 30px; + } + + footer { + margin: 0 0 0 30px; + } + + header ul, header p.view { + position:static; + } +} + +@media print, screen and (max-width: 480px) { + + header ul li.download { + display:none; + } + + footer { + margin: 0 0 0 20px; + } + + footer a{ + display:block; + } + +} + +@media print { + body { + padding:0.4in; + font-size:12pt; + color:#444; + } +} \ No newline at end of file