Update documents

This commit is contained in:
Jinzhu 2016-02-28 18:07:46 +08:00
parent b1725280c2
commit 6428d4264f
27 changed files with 1697 additions and 128 deletions

View File

@ -0,0 +1,41 @@
{% macro articles(_articles) %}
{% for article in _articles %}
<li class="chapter {% if article.path == file.path and not article.anchor %}active{% endif %}" data-level="{{ article.level }}" {% if article.path %}data-path="{{ article.path|resolveFile }}"{% endif %}>
{% if article.path %}
<a href="{{ article.path|resolveFile }}{{ article.anchor }}">
{% elif article.url %}
<a target="_blank" href="{{ article.url }}">
{% else %}
<span>
{% endif %}
{% if article.level != "0" %}
<b>{{ article.level }}.</b>
{% endif %}
{{ article.title }}
{% if article.path or article.url %}
</a>
{% else %}
</span>
{% endif %}
{% if article.articles.length > 0 %}
<ul class="articles">
{{ articles(article.articles) }}
</ul>
{% endif %}
</li>
{% endfor %}
{% endmacro %}
<div class="book-summary">
{% block book_summary %}
<nav role="navigation">
<ul class="summary">
{% for part in summary.parts %}
{{ articles(part.articles) }}
<li class="divider"></li>
{% endfor %}
</ul>
</nav>
{% endblock %}
</div>

View File

@ -71,7 +71,7 @@ The database handle returned from db.Begin() should be used for all oper">
data-chapter-title="Advanced Usage" data-chapter-title="Advanced Usage"
data-filepath="advanced.md" data-filepath="advanced.md"
data-basepath="" data-basepath=""
data-revision="Fri Feb 26 2016 21:50:14 GMT+0800 (CST)" data-revision="Sun Feb 28 2016 16:13:20 GMT+0800 (CST)"
data-innerlanguage=""> data-innerlanguage="">
@ -554,17 +554,12 @@ The database handle returned from db.Begin() should be used for all oper">
<li class="divider"></li> <li class="divider"></li>
<li>
<a href="https://www.gitbook.com" target="blank" class="gitbook-link">
Published with GitBook
</a>
</li>
</ul> </ul>
</nav> </nav>
</div> </div>
<div class="book-body"> <div class="book-body">
<div class="body-inner"> <div class="body-inner">
@ -757,6 +752,10 @@ db<span class="token operator">.</span><span class="token function">SetLogger<sp
<script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
<script src="gitbook/gitbook-plugin-search/lunr.min.js"></script> <script src="gitbook/gitbook-plugin-search/lunr.min.js"></script>
@ -772,13 +771,9 @@ db<span class="token operator">.</span><span class="token function">SetLogger<sp
<script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script> <script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script>
<script src="gitbook/gitbook-plugin-livereload/plugin.js"></script>
<script> <script>
require(["gitbook"], function(gitbook) { require(["gitbook"], function(gitbook) {
gitbook.start({"toc":{"addClass":true,"className":"toc"},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"base":"https://github.com/jinzhu/gorm/edit/master","label":"Edit This Page"},"prism":{},"search":{"maxIndexSize":1000000},"sharing":{"all":["facebook","google","instapaper","twitter","weibo"],"facebook":true,"google":false,"instapaper":false,"twitter":true,"vk":false,"weibo":false},"fontsettings":{"family":"sans","size":2,"theme":"white"},"theme-default":{"styles":{"ebook":"styles/ebook.css","epub":"styles/epub.css","mobi":"styles/mobi.css","pdf":"styles/pdf.css","print":"styles/print.css","website":"styles/website.css"}},"livereload":{}}); gitbook.start({"fontsettings":{"theme":"night","family":"sans","size":4},"toc":{"addClass":true,"className":"toc"},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"base":"https://github.com/jinzhu/gorm/edit/master","label":"Edit This Page"},"prism":{},"anker-enable":{},"search":{"maxIndexSize":1000000},"sharing":{"all":["facebook","google","instapaper","twitter","weibo"],"facebook":true,"google":false,"instapaper":false,"twitter":true,"vk":false,"weibo":false},"theme-default":{"styles":{"ebook":"styles/ebook.css","epub":"styles/epub.css","mobi":"styles/mobi.css","pdf":"styles/pdf.css","print":"styles/print.css","website":"styles/website.css"}}});
}); });
</script> </script>

View File

@ -70,7 +70,7 @@
data-chapter-title="Associations" data-chapter-title="Associations"
data-filepath="associations.md" data-filepath="associations.md"
data-basepath="" data-basepath=""
data-revision="Fri Feb 26 2016 21:50:14 GMT+0800 (CST)" data-revision="Sun Feb 28 2016 16:13:20 GMT+0800 (CST)"
data-innerlanguage=""> data-innerlanguage="">
@ -553,17 +553,12 @@
<li class="divider"></li> <li class="divider"></li>
<li>
<a href="https://www.gitbook.com" target="blank" class="gitbook-link">
Published with GitBook
</a>
</li>
</ul> </ul>
</nav> </nav>
</div> </div>
<div class="book-body"> <div class="book-body">
<div class="body-inner"> <div class="body-inner">
@ -758,6 +753,10 @@ db<span class="token operator">.</span><span class="token function">Model<span c
<script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
<script src="gitbook/gitbook-plugin-search/lunr.min.js"></script> <script src="gitbook/gitbook-plugin-search/lunr.min.js"></script>
@ -773,13 +772,9 @@ db<span class="token operator">.</span><span class="token function">Model<span c
<script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script> <script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script>
<script src="gitbook/gitbook-plugin-livereload/plugin.js"></script>
<script> <script>
require(["gitbook"], function(gitbook) { require(["gitbook"], function(gitbook) {
gitbook.start({"toc":{"addClass":true,"className":"toc"},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"base":"https://github.com/jinzhu/gorm/edit/master","label":"Edit This Page"},"prism":{},"search":{"maxIndexSize":1000000},"sharing":{"all":["facebook","google","instapaper","twitter","weibo"],"facebook":true,"google":false,"instapaper":false,"twitter":true,"vk":false,"weibo":false},"fontsettings":{"family":"sans","size":2,"theme":"white"},"theme-default":{"styles":{"ebook":"styles/ebook.css","epub":"styles/epub.css","mobi":"styles/mobi.css","pdf":"styles/pdf.css","print":"styles/print.css","website":"styles/website.css"}},"livereload":{}}); gitbook.start({"fontsettings":{"theme":"night","family":"sans","size":4},"toc":{"addClass":true,"className":"toc"},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"base":"https://github.com/jinzhu/gorm/edit/master","label":"Edit This Page"},"prism":{},"anker-enable":{},"search":{"maxIndexSize":1000000},"sharing":{"all":["facebook","google","instapaper","twitter","weibo"],"facebook":true,"google":false,"instapaper":false,"twitter":true,"vk":false,"weibo":false},"theme-default":{"styles":{"ebook":"styles/ebook.css","epub":"styles/epub.css","mobi":"styles/mobi.css","pdf":"styles/pdf.css","print":"styles/print.css","website":"styles/website.css"}}});
}); });
</script> </script>

View File

@ -1,19 +0,0 @@
{
"plugins": [
"prism", "-highlight", "toggle-chapters", "toc",
"github", "anchors", "edit-link"
],
"pluginsConfig": {
"toc": {
"addClass": true,
"className": "toc"
},
"github": {
"url": "https://github.com/jinzhu/gorm"
},
"edit-link": {
"base": "https://github.com/jinzhu/gorm/edit/gh-pages",
"label": "Edit This Page"
}
}
}

1
build.sh Executable file
View File

@ -0,0 +1 @@
cd documents; gitbook build; pwd

View File

@ -71,7 +71,7 @@ If any callback returns an error, gorm will stop future operations and rollback
data-chapter-title="Callbacks" data-chapter-title="Callbacks"
data-filepath="callbacks.md" data-filepath="callbacks.md"
data-basepath="" data-basepath=""
data-revision="Fri Feb 26 2016 21:50:14 GMT+0800 (CST)" data-revision="Sun Feb 28 2016 16:13:20 GMT+0800 (CST)"
data-innerlanguage=""> data-innerlanguage="">
@ -554,17 +554,12 @@ If any callback returns an error, gorm will stop future operations and rollback
<li class="divider"></li> <li class="divider"></li>
<li>
<a href="https://www.gitbook.com" target="blank" class="gitbook-link">
Published with GitBook
</a>
</li>
</ul> </ul>
</nav> </nav>
</div> </div>
<div class="book-body"> <div class="book-body">
<div class="body-inner"> <div class="body-inner">
@ -688,6 +683,10 @@ For this Gorm supports passing transactions to callbacks like this:</p>
<script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
<script src="gitbook/gitbook-plugin-search/lunr.min.js"></script> <script src="gitbook/gitbook-plugin-search/lunr.min.js"></script>
@ -703,13 +702,9 @@ For this Gorm supports passing transactions to callbacks like this:</p>
<script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script> <script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script>
<script src="gitbook/gitbook-plugin-livereload/plugin.js"></script>
<script> <script>
require(["gitbook"], function(gitbook) { require(["gitbook"], function(gitbook) {
gitbook.start({"toc":{"addClass":true,"className":"toc"},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"base":"https://github.com/jinzhu/gorm/edit/master","label":"Edit This Page"},"prism":{},"search":{"maxIndexSize":1000000},"sharing":{"all":["facebook","google","instapaper","twitter","weibo"],"facebook":true,"google":false,"instapaper":false,"twitter":true,"vk":false,"weibo":false},"fontsettings":{"family":"sans","size":2,"theme":"white"},"theme-default":{"styles":{"ebook":"styles/ebook.css","epub":"styles/epub.css","mobi":"styles/mobi.css","pdf":"styles/pdf.css","print":"styles/print.css","website":"styles/website.css"}},"livereload":{}}); gitbook.start({"fontsettings":{"theme":"night","family":"sans","size":4},"toc":{"addClass":true,"className":"toc"},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"base":"https://github.com/jinzhu/gorm/edit/master","label":"Edit This Page"},"prism":{},"anker-enable":{},"search":{"maxIndexSize":1000000},"sharing":{"all":["facebook","google","instapaper","twitter","weibo"],"facebook":true,"google":false,"instapaper":false,"twitter":true,"vk":false,"weibo":false},"theme-default":{"styles":{"ebook":"styles/ebook.css","epub":"styles/epub.css","mobi":"styles/mobi.css","pdf":"styles/pdf.css","print":"styles/print.css","website":"styles/website.css"}}});
}); });
</script> </script>

View File

@ -70,7 +70,7 @@
data-chapter-title="CRUD: Reading and Writing Data" data-chapter-title="CRUD: Reading and Writing Data"
data-filepath="curd.md" data-filepath="curd.md"
data-basepath="" data-basepath=""
data-revision="Fri Feb 26 2016 21:50:14 GMT+0800 (CST)" data-revision="Sun Feb 28 2016 16:13:20 GMT+0800 (CST)"
data-innerlanguage=""> data-innerlanguage="">
@ -553,17 +553,12 @@
<li class="divider"></li> <li class="divider"></li>
<li>
<a href="https://www.gitbook.com" target="blank" class="gitbook-link">
Published with GitBook
</a>
</li>
</ul> </ul>
</nav> </nav>
</div> </div>
<div class="book-body"> <div class="book-body">
<div class="body-inner"> <div class="body-inner">
@ -1209,6 +1204,10 @@ db<span class="token operator">.</span><span class="token function">Unscoped<spa
<script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
<script src="gitbook/gitbook-plugin-search/lunr.min.js"></script> <script src="gitbook/gitbook-plugin-search/lunr.min.js"></script>
@ -1224,13 +1223,9 @@ db<span class="token operator">.</span><span class="token function">Unscoped<spa
<script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script> <script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script>
<script src="gitbook/gitbook-plugin-livereload/plugin.js"></script>
<script> <script>
require(["gitbook"], function(gitbook) { require(["gitbook"], function(gitbook) {
gitbook.start({"toc":{"addClass":true,"className":"toc"},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"base":"https://github.com/jinzhu/gorm/edit/master","label":"Edit This Page"},"prism":{},"search":{"maxIndexSize":1000000},"sharing":{"all":["facebook","google","instapaper","twitter","weibo"],"facebook":true,"google":false,"instapaper":false,"twitter":true,"vk":false,"weibo":false},"fontsettings":{"family":"sans","size":2,"theme":"white"},"theme-default":{"styles":{"ebook":"styles/ebook.css","epub":"styles/epub.css","mobi":"styles/mobi.css","pdf":"styles/pdf.css","print":"styles/print.css","website":"styles/website.css"}},"livereload":{}}); gitbook.start({"fontsettings":{"theme":"night","family":"sans","size":4},"toc":{"addClass":true,"className":"toc"},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"base":"https://github.com/jinzhu/gorm/edit/master","label":"Edit This Page"},"prism":{},"anker-enable":{},"search":{"maxIndexSize":1000000},"sharing":{"all":["facebook","google","instapaper","twitter","weibo"],"facebook":true,"google":false,"instapaper":false,"twitter":true,"vk":false,"weibo":false},"theme-default":{"styles":{"ebook":"styles/ebook.css","epub":"styles/epub.css","mobi":"styles/mobi.css","pdf":"styles/pdf.css","print":"styles/print.css","website":"styles/website.css"}}});
}); });
</script> </script>

View File

@ -70,7 +70,7 @@
data-chapter-title="Database" data-chapter-title="Database"
data-filepath="database.md" data-filepath="database.md"
data-basepath="" data-basepath=""
data-revision="Fri Feb 26 2016 21:50:14 GMT+0800 (CST)" data-revision="Sun Feb 28 2016 16:13:20 GMT+0800 (CST)"
data-innerlanguage=""> data-innerlanguage="">
@ -553,17 +553,12 @@
<li class="divider"></li> <li class="divider"></li>
<li>
<a href="https://www.gitbook.com" target="blank" class="gitbook-link">
Published with GitBook
</a>
</li>
</ul> </ul>
</nav> </nav>
</div> </div>
<div class="book-body"> <div class="book-body">
<div class="body-inner"> <div class="body-inner">
@ -750,6 +745,10 @@ db<span class="token operator">.</span><span class="token function">Model<span c
<script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
<script src="gitbook/gitbook-plugin-search/lunr.min.js"></script> <script src="gitbook/gitbook-plugin-search/lunr.min.js"></script>
@ -765,13 +764,9 @@ db<span class="token operator">.</span><span class="token function">Model<span c
<script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script> <script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script>
<script src="gitbook/gitbook-plugin-livereload/plugin.js"></script>
<script> <script>
require(["gitbook"], function(gitbook) { require(["gitbook"], function(gitbook) {
gitbook.start({"toc":{"addClass":true,"className":"toc"},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"base":"https://github.com/jinzhu/gorm/edit/master","label":"Edit This Page"},"prism":{},"search":{"maxIndexSize":1000000},"sharing":{"all":["facebook","google","instapaper","twitter","weibo"],"facebook":true,"google":false,"instapaper":false,"twitter":true,"vk":false,"weibo":false},"fontsettings":{"family":"sans","size":2,"theme":"white"},"theme-default":{"styles":{"ebook":"styles/ebook.css","epub":"styles/epub.css","mobi":"styles/mobi.css","pdf":"styles/pdf.css","print":"styles/print.css","website":"styles/website.css"}},"livereload":{}}); gitbook.start({"fontsettings":{"theme":"night","family":"sans","size":4},"toc":{"addClass":true,"className":"toc"},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"base":"https://github.com/jinzhu/gorm/edit/master","label":"Edit This Page"},"prism":{},"anker-enable":{},"search":{"maxIndexSize":1000000},"sharing":{"all":["facebook","google","instapaper","twitter","weibo"],"facebook":true,"google":false,"instapaper":false,"twitter":true,"vk":false,"weibo":false},"theme-default":{"styles":{"ebook":"styles/ebook.css","epub":"styles/epub.css","mobi":"styles/mobi.css","pdf":"styles/pdf.css","print":"styles/print.css","website":"styles/website.css"}}});
}); });
</script> </script>

View File

@ -70,7 +70,7 @@
data-chapter-title="Development" data-chapter-title="Development"
data-filepath="development.md" data-filepath="development.md"
data-basepath="" data-basepath=""
data-revision="Fri Feb 26 2016 21:50:14 GMT+0800 (CST)" data-revision="Sun Feb 28 2016 16:13:20 GMT+0800 (CST)"
data-innerlanguage=""> data-innerlanguage="">
@ -553,17 +553,12 @@
<li class="divider"></li> <li class="divider"></li>
<li>
<a href="https://www.gitbook.com" target="blank" class="gitbook-link">
Published with GitBook
</a>
</li>
</ul> </ul>
</nav> </nav>
</div> </div>
<div class="book-body"> <div class="book-body">
<div class="body-inner"> <div class="body-inner">
@ -678,6 +673,10 @@ db.Callback().Create().Before(&quot;gorm:create&quot;).After(&quot;gorm:before_c
<script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
<script src="gitbook/gitbook-plugin-search/lunr.min.js"></script> <script src="gitbook/gitbook-plugin-search/lunr.min.js"></script>
@ -693,13 +692,9 @@ db.Callback().Create().Before(&quot;gorm:create&quot;).After(&quot;gorm:before_c
<script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script> <script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script>
<script src="gitbook/gitbook-plugin-livereload/plugin.js"></script>
<script> <script>
require(["gitbook"], function(gitbook) { require(["gitbook"], function(gitbook) {
gitbook.start({"toc":{"addClass":true,"className":"toc"},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"base":"https://github.com/jinzhu/gorm/edit/master","label":"Edit This Page"},"prism":{},"search":{"maxIndexSize":1000000},"sharing":{"all":["facebook","google","instapaper","twitter","weibo"],"facebook":true,"google":false,"instapaper":false,"twitter":true,"vk":false,"weibo":false},"fontsettings":{"family":"sans","size":2,"theme":"white"},"theme-default":{"styles":{"ebook":"styles/ebook.css","epub":"styles/epub.css","mobi":"styles/mobi.css","pdf":"styles/pdf.css","print":"styles/print.css","website":"styles/website.css"}},"livereload":{}}); gitbook.start({"fontsettings":{"theme":"night","family":"sans","size":4},"toc":{"addClass":true,"className":"toc"},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"base":"https://github.com/jinzhu/gorm/edit/master","label":"Edit This Page"},"prism":{},"anker-enable":{},"search":{"maxIndexSize":1000000},"sharing":{"all":["facebook","google","instapaper","twitter","weibo"],"facebook":true,"google":false,"instapaper":false,"twitter":true,"vk":false,"weibo":false},"theme-default":{"styles":{"ebook":"styles/ebook.css","epub":"styles/epub.css","mobi":"styles/mobi.css","pdf":"styles/pdf.css","print":"styles/print.css","website":"styles/website.css"}}});
}); });
</script> </script>

3
documents/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/_book
/node_modules
/gitbook

74
documents/README.md Normal file
View File

@ -0,0 +1,74 @@
# GORM
The fantastic ORM library for Golang, aims to be developer friendly.
[![wercker status](https://app.wercker.com/status/0cb7bb1039e21b74f8274941428e0921/s/master "wercker status")](https://app.wercker.com/project/bykey/0cb7bb1039e21b74f8274941428e0921)
[![GoDoc](https://godoc.org/github.com/jinzhu/gorm?status.svg)](https://godoc.org/github.com/jinzhu/gorm)
[![Join the chat at https://gitter.im/jinzhu/gorm](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jinzhu/gorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## Overview
* Full-Featured ORM (almost)
* Chainable API
* Auto Migrations
* Relations (Has One, Has Many, Belongs To, Many To Many, [Polymorphism](#polymorphism))
* Callbacks (Before/After Create/Save/Update/Delete/Find)
* Preloading (eager loading)
* Transactions
* Embed Anonymous Struct
* Soft Deletes
* Customizable Logger
* Iteration Support via [Rows](#row--rows)
* Every feature comes with tests
* Developer Friendly
## Install
```
go get -u github.com/jinzhu/gorm
```
## Basic Usage
```go
type Product struct {
gorm.Model
Code string
Price uint
}
var db *gorm.DB
func init() {
var err error
db, err = gorm.Open("sqlite", "test.db")
}
func main() {
db.Create(&Product{Code: "L1212", Price: 1000})
var product Product
db.First(&product, 1) // find product with id 1
db.First(&product, "code = ?", "L1212") // find product with code l1212
db.Model(&product).Update("Price", 2000) // update product's price to 2000
db.Delete(&product) // delete product
}
```
# Author
**jinzhu**
* <http://github.com/jinzhu>
* <wosmvp@gmail.com>
* <http://twitter.com/zhangjinzhu>
# Contributors
https://github.com/jinzhu/gorm/graphs/contributors
## License
Released under the [MIT License](https://github.com/jinzhu/gorm/blob/master/License).

31
documents/SUMMARY.md Normal file
View File

@ -0,0 +1,31 @@
# Summary
* [Getting Started with GORM](README.md)
* [Database](database.md)
* [Database Connection](database.md#connecting-to-a-database)
* [Migration](database.md#migration)
* [Models](models.md)
* [Model Defination](models.md#model-defination),
* [Naming Conventions & Overriding](models.md#conventions-overriding-conventions),
* [Associations](associations.md)
* [Belongs To](associations.md#belongs-to)
* [Has One](associations.md#has-one)
* [Has Many](associations.md#has-many)
* [Many To Many](associations.md#many-to-many)
* [Polymorphism](associations.md#polymorphism)
* [Association Mode](associations.md#association-mode)
* [CRUD: Reading and Writing Data](curd.md)
* [Create](curd.md#create),
* [Query](curd.md#query),
* [Preloading (Eager Loading)](curd.md#preloading),
* [Update](curd.md#update),
* [Delete / Soft Delete](curd.md#delete)
* [Callbacks](callbacks.md)
* [Advanced Usage](advanced.md)
* [Error Handling](advanced.md#error-handling)
* [Transactions](advanced.md#transactions)
* [Raw SQL & SQL Builder](advanced.md#sql-builder)
* [Composite Primary Key](advanced.md#compose-primary-key)
* [Overriding Logger](advanced.md#logger)
* [Development](development.md)
* [Write Plugins](development.md#callbacks)

View File

@ -0,0 +1,41 @@
{% macro articles(_articles) %}
{% for article in _articles %}
<li class="chapter {% if article.path == file.path and not article.anchor %}active{% endif %}" data-level="{{ article.level }}" {% if article.path %}data-path="{{ article.path|resolveFile }}"{% endif %}>
{% if article.path %}
<a href="{{ article.path|resolveFile }}{{ article.anchor }}">
{% elif article.url %}
<a target="_blank" href="{{ article.url }}">
{% else %}
<span>
{% endif %}
{% if article.level != "0" %}
<b>{{ article.level }}.</b>
{% endif %}
{{ article.title }}
{% if article.path or article.url %}
</a>
{% else %}
</span>
{% endif %}
{% if article.articles.length > 0 %}
<ul class="articles">
{{ articles(article.articles) }}
</ul>
{% endif %}
</li>
{% endfor %}
{% endmacro %}
<div class="book-summary">
{% block book_summary %}
<nav role="navigation">
<ul class="summary">
{% for part in summary.parts %}
{{ articles(part.articles) }}
<li class="divider"></li>
{% endfor %}
</ul>
</nav>
{% endblock %}
</div>

149
documents/advanced.md Normal file
View File

@ -0,0 +1,149 @@
# Advanced Usage
<!-- toc -->
## Error Handling
```go
query := db.Where("name = ?", "jinzhu").First(&user)
query := db.First(&user).Limit(10).Find(&users)
// query.Error will return the last happened error
// So you could do error handing in your application like this:
if err := db.Where("name = ?", "jinzhu").First(&user).Error; err != nil {
// error handling...
}
// RecordNotFound
// If no record found when you query data, gorm will return RecordNotFound error, you could check it like this:
db.Where("name = ?", "hello world").First(&User{}).Error == gorm.RecordNotFound
// Or use the shortcut method
db.Where("name = ?", "hello world").First(&user).RecordNotFound()
if db.Model(&user).Related(&credit_card).RecordNotFound() {
// no credit card found error handling
}
```
## Transactions
To perform a set of operations within a transaction, the general flow is as below.
The database handle returned from ``` db.Begin() ``` should be used for all operations within the transaction.
(Note that all individual save and delete operations are run in a transaction by default.)
```go
// begin
tx := db.Begin()
// do some database operations (use 'tx' from this point, not 'db')
tx.Create(...)
...
// rollback in case of error
tx.Rollback()
// Or commit if all is ok
tx.Commit()
```
### A Specific Example
```go
func CreateAnimals(db *gorm.DB) err {
tx := db.Begin()
// Note the use of tx as the database handle once you are within a transaction
if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
tx.Rollback()
return err
}
if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
tx.Rollback()
return err
}
tx.Commit()
return nil
}
```
## Raw SQL
```go
db.Exec("DROP TABLE users;")
db.Exec("UPDATE orders SET shipped_at=? WHERE id IN (?)", time.Now, []int64{11,22,33})
```
## Row & Rows
It is even possible to get query result as `*sql.Row` or `*sql.Rows`
```go
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)
...
}
// 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 Rows
```go
rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
defer rows.Close()
for rows.Next() {
var user User
db.ScanRows(rows, &user)
// do something
}
```
## Composite Primary Key
```go
type Product struct {
ID string `gorm:"primary_key"`
LanguageCode string `gorm:"primary_key"`
}
```
## Logger
Gorm has built-in logger support
```go
// Enable Logger
db.LogMode(true)
// Diable Logger
db.LogMode(false)
// Debug a single operation
db.Debug().Where("name = ?", "jinzhu").First(&User{})
```
![logger](https://raw.github.com/jinzhu/gorm/master/doc/logger.png)
### Customize Logger
```go
// Refer gorm's default logger for how to: https://github.com/jinzhu/gorm/blob/master/logger.go#files
db.SetLogger(gorm.Logger{revel.TRACE})
db.SetLogger(log.New(os.Stdout, "\r\n", 0))
```

150
documents/associations.md Normal file
View File

@ -0,0 +1,150 @@
# Associations
<!-- toc -->
## Belongs To
```go
// User belongs to a profile, ProfileID is the foreign key
type User struct {
gorm.Model
Profile Profile
ProfileID int
}
type Profile struct {
gorm.Model
Name string
}
db.Model(&user).Related(&profile)
//// SELECT * FROM profiles WHERE id = 111; // 111 is user's foreign key ProfileID
```
## Has One
```go
// User has one CreditCard, UserID is the foreign key
type User struct {
gorm.Model
CreditCard CreditCard
}
type CreditCard struct {
gorm.Model
UserID uint
Number string
}
var card CreditCard
db.Model(&user).Related(&card, "CreditCard")
//// SELECT * FROM credit_cards WHERE user_id = 123; // 123 is user's primary key
// CreditCard is user's field name, it means get user's CreditCard relations and fill it into variable card
// If the field name is same as the variable's type name, like above example, it could be omitted, like:
db.Model(&user).Related(&card)
```
## Has Many
```go
// User has many emails, UserID is the foreign key
type User struct {
gorm.Model
Emails []Email
}
type Email struct {
gorm.Model
Email string
UserID uint
}
db.Model(&user).Related(&emails)
//// SELECT * FROM emails WHERE user_id = 111; // 111 is user's primary key
```
## Many To Many
```go
// User has and belongs to many languages, use `user_languages` as join table
type User struct {
gorm.Model
Languages []Language `gorm:"many2many:user_languages;"`
}
type Language struct {
gorm.Model
Name string
}
db.Model(&user).Related(&languages)
//// SELECT * FROM "languages" INNER JOIN "user_languages" ON "user_languages"."language_id" = "languages"."id" WHERE "user_languages"."user_id" = 111
```
## Polymorphism
Supports polymorphic has-many and has-one associations.
```go
type Cat struct {
Id int
Name string
Toy Toy `gorm:"polymorphic:Owner;"`
}
type Dog struct {
Id int
Name string
Toy Toy `gorm:"polymorphic:Owner;"`
}
type Toy struct {
Id int
Name string
OwnerId int
OwnerType string
}
```
Note: polymorphic belongs-to and many-to-many are explicitly NOT supported, and will throw errors.
## Association Mode
Association Mode contains some helper methods to handle relationship things easily.
```go
// Start Association Mode
var user User
db.Model(&user).Association("Languages")
// `user` is the source, it need to be a valid record (contains primary key)
// `Languages` is source's field name for a relationship.
// If those conditions not matched, will return an error, check it with:
// db.Model(&user).Association("Languages").Error
// Query - Find out all related associations
db.Model(&user).Association("Languages").Find(&languages)
// Append - Append new associations for many2many, has_many, will replace current association for has_one, belongs_to
db.Model(&user).Association("Languages").Append([]Language{languageZH, languageEN})
db.Model(&user).Association("Languages").Append(Language{Name: "DE"})
// Delete - Remove relationship between source & passed arguments, won't delete those arguments
db.Model(&user).Association("Languages").Delete([]Language{languageZH, languageEN})
db.Model(&user).Association("Languages").Delete(languageZH, languageEN)
// Replace - Replace current associations with new one
db.Model(&user).Association("Languages").Replace([]Language{languageZH, languageEN})
db.Model(&user).Association("Languages").Replace(Language{Name: "DE"}, languageEN)
// Count - Return the count of current associations
db.Model(&user).Association("Languages").Count()
// Clear - Remove relationship between source & current associations, won't delete those associations
db.Model(&user).Association("Languages").Clear()
```

24
documents/book.json Normal file
View File

@ -0,0 +1,24 @@
{
"title": "GORM Guide",
"plugins": [
"-highlight", "prism", "toc", "github", "edit-link", "anker-enable"
],
"pluginsConfig": {
"fontsettings": {
"theme": "night",
"family": "sans",
"size": 4
},
"toc": {
"addClass": true,
"className": "toc"
},
"github": {
"url": "https://github.com/jinzhu/gorm"
},
"edit-link": {
"base": "https://github.com/jinzhu/gorm/edit/master",
"label": "Edit This Page"
}
}
}

78
documents/callbacks.md Normal file
View File

@ -0,0 +1,78 @@
# Callbacks
<!-- toc -->
Callbacks are methods defined on the pointer of struct.
If any callback returns an error, gorm will stop future operations and rollback all changes.
Here is the list of all available callbacks:
(listed in the same order in which they will get called during the respective operations)
### Creating An Object
```go
BeforeSave
BeforeCreate
// save before associations
// save self
// save after associations
AfterCreate
AfterSave
```
### Updating An Object
```go
BeforeSave
BeforeUpdate
// save before associations
// save self
// save after associations
AfterUpdate
AfterSave
```
### Destroying An Object
```go
BeforeDelete
// delete self
AfterDelete
```
### After Find
```go
// load data from database
AfterFind
```
### Example
```go
func (u *User) BeforeUpdate() (err error) {
if u.readonly() {
err = errors.New("read only user")
}
return
}
// Rollback the insertion if user's id greater than 1000
func (u *User) AfterCreate() (err error) {
if (u.Id > 1000) {
err = errors.New("user id is already greater than 1000")
}
return
}
```
Save/delete operations in gorm are running in a transaction.
Changes made in that transaction are not visible unless it is commited.
So if you want to use those changes in your callbacks, you need to run your SQL in the same transaction.
For this Gorm supports passing transactions to callbacks like this:
```go
func (u *User) AfterCreate(tx *gorm.DB) (err error) {
tx.Model(u).Update("role", "admin")
return
}
```

674
documents/curd.md Normal file
View File

@ -0,0 +1,674 @@
# CRUD: Reading and Writing Data
<!-- toc -->
## Create
### Create Record
```go
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
db.NewRecord(user) // => returns `true` as primary key is blank
db.Create(&user)
db.NewRecord(user) // => return `false` after `user` created
// Associations will be inserted automatically when save the record
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"}},
Languages: []Language{{Name: "ZH"}, {Name: "EN"}},
}
db.Create(&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");
//// INSERT INTO "languages" ("name") VALUES ('ZH');
//// INSERT INTO user_languages ("user_id","language_id") VALUES (111, 1);
//// INSERT INTO "languages" ("name") VALUES ('EN');
//// INSERT INTO user_languages ("user_id","language_id") VALUES (111, 2);
//// COMMIT;
```
### Create With Associations
Refer Associations for more details
### Default Values
You could defined default value in the `sql` tag, then the generated creating SQL will ignore these fields that including default value and its value is blank, and after inserted the record into databae, gorm will load those fields's value from database.
```go
type Animal struct {
ID int64
Name string `sql:"default:'galeone'"`
Age int64
}
var animal = Animal{Age: 99, Name: ""}
db.Create(&animal)
// INSERT INTO animals("age") values('99');
// SELECT name from animals WHERE ID=111; // the returning primary key is 111
// animal.Name => 'galeone'
```
### Setting Primary Key In Callbacks
If you want to set primary key in `BeforeCreate` callback, you could use `scope.SetColumn`, for example:
```go
func (user *User) BeforeCreate(scope *gorm.Scope) error {
scope.SetColumn("ID", uuid.New())
return nil
}
```
### Extra Creating option
```go
// Add extra SQL option for inserting SQL
db.Set("gorm:insert_option", "ON CONFLICT").Create(&product)
// INSERT INTO products (name, code) VALUES ("name", "code") ON CONFLICT;
```
## Query
```go
// Get the first record
db.First(&user)
//// SELECT * FROM users ORDER BY id LIMIT 1;
// 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 (Plain SQL)
```go
// 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)
// IN
db.Where("name in (?)", []string{"jinzhu", "jinzhu 2"}).Find(&users)
// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
// AND
db.Where("name = ? and age >= ?", "jinzhu", "22").Find(&users)
// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
```
### Query With Where (Struct & Map)
```go
// Struct
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
//// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 LIMIT 1;
// Map
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
//// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;
// Slice of primary keys
db.Where([]int64{20, 21, 22}).Find(&users)
//// SELECT * FROM users WHERE id IN (20, 21, 22);
```
### Query With Not
```go
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 slice of 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;
// Plain SQL
db.Not("name = ?", "jinzhu").First(&user)
//// SELECT * FROM users WHERE NOT(name = "jinzhu");
// Struct
db.Not(User{Name: "jinzhu"}).First(&user)
//// SELECT * FROM users WHERE name <> "jinzhu";
```
### Query With Inline Condition
```go
// Get by primary key
db.First(&user, 23)
//// SELECT * FROM users WHERE id = 23 LIMIT 1;
// Plain SQL
db.Find(&user, "name = ?", "jinzhu")
//// SELECT * FROM users WHERE name = "jinzhu";
db.Find(&users, "name <> ? AND age > ?", "jinzhu", 20)
//// SELECT * FROM users WHERE name <> "jinzhu" AND age > 20;
// Struct
db.Find(&users, User{Age: 20})
//// SELECT * FROM users WHERE age = 20;
// Map
db.Find(&users, map[string]interface{}{"age": 20})
//// SELECT * FROM users WHERE age = 20;
```
### Query With Or
```go
db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
//// SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';
// Struct
db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2"}).Find(&users)
//// SELECT * FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2';
// Map
db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2"}).Find(&users)
```
### Query Chains
Gorm has a chainable API, you could use it like this
```go
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)
```
### Extra Querying option
```go
// Add extra SQL option for selecting SQL
db.Set("gorm:query_option", "FOR UPDATE").First(&user, 10)
//// SELECT * FROM users WHERE id = 10 FOR UPDATE;
```
### FirstOrInit
Get the first matched record, or initialize a record with search conditions.
```go
// Unfound
db.FirstOrInit(&user, User{Name: "non_existing"})
//// user -> User{Name: "non_existing"}
// Found
db.Where(User{Name: "Jinzhu"}).FirstOrInit(&user)
//// user -> User{Id: 111, Name: "Jinzhu", Age: 20}
db.FirstOrInit(&user, map[string]interface{}{"name": "jinzhu"})
//// user -> User{Id: 111, Name: "Jinzhu", Age: 20}
```
#### Attrs
Ignore some values when searching, but use them to initialize the struct if record is not found.
```go
// Unfound
db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrInit(&user)
//// SELECT * FROM USERS WHERE name = 'non_existing';
//// user -> User{Name: "non_existing", Age: 20}
db.Where(User{Name: "noexisting_user"}).Attrs("age", 20).FirstOrInit(&user)
//// SELECT * FROM USERS WHERE name = 'non_existing';
//// user -> User{Name: "non_existing", Age: 20}
// Found
db.Where(User{Name: "Jinzhu"}).Attrs(User{Age: 30}).FirstOrInit(&user)
//// SELECT * FROM USERS WHERE name = jinzhu';
//// user -> User{Id: 111, Name: "Jinzhu", Age: 20}
```
#### Assign
Ignore some values when searching, but assign it to the result regardless it is found or not.
```go
// Unfound
db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrInit(&user)
//// user -> User{Name: "non_existing", Age: 20}
// Found
db.Where(User{Name: "Jinzhu"}).Assign(User{Age: 30}).FirstOrInit(&user)
//// SELECT * FROM USERS WHERE name = jinzhu';
//// user -> User{Id: 111, Name: "Jinzhu", Age: 30}
```
### FirstOrCreate
Get the first matched record, or create with search conditions.
```go
// Unfound
db.FirstOrCreate(&user, User{Name: "non_existing"})
//// INSERT INTO "users" (name) VALUES ("non_existing");
//// user -> User{Id: 112, Name: "non_existing"}
// Found
db.Where(User{Name: "Jinzhu"}).FirstOrCreate(&user)
//// user -> User{Id: 111, Name: "Jinzhu"}
```
#### Attrs
Ignore some values when searching, but use them to create the struct if record is not found. like `FirstOrInit`
```go
// Unfound
db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrCreate(&user)
//// SELECT * FROM users WHERE name = 'non_existing';
//// INSERT INTO "users" (name, age) VALUES ("non_existing", 20);
//// user -> User{Id: 112, Name: "non_existing", Age: 20}
// Found
db.Where(User{Name: "jinzhu"}).Attrs(User{Age: 30}).FirstOrCreate(&user)
//// SELECT * FROM users WHERE name = 'jinzhu';
//// user -> User{Id: 111, Name: "jinzhu", Age: 20}
```
#### Assign
Ignore some values when searching, but assign it to the record regardless it is found or not, then save back to database. like `FirstOrInit`
```go
// Unfound
db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrCreate(&user)
//// SELECT * FROM users WHERE name = 'non_existing';
//// INSERT INTO "users" (name, age) VALUES ("non_existing", 20);
//// user -> User{Id: 112, Name: "non_existing", Age: 20}
// Found
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 -> User{Id: 111, Name: "jinzhu", Age: 30}
```
### Select
```go
db.Select("name, age").Find(&users)
//// SELECT name, age FROM users;
db.Select([]string{"name", "age"}).Find(&users)
//// SELECT name, age FROM users;
db.Table("users").Select("COALESCE(age,?)", 42).Rows()
//// SELECT COALESCE(age,'42') FROM users;
```
### Order
```go
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
```go
db.Limit(3).Find(&users)
//// SELECT * FROM users LIMIT 3;
// Cancel limit condition with -1
db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
//// SELECT * FROM users LIMIT 10; (users1)
//// SELECT * FROM users; (users2)
```
### Offset
```go
db.Offset(3).Find(&users)
//// SELECT * FROM users OFFSET 3;
// Cancel offset condition with -1
db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
//// SELECT * FROM users OFFSET 10; (users1)
//// SELECT * FROM users; (users2)
```
### Count
```go
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)
db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count)
//// SELECT count(*) FROM users WHERE name = 'jinzhu'; (count)
db.Table("deleted_users").Count(&count)
//// SELECT count(*) FROM deleted_users;
```
### Group & Having
```go
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
```go
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)
// multiple joins with parameter
db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user)
```
### Pluck
Get selected attributes as map
```go
var ages []int64
db.Find(&users).Pluck("age", &ages)
var names []string
db.Model(&User{}).Pluck("name", &names)
db.Table("deleted_users").Pluck("name", &names)
// Requesting more than one column? Do it like this:
db.Select("name, age").Find(&users)
```
### Scan
Scan results into another struct.
```go
type Result struct {
Name string
Age int
}
var result Result
db.Table("users").Select("name, age").Where("name = ?", 3).Scan(&result)
// Raw SQL
db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)
```
### Scopes
```go
func AmountGreaterThan1000(db *gorm.DB) *gorm.DB {
return db.Where("amount > ?", 1000)
}
func PaidWithCreditCard(db *gorm.DB) *gorm.DB {
return db.Where("pay_mode_sign = ?", "C")
}
func PaidWithCod(db *gorm.DB) *gorm.DB {
return db.Where("pay_mode_sign = ?", "C")
}
func OrderStatus(status []string) func (db *gorm.DB) *gorm.DB {
return func (db *gorm.DB) *gorm.DB {
return db.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
```
### Specifying The Table Name
```go
// Create `deleted_users` table with struct User's definition
db.Table("deleted_users").CreateTable(&User{})
var deleted_users []User
db.Table("deleted_users").Find(&deleted_users)
//// SELECT * FROM deleted_users;
db.Table("deleted_users").Where("name = ?", "jinzhu").Delete()
//// DELETE FROM deleted_users WHERE name = 'jinzhu';
```
## Update
### Update All Fields
`Save` will include all fields when perform the Updating SQL, even it is not changed
```go
db.First(&user)
user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)
//// UPDATE users SET name='jinzhu 2', age=100, birthday='2016-01-01', updated_at = '2013-11-17 21:34:10' WHERE id=111;
```
### Update Changed Fields
If you only want to update changed Fields, you could use `Update`, `Updates`
```go
// Update single attribute 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;
// Update single attribute with combined conditions
db.Model(&user).Where("active = ?", true).Update("name", "hello")
//// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;
// Update multiple attributes with `map`, will only update those changed fields
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
//// UPDATE users SET name='hello', age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
// Update multiple attributes with `struct`, will only update those changed & non blank fields
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;
// WARNING when update with struct, GORM will only update those fields that with non blank value
// For below Update, nothing will be updated as "", 0, false are blank values of their types
db.Model(&user).Updates(User{Name: "", Age: 0, Actived: false})
```
### Update Selected Fields
If you only want to update or ignore some fields when updating, you could use `Select`, `Omit`
```go
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
//// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;
db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
//// UPDATE users SET age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
```
### Update Changed Fields Without Callbacks
Updating operations above will invoke `BeforeUpdate`, `AfterUpdate`, Update UpdatedAt timestamp, Save Associations callbacks, if you don't call them, you could use `UpdateColumn`, `UpdateColumns`
```go
// Update single attribute, similar with `Update`
db.Model(&user).UpdateColumn("name", "hello")
//// UPDATE users SET name='hello' WHERE id = 111;
// Update multiple attributes, similar with `Updates`
db.Model(&user).UpdateColumns(User{Name: "hello", Age: 18})
//// UPDATE users SET name='hello', age=18 WHERE id = 111;
```
### Batch Updates
Callbacks won't run when do batch updates
```go
db.Table("users").Where("id IN (?)", []int{10, 11}).Updates(map[string]interface{}{"name": "hello", "age": 18})
//// UPDATE users SET name='hello', age=18 WHERE id IN (10, 11);
// Update with struct only works with none zero values, or use map[string]interface{}
db.Model(User{}).Updates(User{Name: "hello", Age: 18})
//// UPDATE users SET name='hello', age=18;
// Get updated records count with `RowsAffected`
db.Model(User{}).Updates(User{Name: "hello", Age: 18}).RowsAffected
```
### Update with SQL Expression
```go
DB.Model(&product).Update("price", gorm.Expr("price * ? + ?", 2, 100))
//// UPDATE "products" SET "price" = price * '2' + '100', "updated_at" = '2013-11-17 21:34:10' WHERE "id" = '2';
DB.Model(&product).Updates(map[string]interface{}{"price": gorm.Expr("price * ? + ?", 2, 100)})
//// UPDATE "products" SET "price" = price * '2' + '100', "updated_at" = '2013-11-17 21:34:10' WHERE "id" = '2';
DB.Model(&product).UpdateColumn("quantity", gorm.Expr("quantity - ?", 1))
//// UPDATE "products" SET "quantity" = quantity - 1 WHERE "id" = '2';
DB.Model(&product).Where("quantity > 1").UpdateColumn("quantity", gorm.Expr("quantity - ?", 1))
//// UPDATE "products" SET "quantity" = quantity - 1 WHERE "id" = '2' AND quantity > 1;
```
### Change Updating Values In Callbacks
If you want to change updating values in callbacks using `BeforeUpdate`, `BeforeSave`, you could use `scope.SetColumn`, for example:
```go
func (user *User) BeforeSave(scope *gorm.Scope) (err error) {
if pw, err := bcrypt.GenerateFromPassword(user.Password, 0); err == nil {
scope.SetColumn("EncryptedPassword", pw)
}
}
```
### Extra Updating option
```go
// Add extra SQL option for updating SQL
db.Model(&user).Set("gorm:update_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Update("name, "hello")
//// UPDATE users SET name='hello', updated_at = '2013-11-17 21:34:10' WHERE id=111 OPTION (OPTIMIZE FOR UNKNOWN);
```
## Delete
```go
// Delete an existing record
db.Delete(&email)
//// DELETE from emails where id=10;
// Add extra SQL option for deleting SQL
db.Set("gorm:delete_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Delete(&email)
//// DELETE from emails where id=10 OPTION (OPTIMIZE FOR UNKNOWN);
```
### Batch Delete
```go
db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
//// DELETE from emails where email LIKE "%jinhu%";
```
### Soft Delete
If struct has `DeletedAt` field, it will get soft delete ability automatically!
Then it won't be deleted from database permanently when call `Delete`.
```go
db.Delete(&user)
//// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;
// Batch Delete
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 query them
db.Where("age = 20").Find(&user)
//// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;
// 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;
```

156
documents/database.md Normal file
View File

@ -0,0 +1,156 @@
# Database
<!-- toc -->
## Connecting to a database
#### MySQL
**NOTE** don't forgot params `parseTime` to handle data type `time.Time`, [more support parameters](https://github.com/go-sql-driver/mysql#parameters)
```go
import (
"github.com/jinzhu/gorm"
_ "github.com/go-sql-driver/mysql"
)
func main() {
db, err := gorm.Open("mysql", "user:password@/dbname?charset=utf8&parseTime=True&loc=Local")
}
```
#### PostgreSQL
```go
import (
"github.com/jinzhu/gorm"
_ "github.com/lib/pq"
)
func main() {
db, err := gorm.Open("postgres", "user=gorm dbname=gorm sslmode=disable")
}
```
#### Sqlite3
```go
import (
"github.com/jinzhu/gorm"
_ "github.com/mattn/go-sqlite3"
)
func main() {
db, err := gorm.Open("sqlite3", "/tmp/gorm.db")
}
```
#### Write Dialect for unsupported databases
GORM officially support above databases, for unsupported databaes, you could write a dialect for that.
Refer: https://github.com/jinzhu/gorm/blob/master/dialect.go
## Generic database object *sql.DB
[*sql.DB](http://golang.org/pkg/database/sql/#DB)
```go
// Get generic database object *sql.DB to use its functions
db.DB()
// Connection Pool
db.DB().SetMaxIdleConns(10)
db.DB().SetMaxOpenConns(100)
// Ping
db.DB().Ping()
```
## Migration
<!-- toc -->
### Auto Migration
Automatically migrate your schema, to keep your schema update to date
**WARNING** AutoMigrate will ONLY create tables, columns and indexes if doesn't exist,
WON'T change existing column's type or delete unused columns to protect your data
```go
db.AutoMigrate(&User{})
db.AutoMigrate(&User{}, &Product{}, &Order{})
// Add table suffix when create tables
db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User{})
```
### Has Table
```go
// Check if model `User`'s table has been created or not
db.HasTable(&User{})
// Check table `users` exists or not
db.HasTable("users")
```
### Create Table
```go
db.CreateTable(&User{})
db.Set("gorm:table_options", "ENGINE=InnoDB").CreateTable(&User{})
// will append "ENGINE=InnoDB" to the SQL statement when creating table `users`
```
### Drop table
```go
db.DropTable(&User{})
```
### ModifyColumn
Change column's type
```go
// change column description's data type to `text` for model `User`'s table
db.Model(&User{}).ModifyColumn("description", "text")
```
### DropColumn
```go
db.Model(&User{}).DropColumn("description")
```
### Add Foreign Key
```go
// Add foreign key
// 1st param : foreignkey field
// 2nd param : destination table(id)
// 3rd param : ONDELETE
// 4th param : ONUPDATE
db.Model(&User{}).AddForeignKey("city_id", "cities(id)", "RESTRICT", "RESTRICT")
```
### Indexes
```go
// Add index
db.Model(&User{}).AddIndex("idx_user_name", "name")
// Multiple column index
db.Model(&User{}).AddIndex("idx_user_name_age", "name", "age")
// Add unique index
db.Model(&User{}).AddUniqueIndex("idx_user_name", "name")
// Multiple column unique index
db.Model(&User{}).AddUniqueIndex("idx_user_name_age", "name", "age")
// Remove index
db.Model(&User{}).RemoveIndex("idx_user_name")
```

70
documents/development.md Normal file
View File

@ -0,0 +1,70 @@
# Gorm Development
<!-- toc -->
## Architecture
The most notable component of Gorm is`gorm.DB`, which hold database connection. It could be initialized like this:
db, err := gorm.Open("postgres", "user=gorm dbname=gorm sslmode=disable")
Gorm has chainable API, `gorm.DB` is the bridge of chains, it save related information and pass it to the next chain.
Lets use below code to explain how it works:
db.Where("name = ?", "jinzhu").Find(&users)
// equivalent code
newdb := db.Where("name =?", "jinzhu")
newdb.Find(&user)
`newdb` is `db`'s clone, in addition, it contains search conditions from the `Where` method.
`Find` is a query method, it creates a `Scope` instance, and pass it as argument to query callbacks.
There are four kinds of callbacks corresponds to sql's CURD: create callbacks, update callbacks, query callbacks, delete callbacks.
## Callbacks
### Register a new callback
func updateCreated(scope *Scope) {
if scope.HasColumn("Created") {
scope.SetColumn("Created", NowFunc())
}
}
db.Callback().Create().Register("update_created_at", updateCreated)
// register a callback for Create process
### Delete an existing callback
db.Callback().Create().Remove("gorm:create")
// delete callback `gorm:create` from Create callbacks
### Replace an existing callback
db.Callback().Create().Replace("gorm:create", newCreateFunction)
// replace callback `gorm:create` with new function `newCreateFunction` for Create process
### Register callback orders
db.Callback().Create().Before("gorm:create").Register("update_created_at", updateCreated)
db.Callback().Create().After("gorm:create").Register("update_created_at", updateCreated)
db.Callback().Query().After("gorm:query").Register("my_plugin:after_query", afterQuery)
db.Callback().Delete().After("gorm:delete").Register("my_plugin:after_delete", afterDelete)
db.Callback().Update().Before("gorm:update").Register("my_plugin:before_update", beforeUpdate)
db.Callback().Create().Before("gorm:create").After("gorm:before_create").Register("my_plugin:before_create", beforeCreate)
### Callback API
Gorm is powered by callbacks, so you could refer below links to learn how to write callbacks
[Create callbacks](https://github.com/jinzhu/gorm/blob/master/callback_create.go)
[Update callbacks](https://github.com/jinzhu/gorm/blob/master/callback_update.go)
[Query callbacks](https://github.com/jinzhu/gorm/blob/master/callback_query.go)
[Delete callbacks](https://github.com/jinzhu/gorm/blob/master/callback_delete.go)
View [https://github.com/jinzhu/gorm/blob/master/scope.go](https://github.com/jinzhu/gorm/blob/master/scope.go) for all available API

BIN
documents/logger.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

111
documents/models.md Normal file
View File

@ -0,0 +1,111 @@
# Models
<!-- toc -->
## Model Defination
```go
type User struct {
gorm.Model
Birthday time.Time
Age int
Name string `sql:"size:255"` // Default size for string is 255, you could reset it with this tag
Num int `sql:"AUTO_INCREMENT"`
IgnoreMe int `sql:"-"` // Ignore this field
}
```
## Conventions & Overriding Conventions
### `gorm.Model` struct
Gorm has defined struct `gorm.Model`, which could be embeded in your models, it will add fields `ID`, `CreatedAt`, `UpdatedAt`, `DeletedAt` to your model
```go
// Model's definition
type Model struct {
ID uint `gorm:"primary_key"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
}
```
### Table name is the pluralized version of struct name
```go
type User struct {} // default table name is `users`
// set User's table name to be `profiles
type (User) TableName() string {
return "profiles"
}
func (u User) TableName() string {
if u.Role == "admin" {
return "admin_users"
} else {
return "users"
}
}
// Disable table name's pluralization globally
db.SingularTable(true) // if set this to true, `User`'s default table name will be `user`, table name setted with `TableName` won't be affected
```
### Column name is the snake case of field's name
```go
type User struct {
ID uint // column name will be `id`
Name string // column name will be `name`
Birthday time.Time // column name will be `birthday`
CreatedAt time.Time // column name will be `created_at`
}
type Animal struct {
AnimalId int64 `gorm:"column:beast_id"` // set column name to `beast_id`
Birthday time.Time `gorm:"column:day_of_the_beast"` // set column name to `day_of_the_beast`
Age int64 `gorm:"column:age_of_the_beast"` // set column name to `age_of_the_beast`
}
```
### Field `ID` as primary key
```go
type User struct {
ID uint // field named `ID` is the default primary key for `User`
Name string
}
// your could also use tag `primary_key` to set other field as primary key
type Animal struct {
AnimalId int64 `gorm:"primary_key"` // set AnimalId to be primary key
Name string
Age int64
}
```
### Field `CreatedAt` used to store record's created time
Create records having `CreatedAt` field will set it to current time.
```go
db.Create(&user) // will set `CreatedAt` to current time
// To change its value, you could use `Update`
db.Model(&user).Update("CreatedAt", time.Now())
```
### Use `UpdatedAt` used to store record's updated time
Save records having `UpdatedAt` field will set it to current time.
```go
db.Save(&user) // will set `UpdatedAt` to current time
db.Model(&user).Update("name", "jinzhu") // will set `UpdatedAt` to current time
```
### Use `DeletedAt` to store record's deleted time if field exists
Delete records having `DeletedAt` field, it won't delete the record from database, but will set field `DeletedAt`'s value to current time.

View File

@ -27,14 +27,38 @@ $ npm install gitbook-cli -g
`gitbook-cli` is an utility to install and use multiple versions of GitBook on the same system. It will automatically install the required version to build a book. `gitbook-cli` is an utility to install and use multiple versions of GitBook on the same system. It will automatically install the required version to build a book.
##### Using pre-releases ##### Create a book
GitBook can setup a boilerplate book:
```
$ gitbook init
```
If you wish to create the book into a new directory, you can do so by running `gitbook init ./directory`
Preview and serve your book using:
```
$ gitbook serve
```
or build the static website using:
```
$ gitbook build
```
##### Install pre-releases
`gitbook-cli` makes it easy to install and test other versions of GitBook with your book: `gitbook-cli` makes it easy to install and test other versions of GitBook with your book:
``` ```
$ gitbook install beta $ gitbook fetch beta
``` ```
Use `gitbook ls-remote` to list remote versions available for install.
##### Debugging ##### Debugging
You can use the options `--log=debug` and `--debug` to get better error messages (with stack trace). For example: You can use the options `--log=debug` and `--debug` to get better error messages (with stack trace). For example:

View File

@ -0,0 +1,12 @@
require(["gitbook"], function(gitbook) {
gitbook.events.bind("page.change", function() {
// For Anker tag link
if (location.hash) {
// deley for other event
setTimeout(function(){
// wait for loading
document.location = location.hash;
}, 300);
}
});
});

View File

@ -1,11 +0,0 @@
(function() {
var newEl = document.createElement('script'),
firstScriptTag = document.getElementsByTagName('script')[0];
if (firstScriptTag) {
newEl.async = 1;
newEl.src = '//' + window.location.hostname + ':35729/livereload.js';
firstScriptTag.parentNode.insertBefore(newEl, firstScriptTag);
}
})();

View File

@ -68,7 +68,7 @@
data-chapter-title="Getting Started with GORM" data-chapter-title="Getting Started with GORM"
data-filepath="README.md" data-filepath="README.md"
data-basepath="" data-basepath=""
data-revision="Fri Feb 26 2016 21:50:14 GMT+0800 (CST)" data-revision="Sun Feb 28 2016 16:13:20 GMT+0800 (CST)"
data-innerlanguage=""> data-innerlanguage="">
@ -551,17 +551,12 @@
<li class="divider"></li> <li class="divider"></li>
<li>
<a href="https://www.gitbook.com" target="blank" class="gitbook-link">
Published with GitBook
</a>
</li>
</ul> </ul>
</nav> </nav>
</div> </div>
<div class="book-body"> <div class="book-body">
<div class="body-inner"> <div class="body-inner">
@ -675,6 +670,10 @@
<script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
<script src="gitbook/gitbook-plugin-search/lunr.min.js"></script> <script src="gitbook/gitbook-plugin-search/lunr.min.js"></script>
@ -690,13 +689,9 @@
<script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script> <script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script>
<script src="gitbook/gitbook-plugin-livereload/plugin.js"></script>
<script> <script>
require(["gitbook"], function(gitbook) { require(["gitbook"], function(gitbook) {
gitbook.start({"toc":{"addClass":true,"className":"toc"},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"base":"https://github.com/jinzhu/gorm/edit/master","label":"Edit This Page"},"prism":{},"search":{"maxIndexSize":1000000},"sharing":{"all":["facebook","google","instapaper","twitter","weibo"],"facebook":true,"google":false,"instapaper":false,"twitter":true,"vk":false,"weibo":false},"fontsettings":{"family":"sans","size":2,"theme":"white"},"theme-default":{"styles":{"ebook":"styles/ebook.css","epub":"styles/epub.css","mobi":"styles/mobi.css","pdf":"styles/pdf.css","print":"styles/print.css","website":"styles/website.css"}},"livereload":{}}); gitbook.start({"fontsettings":{"theme":"night","family":"sans","size":4},"toc":{"addClass":true,"className":"toc"},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"base":"https://github.com/jinzhu/gorm/edit/master","label":"Edit This Page"},"prism":{},"anker-enable":{},"search":{"maxIndexSize":1000000},"sharing":{"all":["facebook","google","instapaper","twitter","weibo"],"facebook":true,"google":false,"instapaper":false,"twitter":true,"vk":false,"weibo":false},"theme-default":{"styles":{"ebook":"styles/ebook.css","epub":"styles/epub.css","mobi":"styles/mobi.css","pdf":"styles/pdf.css","print":"styles/print.css","website":"styles/website.css"}}});
}); });
</script> </script>

View File

@ -70,7 +70,7 @@
data-chapter-title="Models" data-chapter-title="Models"
data-filepath="models.md" data-filepath="models.md"
data-basepath="" data-basepath=""
data-revision="Fri Feb 26 2016 21:50:14 GMT+0800 (CST)" data-revision="Sun Feb 28 2016 16:13:20 GMT+0800 (CST)"
data-innerlanguage=""> data-innerlanguage="">
@ -553,17 +553,12 @@
<li class="divider"></li> <li class="divider"></li>
<li>
<a href="https://www.gitbook.com" target="blank" class="gitbook-link">
Published with GitBook
</a>
</li>
</ul> </ul>
</nav> </nav>
</div> </div>
<div class="book-body"> <div class="book-body">
<div class="body-inner"> <div class="body-inner">
@ -718,6 +713,10 @@ db<span class="token operator">.</span><span class="token function">Model<span c
<script src="gitbook/gitbook-plugin-anker-enable/anker.js"></script>
<script src="gitbook/gitbook-plugin-search/lunr.min.js"></script> <script src="gitbook/gitbook-plugin-search/lunr.min.js"></script>
@ -733,13 +732,9 @@ db<span class="token operator">.</span><span class="token function">Model<span c
<script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script> <script src="gitbook/gitbook-plugin-fontsettings/buttons.js"></script>
<script src="gitbook/gitbook-plugin-livereload/plugin.js"></script>
<script> <script>
require(["gitbook"], function(gitbook) { require(["gitbook"], function(gitbook) {
gitbook.start({"toc":{"addClass":true,"className":"toc"},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"base":"https://github.com/jinzhu/gorm/edit/master","label":"Edit This Page"},"prism":{},"search":{"maxIndexSize":1000000},"sharing":{"all":["facebook","google","instapaper","twitter","weibo"],"facebook":true,"google":false,"instapaper":false,"twitter":true,"vk":false,"weibo":false},"fontsettings":{"family":"sans","size":2,"theme":"white"},"theme-default":{"styles":{"ebook":"styles/ebook.css","epub":"styles/epub.css","mobi":"styles/mobi.css","pdf":"styles/pdf.css","print":"styles/print.css","website":"styles/website.css"}},"livereload":{}}); gitbook.start({"fontsettings":{"theme":"night","family":"sans","size":4},"toc":{"addClass":true,"className":"toc"},"github":{"url":"https://github.com/jinzhu/gorm"},"edit-link":{"base":"https://github.com/jinzhu/gorm/edit/master","label":"Edit This Page"},"prism":{},"anker-enable":{},"search":{"maxIndexSize":1000000},"sharing":{"all":["facebook","google","instapaper","twitter","weibo"],"facebook":true,"google":false,"instapaper":false,"twitter":true,"vk":false,"weibo":false},"theme-default":{"styles":{"ebook":"styles/ebook.css","epub":"styles/epub.css","mobi":"styles/mobi.css","pdf":"styles/pdf.css","print":"styles/print.css","website":"styles/website.css"}}});
}); });
</script> </script>