Repository: gobuffalo/pop
Pull request #617 (2021-01-27 20:42:15)

Latest from development
Some fixes and version bump of the Postgres libraries.


Patch:
diff --git a/belongs_to.go b/belongs_to.go
index 0b5c977..d261a31 100644
--- a/belongs_to.go
+++ b/belongs_to.go
@@ -19,7 +19,7 @@ func (c *Connection) BelongsToAs(model interface{}, as string) *Query {
 // BelongsTo adds a "where" clause based on the "ID" of the
 // "model" passed into it.
 func (q *Query) BelongsTo(model interface{}) *Query {
-	m := &Model{Value: model}
+	m := NewModel(model, q.Connection.Context())
 	q.Where(fmt.Sprintf("%s = ?", m.associationName()), m.ID())
 	return q
 }
@@ -27,7 +27,7 @@ func (q *Query) BelongsTo(model interface{}) *Query {
 // BelongsToAs adds a "where" clause based on the "ID" of the
 // "model" passed into it, using an alias.
 func (q *Query) BelongsToAs(model interface{}, as string) *Query {
-	m := &Model{Value: model}
+	m := NewModel(model, q.Connection.Context())
 	q.Where(fmt.Sprintf("%s = ?", as), m.ID())
 	return q
 }
@@ -42,8 +42,8 @@ func (c *Connection) BelongsToThrough(bt, thru interface{}) *Query {
 // through the associated "thru" model.
 func (q *Query) BelongsToThrough(bt, thru interface{}) *Query {
 	q.belongsToThroughClauses = append(q.belongsToThroughClauses, belongsToThroughClause{
-		BelongsTo: &Model{Value: bt},
-		Through:   &Model{Value: thru},
+		BelongsTo: NewModel(bt, q.Connection.Context()),
+		Through:   NewModel(thru, q.Connection.Context()),
 	})
 	return q
 }
diff --git a/columns/columns.go b/columns/columns.go
index 8ee8f15..4ea2656 100644
--- a/columns/columns.go
+++ b/columns/columns.go
@@ -13,6 +13,7 @@ type Columns struct {
 	lock       *sync.RWMutex
 	TableName  string
 	TableAlias string
+	IDField    string
 }
 
 // Add a column to the list.
@@ -74,7 +75,7 @@ func (c *Columns) Add(names ...string) []*Column {
 				} else if xs[1] == "w" {
 					col.Readable = false
 				}
-			} else if col.Name == "id" {
+			} else if col.Name == c.IDField {
 				col.Writeable = false
 			}
 
@@ -98,7 +99,7 @@ func (c *Columns) Remove(names ...string) {
 
 // Writeable gets a list of the writeable columns from the column list.
 func (c Columns) Writeable() *WriteableColumns {
-	w := &WriteableColumns{NewColumnsWithAlias(c.TableName, c.TableAlias)}
+	w := &WriteableColumns{NewColumnsWithAlias(c.TableName, c.TableAlias, c.IDField)}
 	for _, col := range c.Cols {
 		if col.Writeable {
 			w.Cols[col.Name] = col
@@ -109,7 +110,7 @@ func (c Columns) Writeable() *WriteableColumns {
 
 // Readable gets a list of the readable columns from the column list.
 func (c Columns) Readable() *ReadableColumns {
-	w := &ReadableColumns{NewColumnsWithAlias(c.TableName, c.TableAlias)}
+	w := &ReadableColumns{NewColumnsWithAlias(c.TableName, c.TableAlias, c.IDField)}
 	for _, col := range c.Cols {
 		if col.Readable {
 			w.Cols[col.Name] = col
@@ -157,17 +158,18 @@ func (c Columns) SymbolizedString() string {
 }
 
 // NewColumns constructs a list of columns for a given table name.
-func NewColumns(tableName string) Columns {
-	return NewColumnsWithAlias(tableName, "")
+func NewColumns(tableName, idField string) Columns {
+	return NewColumnsWithAlias(tableName, "", idField)
 }
 
 // NewColumnsWithAlias constructs a list of columns for a given table
 // name, using a given alias for the table.
-func NewColumnsWithAlias(tableName string, tableAlias string) Columns {
+func NewColumnsWithAlias(tableName, tableAlias, idField string) Columns {
 	return Columns{
 		lock:       &sync.RWMutex{},
 		Cols:       map[string]*Column{},
 		TableName:  tableName,
 		TableAlias: tableAlias,
+		IDField:    idField,
 	}
 }
diff --git a/columns/columns_for_struct.go b/columns/columns_for_struct.go
index 22cdbeb..a20cd42 100644
--- a/columns/columns_for_struct.go
+++ b/columns/columns_for_struct.go
@@ -6,17 +6,17 @@ import (
 
 // ForStruct returns a Columns instance for
 // the struct passed in.
-func ForStruct(s interface{}, tableName string) (columns Columns) {
-	return ForStructWithAlias(s, tableName, "")
+func ForStruct(s interface{}, tableName, idField string) (columns Columns) {
+	return ForStructWithAlias(s, tableName, "", idField)
 }
 
 // ForStructWithAlias returns a Columns instance for the struct passed in.
 // If the tableAlias is not empty, it will be used.
-func ForStructWithAlias(s interface{}, tableName string, tableAlias string) (columns Columns) {
-	columns = NewColumnsWithAlias(tableName, tableAlias)
+func ForStructWithAlias(s interface{}, tableName, tableAlias, idField string) (columns Columns) {
+	columns = NewColumnsWithAlias(tableName, tableAlias, idField)
 	defer func() {
 		if r := recover(); r != nil {
-			columns = NewColumnsWithAlias(tableName, tableAlias)
+			columns = NewColumnsWithAlias(tableName, tableAlias, idField)
 			columns.Add("*")
 		}
 	}()
diff --git a/connection.go b/connection.go
index df116fb..6e0f55e 100644
--- a/connection.go
+++ b/connection.go
@@ -33,6 +33,16 @@ func (c *Connection) URL() string {
 	return c.Dialect.URL()
 }
 
+// Context returns the connection's context set by "Context()" or context.TODO()
+// if no context is set.
+func (c *Connection) Context() context.Context {
+	if c, ok := c.Store.(interface{ Context() context.Context }); ok {
+		return c.Context()
+	}
+
+	return context.TODO()
+}
+
 // MigrationURL returns the datasource connection string used for running the migrations
 func (c *Connection) MigrationURL() string {
 	return c.Dialect.MigrationURL()
diff --git a/connection_details.go b/connection_details.go
index 76c165b..6456b7d 100644
--- a/connection_details.go
+++ b/connection_details.go
@@ -2,13 +2,14 @@ package pop
 
 import (
 	"fmt"
-	"github.com/luna-duclos/instrumentedsql"
 	"net/url"
 	"regexp"
 	"strconv"
 	"strings"
 	"time"
 
+	"github.com/luna-duclos/instrumentedsql"
+
 	"github.com/gobuffalo/pop/v5/internal/defaults"
 	"github.com/gobuffalo/pop/v5/logging"
 	"github.com/pkg/errors"
@@ -179,6 +180,10 @@ func (cd *ConnectionDetails) OptionsString(s string) string {
 	}
 	if cd.Options != nil {
 		for k, v := range cd.Options {
+			if k == "migration_table_name" {
+				continue
+			}
+
 			s = fmt.Sprintf("%s&%s=%s", s, k, v)
 		}
 	}
diff --git a/connection_instrumented.go b/connection_instrumented.go
index 84cf782..f2d46b0 100644
--- a/connection_instrumented.go
+++ b/connection_instrumented.go
@@ -3,6 +3,7 @@ package pop
 import (
 	"database/sql"
 	"database/sql/driver"
+
 	mysqld "github.com/go-sql-driver/mysql"
 	"github.com/gobuffalo/pop/v5/logging"
 	pgx "github.com/jackc/pgx/v4/stdlib"
diff --git a/dialect_sqlite.go b/dialect_sqlite.go
index b304509..47bc3e2 100644
--- a/dialect_sqlite.go
+++ b/dialect_sqlite.go
@@ -5,7 +5,6 @@ package pop
 import (
 	"database/sql/driver"
 	"fmt"
-	"github.com/mattn/go-sqlite3"
 	"io"
 	"net/url"
 	"os"
@@ -15,6 +14,8 @@ import (
 	"sync"
 	"time"
 
+	"github.com/mattn/go-sqlite3"
+
 	"github.com/gobuffalo/fizz"
 	"github.com/gobuffalo/fizz/translators"
 	_ "github.com/mattn/go-sqlite3" // Load SQLite3 CGo driver
diff --git a/executors.go b/executors.go
index 9704c91..657ecea 100644
--- a/executors.go
+++ b/executors.go
@@ -13,7 +13,7 @@ import (
 
 // Reload fetch fresh data for a given model, using its ID.
 func (c *Connection) Reload(model interface{}) error {
-	sm := Model{Value: model}
+	sm := NewModel(model, c.Context())
 	return sm.iterate(func(m *Model) error {
 		return c.Find(m.Value, m.ID())
 	})
@@ -51,7 +51,7 @@ func (q *Query) ExecWithCount() (int, error) {
 //
 // If model is a slice, each item of the slice is validated then saved in the database.
 func (c *Connection) ValidateAndSave(model interface{}, excludeColumns ...string) (*validate.Errors, error) {
-	sm := &Model{Value: model}
+	sm := NewModel(model, c.Context())
 	if err := sm.beforeValidate(c); err != nil {
 		return nil, err
 	}
@@ -77,7 +77,7 @@ func IsZeroOfUnderlyingType(x interface{}) bool {
 //
 // If model is a slice, each item of the slice is saved in the database.
 func (c *Connection) Save(model interface{}, excludeColumns ...string) error {
-	sm := &Model{Value: model}
+	sm := NewModel(model, c.Context())
 	return sm.iterate(func(m *Model) error {
 		id, err := m.fieldByName("ID")
 		if err != nil {
@@ -95,7 +95,7 @@ func (c *Connection) Save(model interface{}, excludeColumns ...string) error {
 //
 // If model is a slice, each item of the slice is validated then created in the database.
 func (c *Connection) ValidateAndCreate(model interface{}, excludeColumns ...string) (*validate.Errors, error) {
-	sm := &Model{Value: model}
+	sm := NewModel(model, c.Context())
 	if err := sm.beforeValidate(c); err != nil {
 		return nil, err
 	}
@@ -126,7 +126,7 @@ func (c *Connection) ValidateAndCreate(model interface{}, excludeColumns ...stri
 				continue
 			}
 
-			sm := &Model{Value: i}
+			sm := NewModel(i, c.Context())
 			verrs, err := sm.validateAndOnlyCreate(c)
 			if err != nil || verrs.HasAny() {
 				return verrs, err
@@ -140,14 +140,14 @@ func (c *Connection) ValidateAndCreate(model interface{}, excludeColumns ...stri
 				continue
 			}
 
-			sm := &Model{Value: i}
+			sm := NewModel(i, c.Context())
 			verrs, err := sm.validateAndOnlyCreate(c)
 			if err != nil || verrs.HasAny() {
 				return verrs, err
 			}
 		}
 
-		sm := &Model{Value: model}
+		sm := NewModel(model, c.Context())
 		verrs, err = sm.validateCreate(c)
 		if err != nil || verrs.HasAny() {
 			return verrs, err
@@ -170,7 +170,7 @@ func (c *Connection) Create(model interface{}, excludeColumns ...string) error {
 
 	c.disableEager()
 
-	sm := &Model{Value: model}
+	sm := NewModel(model, c.Context())
 	return sm.iterate(func(m *Model) error {
 		return c.timeFunc("Create", func() error {
 			var localIsEager = isEager
@@ -203,7 +203,7 @@ func (c *Connection) Create(model interface{}, excludeColumns ...string) error {
 					}
 
 					if localIsEager {
-						sm := &Model{Value: i}
+						sm := NewModel(i, c.Context())
 						err = sm.iterate(func(m *Model) error {
 							id, err := m.fieldByName("ID")
 							if err != nil {
@@ -228,7 +228,7 @@ func (c *Connection) Create(model interface{}, excludeColumns ...string) error {
 			}
 
 			tn := m.TableName()
-			cols := columns.ForStructWithAlias(m.Value, tn, m.As)
+			cols := m.Columns()
 
 			if tn == sm.TableName() {
 				cols.Remove(excludeColumns...)
@@ -255,7 +255,7 @@ func (c *Connection) Create(model interface{}, excludeColumns ...string) error {
 							continue
 						}
 
-						sm := &Model{Value: i}
+						sm := NewModel(i, c.Context())
 						err = sm.iterate(func(m *Model) error {
 							fbn, err := m.fieldByName("ID")
 							if err != nil {
@@ -318,7 +318,7 @@ func (c *Connection) Create(model interface{}, excludeColumns ...string) error {
 //
 // If model is a slice, each item of the slice is validated then updated in the database.
 func (c *Connection) ValidateAndUpdate(model interface{}, excludeColumns ...string) (*validate.Errors, error) {
-	sm := &Model{Value: model}
+	sm := NewModel(model, c.Context())
 	if err := sm.beforeValidate(c); err != nil {
 		return nil, err
 	}
@@ -337,7 +337,7 @@ func (c *Connection) ValidateAndUpdate(model interface{}, excludeColumns ...stri
 //
 // If model is a slice, each item of the slice is updated in the database.
 func (c *Connection) Update(model interface{}, excludeColumns ...string) error {
-	sm := &Model{Value: model}
+	sm := NewModel(model, c.Context())
 	return sm.iterate(func(m *Model) error {
 		return c.timeFunc("Update", func() error {
 			var err error
@@ -350,8 +350,8 @@ func (c *Connection) Update(model interface{}, excludeColumns ...string) error {
 			}
 
 			tn := m.TableName()
-			cols := columns.ForStructWithAlias(model, tn, m.As)
-			cols.Remove("id", "created_at")
+			cols := columns.ForStructWithAlias(model, tn, m.As, m.IDField())
+			cols.Remove(m.IDField(), "created_at")
 
 			if tn == sm.TableName() {
 				cols.Remove(excludeColumns...)
@@ -377,7 +377,7 @@ func (c *Connection) Update(model interface{}, excludeColumns ...string) error {
 //
 // If model is a slice, each item of the slice is updated in the database.
 func (c *Connection) UpdateColumns(model interface{}, columnNames ...string) error {
-	sm := &Model{Value: model}
+	sm := NewModel(model, c.Context())
 	return sm.iterate(func(m *Model) error {
 		return c.timeFunc("Update", func() error {
 			var err error
@@ -393,11 +393,11 @@ func (c *Connection) UpdateColumns(model interface{}, columnNames ...string) err
 
 			cols := columns.Columns{}
 			if len(columnNames) > 0 && tn == sm.TableName() {
-				cols = columns.NewColumnsWithAlias(tn, m.As)
+				cols = columns.NewColumnsWithAlias(tn, m.As, sm.IDField())
 				cols.Add(columnNames...)
 
 			} else {
-				cols = columns.ForStructWithAlias(model, tn, m.As)
+				cols = columns.ForStructWithAlias(model, tn, m.As, m.IDField())
 			}
 			cols.Remove("id", "created_at")
 
@@ -419,7 +419,7 @@ func (c *Connection) UpdateColumns(model interface{}, columnNames ...string) err
 //
 // If model is a slice, each item of the slice is deleted from the database.
 func (c *Connection) Destroy(model interface{}) error {
-	sm := &Model{Value: model}
+	sm := NewModel(model, c.Context())
 	return sm.iterate(func(m *Model) error {
 		return c.timeFunc("Destroy", func() error {
 			var err error
diff --git a/finders.go b/finders.go
index 9026d14..fa1a220 100644
--- a/finders.go
+++ b/finders.go
@@ -29,7 +29,7 @@ func (c *Connection) Find(model interface{}, id interface{}) error {
 //
 //	q.Find(&User{}, 1)
 func (q *Query) Find(model interface{}, id interface{}) error {
-	m := &Model{Value: model}
+	m := NewModel(model, q.Connection.Context())
 	idq := m.whereID()
 	switch t := id.(type) {
 	case uuid.UUID:
@@ -69,7 +69,7 @@ func (c *Connection) First(model interface{}) error {
 func (q *Query) First(model interface{}) error {
 	err := q.Connection.timeFunc("First", func() error {
 		q.Limit(1)
-		m := &Model{Value: model}
+		m := NewModel(model, q.Connection.Context())
 		if err := q.Connection.Dialect.SelectOne(q.Connection.Store, m, *q); err != nil {
 			return err
 		}
@@ -102,7 +102,7 @@ func (q *Query) Last(model interface{}) error {
 	err := q.Connection.timeFunc("Last", func() error {
 		q.Limit(1)
 		q.Order("created_at DESC, id DESC")
-		m := &Model{Value: model}
+		m := NewModel(model, q.Connection.Context())
 		if err := q.Connection.Dialect.SelectOne(q.Connection.Store, m, *q); err != nil {
 			return err
 		}
@@ -134,7 +134,7 @@ func (c *Connection) All(models interface{}) error {
 //	q.Where("name = ?", "mark").All(&[]User{})
 func (q *Query) All(models interface{}) error {
 	err := q.Connection.timeFunc("All", func() error {
-		m := &Model{Value: models}
+		m := NewModel(models, q.Connection.Context())
 		err := q.Connection.Dialect.SelectMany(q.Connection.Store, m, *q)
 		if err != nil {
 			return err
@@ -258,7 +258,7 @@ func (q *Query) eagerDefaultAssociations(model interface{}) error {
 			}
 		}
 
-		sqlSentence, args := query.ToSQL(&Model{Value: association.Interface()})
+		sqlSentence, args := query.ToSQL(NewModel(association.Interface(), query.Connection.Context()))
 		query = query.RawQuery(sqlSentence, args...)
 
 		if association.Kind() == reflect.Slice || association.Kind() == reflect.Array {
@@ -302,7 +302,7 @@ func (q *Query) Exists(model interface{}) (bool, error) {
 		tmpQuery.Paginator = nil
 		tmpQuery.orderClauses = clauses{}
 		tmpQuery.limitResults = 0
-		query, args := tmpQuery.ToSQL(&Model{Value: model})
+		query, args := tmpQuery.ToSQL(NewModel(model, tmpQuery.Connection.Context()))
 
 		// when query contains custom selected fields / executed using RawQuery,
 		// sql may already contains limit and offset
@@ -348,7 +348,7 @@ func (q Query) CountByField(model interface{}, field string) (int, error) {
 		tmpQuery.Paginator = nil
 		tmpQuery.orderClauses = clauses{}
 		tmpQuery.limitResults = 0
-		query, args := tmpQuery.ToSQL(&Model{Value: model})
+		query, args := tmpQuery.ToSQL(NewModel(model, q.Connection.Context()))
 		// when query contains custom selected fields / executed using RawQuery,
 		//	sql may already contains limit and offset
 
diff --git a/go.mod b/go.mod
index 225a6ac..346ac0e 100644
--- a/go.mod
+++ b/go.mod
@@ -20,8 +20,8 @@ require (
 	github.com/gobuffalo/plush/v4 v4.0.0
 	github.com/gobuffalo/validate/v3 v3.1.0
 	github.com/gofrs/uuid v3.2.0+incompatible
-	github.com/jackc/pgconn v1.6.0
-	github.com/jackc/pgx/v4 v4.6.0
+	github.com/jackc/pgconn v1.8.0
+	github.com/jackc/pgx/v4 v4.10.1
 	github.com/jmoiron/sqlx v1.2.0
 	github.com/karrick/godirwalk v1.16.1 // indirect
 	github.com/lib/pq v1.3.0
diff --git a/go.sum b/go.sum
index 22100ac..e00ce20 100644
--- a/go.sum
+++ b/go.sum
@@ -47,7 +47,6 @@ github.com/gobuffalo/envy v1.8.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6
 github.com/gobuffalo/fizz v1.10.0 h1:I8vad0PnmR+CLjSnZ5L5jlhBm4S88UIGOoZZL3/3e24=
 github.com/gobuffalo/fizz v1.10.0/go.mod h1:J2XGPO0AfJ1zKw7+2BA+6FEGAkyEsdCOLvN93WCT2WI=
 github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
-github.com/gobuffalo/flect v0.2.0 h1:EWCvMGGxOjsgwlWaP+f4+Hh6yrrte7JeFL2S6b+0hdM=
 github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
 github.com/gobuffalo/flect v0.2.1 h1:GPoRjEN0QObosV4XwuoWvSd5uSiL0N3e91/xqyY4crQ=
 github.com/gobuffalo/flect v0.2.1/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc=
@@ -55,7 +54,6 @@ github.com/gobuffalo/genny/v2 v2.0.5 h1:IH0EHcvwKT0MdASzptvkz/ViYBQELTklq1/l8Ot3
 github.com/gobuffalo/genny/v2 v2.0.5/go.mod h1:kRkJuAw9mdI37AiEYjV4Dl+TgkBDYf8HZVjLkqe5eBg=
 github.com/gobuffalo/github_flavored_markdown v1.1.0 h1:8Zzj4fTRl/OP2R7sGerzSf6g2nEJnaBEJe7UAOiEvbQ=
 github.com/gobuffalo/github_flavored_markdown v1.1.0/go.mod h1:TSpTKWcRTI0+v7W3x8dkSKMLJSUpuVitlptCkpeY8ic=
-github.com/gobuffalo/helpers v0.6.0 h1:CL1xOSGeKCaKD1IUpo4RfrkDU83kmkMG4H3dXAS7dw0=
 github.com/gobuffalo/helpers v0.6.0/go.mod h1:pncVrer7x/KRvnL5aJABLAuT/RhKRR9klL6dkUOhyv8=
 github.com/gobuffalo/helpers v0.6.1 h1:LLcL4BsiyDQYtMRUUpyFdBFvFXQ6hNYOpwrcYeilVWM=
 github.com/gobuffalo/helpers v0.6.1/go.mod h1:wInbDi0vTJKZBviURTLRMFLE4+nF2uRuuL2fnlYo7w4=
@@ -63,7 +61,6 @@ github.com/gobuffalo/logger v1.0.3 h1:YaXOTHNPCvkqqA7w05A4v0k2tCdpr+sgFlgINbQ6gq
 github.com/gobuffalo/logger v1.0.3/go.mod h1:SoeejUwldiS7ZsyCBphOGURmWdwUFXs0J7TCjEhjKxM=
 github.com/gobuffalo/nulls v0.2.0 h1:7R0Uec6JlZI02TR29zrs3KFIuUV8Sqe/s/j3yLvs+gc=
 github.com/gobuffalo/nulls v0.2.0/go.mod h1:w4q8RoSCEt87Q0K0sRIZWYeIxkxog5mh3eN3C/n+dUc=
-github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
 github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
 github.com/gobuffalo/packd v1.0.0 h1:6ERZvJHfe24rfFmA9OaoKBdC7+c9sydrytMg8SdFGBM=
 github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI=
@@ -71,11 +68,9 @@ github.com/gobuffalo/packr/v2 v2.8.0 h1:IULGd15bQL59ijXLxEvA5wlMxsmx/ZkQv9T282zN
 github.com/gobuffalo/packr/v2 v2.8.0/go.mod h1:PDk2k3vGevNE3SwVyVRgQCCXETC9SaONCNSXT1Q8M1g=
 github.com/gobuffalo/plush/v4 v4.0.0 h1:ZHdmfr2R7DQ77XzWZK2PGKJOXm9NRy21EZ6Rw7FhuNw=
 github.com/gobuffalo/plush/v4 v4.0.0/go.mod h1:ErFS3UxKqEb8fpFJT7lYErfN/Nw6vHGiDMTjxpk5bQ0=
-github.com/gobuffalo/tags/v3 v3.0.2 h1:gxE6c6fA5radwQeg59aPIeYgCG8YA8AZd3Oh6fh5UXA=
 github.com/gobuffalo/tags/v3 v3.0.2/go.mod h1:ZQeN6TCTiwAFnS0dNcbDtSgZDwNKSpqajvVtt6mlYpA=
 github.com/gobuffalo/tags/v3 v3.1.0 h1:mzdCYooN2VsLRr8KIAdEZ1lh1Py7JSMsiEGCGata2AQ=
 github.com/gobuffalo/tags/v3 v3.1.0/go.mod h1:ZQeN6TCTiwAFnS0dNcbDtSgZDwNKSpqajvVtt6mlYpA=
-github.com/gobuffalo/validate/v3 v3.0.0 h1:dF7Bg8NMF9Zv8bZvUMXYJXxZdj+eSZ8z/lGM7/jVFUE=
 github.com/gobuffalo/validate/v3 v3.0.0/go.mod h1:HFpjq+AIiA2RHoQnQVTFKF/ZpUPXwyw82LgyDPxQ9r0=
 github.com/gobuffalo/validate/v3 v3.1.0 h1:/QQN920PciCfBs3aywtJTvDTHmBFMKoiwkshUWa/HLQ=
 github.com/gobuffalo/validate/v3 v3.1.0/go.mod h1:HFpjq+AIiA2RHoQnQVTFKF/ZpUPXwyw82LgyDPxQ9r0=
@@ -90,6 +85,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
@@ -99,16 +95,17 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
 github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
-github.com/jackc/chunkreader/v2 v2.0.0 h1:DUwgMQuuPnS0rhMXenUtZpqZqrR/30NWY+qQvTpSvEs=
 github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
 github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
 github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
 github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
 github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
 github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
+github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
 github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
-github.com/jackc/pgconn v1.6.0 h1:8FiBxMxS/Z0eQ9BeE1HhL6pzPL1R5x+ZuQ+T86WgZ4I=
-github.com/jackc/pgconn v1.6.0/go.mod h1:yeseQo4xhQbgyJs2c87RAXOH2i624N0Fh1KSPJya7qo=
+github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
+github.com/jackc/pgconn v1.8.0 h1:FmjZ0rOyXTr1wfWs45i4a9vjnjWUAGpMuQLD9OSs+lw=
+github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
 github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
 github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
 github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye4717ITLaNwV9mWbJx0dLCpcRzdA=
@@ -121,36 +118,39 @@ github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod
 github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
 github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
 github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
-github.com/jackc/pgproto3/v2 v2.0.1 h1:Rdjp4NFjwHnEslx2b66FfCI2S0LhO4itac3hXz6WX9M=
 github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
-github.com/jackc/pgproto3/v2 v2.0.2 h1:q1Hsy66zh4vuNsajBUF2PNqfAMMfxU5mk594lPE9vjY=
-github.com/jackc/pgproto3/v2 v2.0.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
-github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8 h1:Q3tB+ExeflWUW7AFcAhXqk40s9mnNYLk1nOkKNZ5GnU=
+github.com/jackc/pgproto3/v2 v2.0.6 h1:b1105ZGEMFe7aCvrT1Cca3VoVb4ZFMaFJLJcg/3zD+8=
+github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
 github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
+github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
+github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
 github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
 github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
-github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59 h1:xOamcCJ9MFJTxR5bvw3ZXmiP8evQMohdt2VJ57C0W8Q=
 github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
-github.com/jackc/pgtype v1.3.0 h1:l8JvKrby3RI7Kg3bYEeU9TA4vqC38QDpFCfcrC7KuN0=
-github.com/jackc/pgtype v1.3.0/go.mod h1:b0JqxHvPmljG+HQ5IsvQ0yqeSi4nGcDTVjFoiLDb0Ik=
-github.com/jackc/pgx v3.6.2+incompatible h1:2zP5OD7kiyR3xzRYMhOcXVvkDZsImVXfj+yIyTQf3/o=
-github.com/jackc/pgx v3.6.2+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
+github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
+github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
+github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
+github.com/jackc/pgtype v1.6.2 h1:b3pDeuhbbzBYcg5kwNmNDun4pFUD/0AAr1kLXZLeNt8=
+github.com/jackc/pgtype v1.6.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig=
 github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
 github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
-github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186 h1:ZQM8qLT/E/CGD6XX0E6q9FAwxJYmWpJufzmLMaFuzgQ=
 github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
-github.com/jackc/pgx/v4 v4.6.0 h1:Fh0O9GdlG4gYpjpwOqjdEodJUQM9jzN3Hdv7PN0xmm0=
-github.com/jackc/pgx/v4 v4.6.0/go.mod h1:vPh43ZzxijXUVJ+t/EmXBtFmbFVO72cuneCT9oAlxAg=
+github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
+github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
+github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
+github.com/jackc/pgx/v4 v4.10.1 h1:/6Q3ye4myIj6AaplUm+eRcz4OhK9HAvFf4ePsG40LJY=
+github.com/jackc/pgx/v4 v4.10.1/go.mod h1:QlrWebbs3kqEZPHCTGyxecvzG6tvIsYu+A5b1raylkA=
 github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
 github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
 github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
 github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
 github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
 github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
 github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/karrick/godirwalk v1.15.3 h1:0a2pXOgtB16CqIqXTiT7+K9L73f74n/aNQUnH6Ortew=
 github.com/karrick/godirwalk v1.15.3/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
 github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw=
 github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
@@ -162,7 +162,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
 github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
 github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@@ -186,14 +185,16 @@ github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=
 github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
 github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
 github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
-github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
 github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
+github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
-github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
 github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
 github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@@ -220,6 +221,7 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
 github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w=
 github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
@@ -227,14 +229,13 @@ github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
 github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
 github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
-github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
 github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
-github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
 github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
+github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoMJBbEoyE/RJoE8vz7Mb8AjvifMMwSyvY=
+github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
@@ -259,7 +260,6 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
 github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
@@ -271,20 +271,27 @@ github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxt
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
 go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4=
 golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
 golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -296,9 +303,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
 golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -322,29 +327,32 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
 golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20200117220505-0cba7a3a9ee9 h1:KOkk4e2xd5OeCDJGwacvr75ICCbCsShrHiqPEdsA9hg=
 golang.org/x/tools v0.0.0-20200117220505-0cba7a3a9ee9/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200308013534-11ec41452d41 h1:9Di9iYgOt9ThCipBxChBVhgNipDoE5mxO84rQV7D0FE=
 golang.org/x/tools v0.0.0-20200308013534-11ec41452d41/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
 golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -354,7 +362,6 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
 google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -368,3 +375,4 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
diff --git a/model.go b/model.go
index 62efd25..1dea97f 100644
--- a/model.go
+++ b/model.go
@@ -1,22 +1,23 @@
 package pop
 
 import (
+	"context"
 	"fmt"
-	"github.com/pkg/errors"
 	"reflect"
-	"sync"
+	"strings"
 	"time"
 
-	"github.com/gobuffalo/flect"
 	nflect "github.com/gobuffalo/flect/name"
+
+	"github.com/gobuffalo/pop/v5/columns"
+	"github.com/pkg/errors"
+
+	"github.com/gobuffalo/flect"
 	"github.com/gofrs/uuid"
 )
 
 var nowFunc = time.Now
 
-var tableMap = map[string]string{}
-var tableMapMu = sync.RWMutex{}
-
 // Value is the contents of a `Model`.
 type Value interface{}
 
@@ -26,8 +27,13 @@ type modelIterable func(*Model) error
 // that is passed in to many functions.
 type Model struct {
 	Value
-	tableName string
-	As        string
+	ctx context.Context
+	As  string
+}
+
+// NewModel returns a new model with the specified value and context.
+func NewModel(v Value, ctx context.Context) *Model {
+	return &Model{Value: v, ctx: ctx}
 }
 
 // ID returns the ID of the Model. All models must have an `ID` field this is
@@ -46,7 +52,18 @@ func (m *Model) ID() interface{} {
 // IDField returns the name of the DB field used for the ID.
 // By default, it will return "id".
 func (m *Model) IDField() string {
-	field, ok := reflect.TypeOf(m.Value).Elem().FieldByName("ID")
+	modelType := reflect.TypeOf(m.Value)
+
+	// remove all indirections
+	for modelType.Kind() == reflect.Slice || modelType.Kind() == reflect.Ptr || modelType.Kind() == reflect.Array {
+		modelType = modelType.Elem()
+	}
+
+	if modelType.Kind() == reflect.String {
+		return "id"
+	}
+
+	field, ok := modelType.FieldByName("ID")
 	if !ok {
 		return "id"
 	}
@@ -74,38 +91,43 @@ type TableNameAble interface {
 	TableName() string
 }
 
+// TableNameAbleWithContext is equal to TableNameAble but will
+// be passed the queries' context. Useful in cases where the
+// table name depends on e.g.
+type TableNameAbleWithContext interface {
+	TableName(ctx context.Context) string
+}
+
 // TableName returns the corresponding name of the underlying database table
 // for a given `Model`. See also `TableNameAble` to change the default name of the table.
 func (m *Model) TableName() string {
 	if s, ok := m.Value.(string); ok {
 		return s
 	}
+
 	if n, ok := m.Value.(TableNameAble); ok {
 		return n.TableName()
 	}
 
-	if m.tableName != "" {
-		return m.tableName
+	if n, ok := m.Value.(TableNameAbleWithContext); ok {
+		if m.ctx == nil {
+			m.ctx = context.TODO()
+		}
+		return n.TableName(m.ctx)
 	}
 
-	t := reflect.TypeOf(m.Value)
-	name, cacheKey := m.typeName(t)
-
-	defer tableMapMu.Unlock()
-	tableMapMu.Lock()
+	return m.typeName(reflect.TypeOf(m.Value))
+}
 
-	if tableMap[cacheKey] == "" {
-		m.tableName = nflect.Tableize(name)
-		tableMap[cacheKey] = m.tableName
-	}
-	return tableMap[cacheKey]
+func (m *Model) Columns() columns.Columns {
+	return columns.ForStructWithAlias(m.Value, m.TableName(), m.As, m.IDField())
 }
 
 func (m *Model) cacheKey(t reflect.Type) string {
 	return t.PkgPath() + "." + t.Name()
 }
 
-func (m *Model) typeName(t reflect.Type) (name, cacheKey string) {
+func (m *Model) typeName(t reflect.Type) (name string) {
 	if t.Kind() == reflect.Ptr {
 		t = t.Elem()
 	}
@@ -117,19 +139,27 @@ func (m *Model) typeName(t reflect.Type) (name, cacheKey string) {
 		}
 
 		// validates if the elem of slice or array implements TableNameAble interface.
-		tableNameAble := (*TableNameAble)(nil)
+		var tableNameAble *TableNameAble
 		if el.Implements(reflect.TypeOf(tableNameAble).Elem()) {
 			v := reflect.New(el)
 			out := v.MethodByName("TableName").Call([]reflect.Value{})
-			name := out[0].String()
-			if tableMap[m.cacheKey(el)] == "" {
-				tableMap[m.cacheKey(el)] = name
-			}
+			return out[0].String()
+		}
+
+		// validates if the elem of slice or array implements TableNameAbleWithContext interface.
+		var tableNameAbleWithContext *TableNameAbleWithContext
+		if el.Implements(reflect.TypeOf(tableNameAbleWithContext).Elem()) {
+			v := reflect.New(el)
+			out := v.MethodByName("TableName").Call([]reflect.Value{reflect.ValueOf(m.ctx)})
+			return out[0].String()
+
+			// We do not want to cache contextualized TableNames because that would break
+			// the contextualization.
 		}
 
-		return el.Name(), m.cacheKey(el)
+		return nflect.Tableize(el.Name())
 	default:
-		return t.Name(), m.cacheKey(t)
+		return nflect.Tableize(t.Name())
 	}
 }
 
@@ -193,11 +223,21 @@ func (m *Model) touchUpdatedAt() {
 }
 
 func (m *Model) whereID() string {
-	return fmt.Sprintf("%s.%s = ?", m.TableName(), m.IDField())
+	as := m.As
+	if as == "" {
+		as = strings.ReplaceAll(m.TableName(), ".", "_")
+	}
+
+	return fmt.Sprintf("%s.%s = ?", as, m.IDField())
 }
 
 func (m *Model) whereNamedID() string {
-	return fmt.Sprintf("%s.%s = :%s", m.TableName(), m.IDField(), m.IDField())
+	as := m.As
+	if as == "" {
+		as = strings.ReplaceAll(m.TableName(), ".", "_")
+	}
+
+	return fmt.Sprintf("%s.%s = :%s", as, m.IDField(), m.IDField())
 }
 
 func (m *Model) isSlice() bool {
@@ -210,7 +250,10 @@ func (m *Model) iterate(fn modelIterable) error {
 		v := reflect.Indirect(reflect.ValueOf(m.Value))
 		for i := 0; i < v.Len(); i++ {
 			val := v.Index(i)
-			newModel := &Model{Value: val.Addr().Interface()}
+			newModel := &Model{
+				Value: val.Addr().Interface(),
+				ctx:   m.ctx,
+			}
 			err := fn(newModel)
 
 			if err != nil {
diff --git a/preload_associations.go b/preload_associations.go
index 4a03b57..c7c8b6c 100644
--- a/preload_associations.go
+++ b/preload_associations.go
@@ -167,7 +167,7 @@ func (ami *AssociationMetaInfo) fkName() string {
 // preload is the query mode used to load associations from database
 // similar to the active record default approach on Rails.
 func preload(tx *Connection, model interface{}, fields ...string) error {
-	mmi := NewModelMetaInfo(&Model{Value: model})
+	mmi := NewModelMetaInfo(NewModel(model, tx.Context()))
 
 	preloadFields, err := mmi.preloadFields(fields...)
 	if err != nil {
@@ -355,7 +355,9 @@ func preloadBelongsTo(tx *Connection, asoc *AssociationMetaInfo, mmi *ModelMetaI
 
 	fkids := []interface{}{}
 	mmi.iterate(func(val reflect.Value) {
-		fkids = append(fkids, mmi.mapper.FieldByName(val, fi.Path).Interface())
+		if !isFieldNilPtr(val, fi) {
+			fkids = append(fkids, mmi.mapper.FieldByName(val, fi.Path).Interface())
+		}
 	})
 
 	if len(fkids) == 0 {
@@ -386,11 +388,15 @@ func preloadBelongsTo(tx *Connection, asoc *AssociationMetaInfo, mmi *ModelMetaI
 
 	// 3) iterate over every model and fill it with the assoc.
 	mmi.iterate(func(mvalue reflect.Value) {
+		if isFieldNilPtr(mvalue, fi) {
+			return
+		}
 		modelAssociationField := mmi.mapper.FieldByName(mvalue, asoc.Name)
 		for i := 0; i < slice.Elem().Len(); i++ {
 			asocValue := slice.Elem().Index(i)
-			if mmi.mapper.FieldByName(mvalue, fi.Path).Interface() == mmi.mapper.FieldByName(asocValue, "ID").Interface() ||
-				reflect.DeepEqual(mmi.mapper.FieldByName(mvalue, fi.Path), mmi.mapper.FieldByName(asocValue, "ID")) {
+			fkField := reflect.Indirect(mmi.mapper.FieldByName(mvalue, fi.Path))
+			if fkField.Interface() == mmi.mapper.FieldByName(asocValue, "ID").Interface() ||
+				reflect.DeepEqual(fkField, mmi.mapper.FieldByName(asocValue, "ID")) {
 
 				switch {
 				case modelAssociationField.Kind() == reflect.Slice || modelAssociationField.Kind() == reflect.Array:
@@ -499,3 +505,8 @@ func preloadManyToMany(tx *Connection, asoc *AssociationMetaInfo, mmi *ModelMeta
 	}
 	return nil
 }
+
+func isFieldNilPtr(val reflect.Value, fi *reflectx.FieldInfo) bool {
+	fieldValue := reflectx.FieldByIndexesReadOnly(val, fi.Index)
+	return fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil()
+}
diff --git a/slices/map.go b/slices/map.go
index d7f58ed..c99e8a1 100644
--- a/slices/map.go
+++ b/slices/map.go
@@ -51,6 +51,9 @@ func (m Map) UnmarshalJSON(b []byte) error {
 	if err != nil {
 		return err
 	}
+	if m == nil {
+		m = Map{}
+	}
 	for key, value := range stuff {
 		m[key] = value
 	}
diff --git a/soda/cmd/migrate_status.go b/soda/cmd/migrate_status.go
index e9a85df..98d0aae 100644
--- a/soda/cmd/migrate_status.go
+++ b/soda/cmd/migrate_status.go
@@ -1,9 +1,10 @@
 package cmd
 
 import (
+	"os"
+
 	"github.com/gobuffalo/pop/v5"
 	"github.com/spf13/cobra"
-	"os"
 )
 
 var migrateStatusCmd = &cobra.Command{
diff --git a/sql_builder.go b/sql_builder.go
index 4ce3570..edd05c4 100644
--- a/sql_builder.go
+++ b/sql_builder.go
@@ -229,7 +229,7 @@ func (sq *sqlBuilder) buildColumns() columns.Columns {
 		if ok && cols.TableAlias == asName {
 			return cols
 		}
-		cols = columns.ForStructWithAlias(sq.Model.Value, tableName, asName)
+		cols = columns.ForStructWithAlias(sq.Model.Value, tableName, asName, sq.Model.IDField())
 		columnCacheMutex.Lock()
 		columnCache[tableName] = cols
 		columnCacheMutex.Unlock()
@@ -237,7 +237,7 @@ func (sq *sqlBuilder) buildColumns() columns.Columns {
 	}
 
 	// acl > 0
-	cols := columns.NewColumns("")
+	cols := columns.NewColumns("", sq.Model.IDField())
 	cols.Add(sq.AddColumns...)
 	return cols
 }
diff --git a/store.go b/store.go
index cc98a26..39c554e 100644
--- a/store.go
+++ b/store.go
@@ -54,3 +54,7 @@ func (s contextStore) Exec(query string, args ...interface{}) (sql.Result, error
 func (s contextStore) PrepareNamed(query string) (*sqlx.NamedStmt, error) {
 	return s.store.PrepareNamedContext(s.ctx, query)
 }
+
+func (s contextStore) Context() context.Context {
+	return s.ctx
+}
diff --git a/validations.go b/validations.go
index 79e7610..ab5b845 100644
--- a/validations.go
+++ b/validations.go
@@ -133,7 +133,7 @@ func (m *Model) iterateAndValidate(fn modelIterableValidator) (*validate.Errors,
 	if v.Kind() == reflect.Slice || v.Kind() == reflect.Array {
 		for i := 0; i < v.Len(); i++ {
 			val := v.Index(i)
-			newModel := &Model{Value: val.Addr().Interface()}
+			newModel := NewModel(val.Addr().Interface(), m.ctx)
 			verrs, err := fn(newModel)
 
 			if err != nil || verrs.HasAny() {


Test patch:
diff --git a/belongs_to_test.go b/belongs_to_test.go
index e4e3a3e..9eb9991 100644
--- a/belongs_to_test.go
+++ b/belongs_to_test.go
@@ -1,6 +1,7 @@
 package pop
 
 import (
+	"context"
 	"testing"
 
 	"github.com/stretchr/testify/require"
@@ -14,7 +15,7 @@ func Test_BelongsTo(t *testing.T) {
 
 	q := PDB.BelongsTo(&User{ID: 1})
 
-	m := &Model{Value: &Enemy{}}
+	m := NewModel(new(Enemy), context.Background())
 
 	sql, _ := q.ToSQL(m)
 	r.Equal(ts("SELECT enemies.A FROM enemies AS enemies WHERE user_id = ?"), sql)
@@ -28,7 +29,7 @@ func Test_BelongsToAs(t *testing.T) {
 
 	q := PDB.BelongsToAs(&User{ID: 1}, "u_id")
 
-	m := &Model{Value: &Enemy{}}
+	m := NewModel(new(Enemy), context.Background())
 
 	sql, _ := q.ToSQL(m)
 	r.Equal(ts("SELECT enemies.A FROM enemies AS enemies WHERE u_id = ?"), sql)
@@ -43,7 +44,7 @@ func Test_BelongsToThrough(t *testing.T) {
 	q := PDB.BelongsToThrough(&User{ID: 1}, &Friend{})
 	qs := "SELECT enemies.A FROM enemies AS enemies, good_friends AS good_friends WHERE good_friends.user_id = ? AND enemies.id = good_friends.enemy_id"
 
-	m := &Model{Value: &Enemy{}}
+	m := NewModel(new(Enemy), context.Background())
 	sql, _ := q.ToSQL(m)
 	r.Equal(ts(qs), sql)
 }
diff --git a/columns/columns_test.go b/columns/columns_test.go
index caa0716..f4699dc 100644
--- a/columns/columns_test.go
+++ b/columns/columns_test.go
@@ -21,8 +21,8 @@ type foos []foo
 func Test_Column_MapsSlice(t *testing.T) {
 	r := require.New(t)
 
-	c1 := columns.ForStruct(&foo{}, "foo")
-	c2 := columns.ForStruct(&foos{}, "foo")
+	c1 := columns.ForStruct(&foo{}, "foo", "id")
+	c2 := columns.ForStruct(&foos{}, "foo", "id")
 	r.Equal(c1.String(), c2.String())
 }
 
@@ -30,7 +30,7 @@ func Test_Columns_Basics(t *testing.T) {
 	r := require.New(t)
 
 	for _, f := range []interface{}{foo{}, &foo{}} {
-		c := columns.ForStruct(f, "foo")
+		c := columns.ForStruct(f, "foo", "id")
 		r.Equal(len(c.Cols), 4)
 		r.Equal(c.Cols["first_name"], &columns.Column{Name: "first_name", Writeable: false, Readable: true, SelectSQL: "first_name as f"})
 		r.Equal(c.Cols["LastName"], &columns.Column{Name: "LastName", Writeable: true, Readable: true, SelectSQL: "foo.LastName"})
@@ -43,7 +43,7 @@ func Test_Columns_Add(t *testing.T) {
 	r := require.New(t)
 
 	for _, f := range []interface{}{foo{}, &foo{}} {
-		c := columns.ForStruct(f, "foo")
+		c := columns.ForStruct(f, "foo", "id")
 		r.Equal(len(c.Cols), 4)
 		c.Add("foo", "first_name")
 		r.Equal(len(c.Cols), 5)
@@ -55,7 +55,7 @@ func Test_Columns_Remove(t *testing.T) {
 	r := require.New(t)
 
 	for _, f := range []interface{}{foo{}, &foo{}} {
-		c := columns.ForStruct(f, "foo")
+		c := columns.ForStruct(f, "foo", "id")
 		r.Equal(len(c.Cols), 4)
 		c.Remove("foo", "first_name")
 		r.Equal(len(c.Cols), 3)
@@ -75,9 +75,43 @@ func (fooQuoter) Quote(key string) string {
 func Test_Columns_Sorted(t *testing.T) {
 	r := require.New(t)
 
-	c := columns.ForStruct(fooWithSuffix{}, "fooWithSuffix")
+	c := columns.ForStruct(fooWithSuffix{}, "fooWithSuffix", "id")
 	r.Equal(len(c.Cols), 2)
 	r.Equal(c.SymbolizedString(), ":amount, :amount_units")
 	r.Equal(c.String(), "amount, amount_units")
 	r.Equal(c.QuotedString(fooQuoter{}), "`amount`, `amount_units`")
 }
+
+func Test_Columns_IDField(t *testing.T) {
+	type withID struct {
+		ID string `db:"id"`
+	}
+
+	r := require.New(t)
+	c := columns.ForStruct(withID{}, "with_id", "id")
+	r.Equal(1, len(c.Cols), "%+v", c)
+	r.Equal(&columns.Column{Name: "id", Writeable: false, Readable: true, SelectSQL: "with_id.id"}, c.Cols["id"])
+}
+
+func Test_Columns_IDField_Readonly(t *testing.T) {
+	type withIDReadonly struct {
+		ID string `db:"id" rw:"r"`
+	}
+
+	r := require.New(t)
+	c := columns.ForStruct(withIDReadonly{}, "with_id_readonly", "id")
+	r.Equal(1, len(c.Cols), "%+v", c)
+	r.Equal(&columns.Column{Name: "id", Writeable: false, Readable: true, SelectSQL: "with_id_readonly.id"}, c.Cols["id"])
+}
+
+func Test_Columns_ID_Field_Not_ID(t *testing.T) {
+	type withNonStandardID struct {
+		PK string `db:"notid"`
+	}
+
+	r := require.New(t)
+
+	c := columns.ForStruct(withNonStandardID{}, "non_standard_id", "notid")
+	r.Equal(1, len(c.Cols), "%+v", c)
+	r.Equal(&columns.Column{Name: "notid", Writeable: false, Readable: true, SelectSQL: "non_standard_id.notid"}, c.Cols["notid"])
+}
diff --git a/columns/readable_columns_test.go b/columns/readable_columns_test.go
index 8394b96..a563d78 100644
--- a/columns/readable_columns_test.go
+++ b/columns/readable_columns_test.go
@@ -10,7 +10,7 @@ import (
 func Test_Columns_ReadableString(t *testing.T) {
 	r := require.New(t)
 	for _, f := range []interface{}{foo{}, &foo{}} {
-		c := columns.ForStruct(f, "foo")
+		c := columns.ForStruct(f, "foo", "id")
 		u := c.Readable().String()
 		r.Equal(u, "LastName, first_name, read")
 	}
@@ -19,7 +19,7 @@ func Test_Columns_ReadableString(t *testing.T) {
 func Test_Columns_Readable_SelectString(t *testing.T) {
 	r := require.New(t)
 	for _, f := range []interface{}{foo{}, &foo{}} {
-		c := columns.ForStruct(f, "foo")
+		c := columns.ForStruct(f, "foo", "id")
 		u := c.Readable().SelectString()
 		r.Equal(u, "first_name as f, foo.LastName, foo.read")
 	}
@@ -28,7 +28,7 @@ func Test_Columns_Readable_SelectString(t *testing.T) {
 func Test_Columns_ReadableString_Symbolized(t *testing.T) {
 	r := require.New(t)
 	for _, f := range []interface{}{foo{}, &foo{}} {
-		c := columns.ForStruct(f, "foo")
+		c := columns.ForStruct(f, "foo", "id")
 		u := c.Readable().SymbolizedString()
 		r.Equal(u, ":LastName, :first_name, :read")
 	}
diff --git a/columns/writeable_columns_test.go b/columns/writeable_columns_test.go
index 269735f..053dbda 100644
--- a/columns/writeable_columns_test.go
+++ b/columns/writeable_columns_test.go
@@ -10,7 +10,7 @@ import (
 func Test_Columns_WriteableString_Symbolized(t *testing.T) {
 	r := require.New(t)
 	for _, f := range []interface{}{foo{}, &foo{}} {
-		c := columns.ForStruct(f, "foo")
+		c := columns.ForStruct(f, "foo", "id")
 		u := c.Writeable().SymbolizedString()
 		r.Equal(u, ":LastName, :write")
 	}
@@ -19,7 +19,7 @@ func Test_Columns_WriteableString_Symbolized(t *testing.T) {
 func Test_Columns_UpdateString(t *testing.T) {
 	r := require.New(t)
 	for _, f := range []interface{}{foo{}, &foo{}} {
-		c := columns.ForStruct(f, "foo")
+		c := columns.ForStruct(f, "foo", "id")
 		u := c.Writeable().UpdateString()
 		r.Equal(u, "LastName = :LastName, write = :write")
 	}
@@ -35,7 +35,7 @@ func Test_Columns_QuotedUpdateString(t *testing.T) {
 	r := require.New(t)
 	q := testQuoter{}
 	for _, f := range []interface{}{foo{}, &foo{}} {
-		c := columns.ForStruct(f, "foo")
+		c := columns.ForStruct(f, "foo", "id")
 		u := c.Writeable().QuotedUpdateString(q)
 		r.Equal(u, "\"LastName\" = :LastName, \"write\" = :write")
 	}
@@ -44,7 +44,7 @@ func Test_Columns_QuotedUpdateString(t *testing.T) {
 func Test_Columns_WriteableString(t *testing.T) {
 	r := require.New(t)
 	for _, f := range []interface{}{foo{}, &foo{}} {
-		c := columns.ForStruct(f, "foo")
+		c := columns.ForStruct(f, "foo", "id")
 		u := c.Writeable().String()
 		r.Equal(u, "LastName, write")
 	}
diff --git a/connection_details_test.go b/connection_details_test.go
index 4121745..877c841 100644
--- a/connection_details_test.go
+++ b/connection_details_test.go
@@ -84,3 +84,22 @@ func Test_ConnectionDetails_Finalize_NoDB_NoURL(t *testing.T) {
 	err := cd.Finalize()
 	r.Error(err)
 }
+
+func Test_ConnectionDetails_OptionsString_Postgres(t *testing.T) {
+	r := require.New(t)
+	cd := &ConnectionDetails{
+		Dialect:  "postgres",
+		Database: "database",
+		Host:     "host",
+		Port:     "1234",
+		User:     "user",
+		Password: "pass",
+		Options: map[string]string{
+			"migration_table_name": "migrations",
+			"sslmode":              "require",
+		},
+	}
+
+	r.Equal("sslmode=require", cd.OptionsString(""))
+	r.Equal("migrations", cd.MigrationTableName())
+}
diff --git a/connection_instrumented_nosqlite_test.go b/connection_instrumented_nosqlite_test.go
index 715a92a..2cf6ae0 100644
--- a/connection_instrumented_nosqlite_test.go
+++ b/connection_instrumented_nosqlite_test.go
@@ -3,8 +3,9 @@
 package pop
 
 import (
-	"github.com/stretchr/testify/require"
 	"testing"
+
+	"github.com/stretchr/testify/require"
 )
 
 func TestInstrumentation_WithoutSqlite(t *testing.T) {
diff --git a/connection_instrumented_test.go b/connection_instrumented_test.go
index d512563..808d863 100644
--- a/connection_instrumented_test.go
+++ b/connection_instrumented_test.go
@@ -3,12 +3,13 @@ package pop
 import (
 	"context"
 	"fmt"
-	"github.com/luna-duclos/instrumentedsql"
-	"github.com/stretchr/testify/suite"
 	"os"
 	"strings"
 	"sync"
 	"time"
+
+	"github.com/luna-duclos/instrumentedsql"
+	"github.com/stretchr/testify/suite"
 )
 
 func testInstrumentedDriver(p *suite.Suite) {
diff --git a/executors_test.go b/executors_test.go
index 4fb4bb7..bee9a5e 100644
--- a/executors_test.go
+++ b/executors_test.go
@@ -510,6 +510,28 @@ func Test_Create_With_Non_ID_PK_String(t *testing.T) {
 	})
 }
 
+func Test_Create_Non_PK_ID(t *testing.T) {
+	if PDB == nil {
+		t.Skip("skipping integration tests")
+	}
+	transaction(func(tx *Connection) {
+		r := require.New(t)
+
+		r.NoError(tx.Create(&NonStandardID{OutfacingID: "make sure the tested entry does not have pk=0"}))
+
+		count, err := tx.Count(&NonStandardID{})
+		entry := &NonStandardID{
+			OutfacingID: "beautiful to the outside ID",
+		}
+		r.NoError(tx.Create(entry))
+
+		ctx, err := tx.Count(&NonStandardID{})
+		r.NoError(err)
+		r.Equal(count+1, ctx)
+		r.NotZero(entry.ID)
+	})
+}
+
 func Test_Eager_Create_Has_Many(t *testing.T) {
 	if PDB == nil {
 		t.Skip("skipping integration tests")
@@ -1470,6 +1492,54 @@ func Test_Update_UUID(t *testing.T) {
 	})
 }
 
+func Test_Update_With_Non_ID_PK(t *testing.T) {
+	if PDB == nil {
+		t.Skip("skipping integration tests")
+	}
+	transaction(func(tx *Connection) {
+		r := require.New(t)
+
+		r.NoError(tx.Create(&CrookedColour{Name: "cc is not the first one"}))
+
+		cc := CrookedColour{
+			Name: "You?",
+		}
+		err := tx.Create(&cc)
+		r.NoError(err)
+		r.NotZero(cc.ID)
+		id := cc.ID
+
+		updatedName := "Me!"
+		cc.Name = updatedName
+		r.NoError(tx.Update(&cc))
+		r.Equal(id, cc.ID)
+
+		r.NoError(tx.Reload(&cc))
+		r.Equal(updatedName, cc.Name)
+		r.Equal(id, cc.ID)
+	})
+}
+
+func Test_Update_Non_PK_ID(t *testing.T) {
+	if PDB == nil {
+		t.Skip("skipping integration tests")
+	}
+	transaction(func(tx *Connection) {
+		r := require.New(t)
+
+		client := &NonStandardID{
+			OutfacingID: "my awesome hydra client",
+		}
+		r.NoError(tx.Create(client))
+
+		updatedID := "your awesome hydra client"
+		client.OutfacingID = updatedID
+		r.NoError(tx.Update(client))
+		r.NoError(tx.Reload(client))
+		r.Equal(updatedID, client.OutfacingID)
+	})
+}
+
 func Test_Destroy(t *testing.T) {
 	if PDB == nil {
 		t.Skip("skipping integration tests")
diff --git a/finders_test.go b/finders_test.go
index 389dc80..7f30727 100644
--- a/finders_test.go
+++ b/finders_test.go
@@ -101,7 +101,7 @@ func Test_Select(t *testing.T) {
 
 		q := tx.Select("name", "email", "\n", "\t\n", "")
 
-		sm := &Model{Value: &User{}}
+		sm := NewModel(new(User), tx.Context())
 		sql, _ := q.ToSQL(sm)
 		r.Equal(tx.Dialect.TranslateSQL("SELECT email, name FROM users AS users"), sql)
 
diff --git a/match_test.go b/match_test.go
index 8cc41a6..0f1591f 100644
--- a/match_test.go
+++ b/match_test.go
@@ -1,8 +1,9 @@
 package pop
 
 import (
-	"github.com/stretchr/testify/require"
 	"testing"
+
+	"github.com/stretchr/testify/require"
 )
 
 func Test_ParseMigrationFilenameFizzDown(t *testing.T) {
diff --git a/migration_info_test.go b/migration_info_test.go
index 0f76824..f217455 100644
--- a/migration_info_test.go
+++ b/migration_info_test.go
@@ -1,9 +1,10 @@
 package pop
 
 import (
-	"github.com/stretchr/testify/assert"
 	"sort"
 	"testing"
+
+	"github.com/stretchr/testify/assert"
 )
 
 func TestSortingMigrations(t *testing.T) {
diff --git a/model_context_test.go b/model_context_test.go
new file mode 100644
index 0000000..6b03a88
--- /dev/null
+++ b/model_context_test.go
@@ -0,0 +1,109 @@
+package pop
+
+import (
+	"context"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/require"
+)
+
+type ContextTable struct {
+	ID        string    `db:"id"`
+	Value     string    `db:"value"`
+	CreatedAt time.Time `db:"created_at"`
+	UpdatedAt time.Time `db:"updated_at"`
+}
+
+func (t ContextTable) TableName(ctx context.Context) string {
+	// This is singular on purpose! It will checck if the TableName is properly
+	// Respected in slices as well.
+	return "context_prefix_" + ctx.Value("prefix").(string) + "_table"
+}
+
+func Test_ModelContext(t *testing.T) {
+	if PDB == nil {
+		t.Skip("skipping integration tests")
+	}
+
+	t.Run("contextless", func(t *testing.T) {
+		r := require.New(t)
+		r.Panics(func() {
+			var c ContextTable
+			r.NoError(PDB.Create(&c))
+		}, "panics if context prefix is not set")
+	})
+
+	for _, prefix := range []string{"a", "b"} {
+		t.Run("prefix="+prefix, func(t *testing.T) {
+			r := require.New(t)
+
+			expected := ContextTable{ID: prefix, Value: prefix}
+			c := PDB.WithContext(context.WithValue(context.Background(), "prefix", prefix))
+			r.NoError(c.Create(&expected))
+
+			var actual ContextTable
+			r.NoError(c.Find(&actual, expected.ID))
+			r.EqualValues(prefix, actual.Value)
+			r.EqualValues(prefix, actual.ID)
+
+			exists, err := c.Where("id = ?", actual.ID).Exists(new(ContextTable))
+			r.NoError(err)
+			r.True(exists)
+
+			count, err := c.Where("id = ?", actual.ID).Count(new(ContextTable))
+			r.NoError(err)
+			r.EqualValues(1, count)
+
+			expected.Value += expected.Value
+			r.NoError(c.Update(&expected))
+
+			r.NoError(c.Find(&actual, expected.ID))
+			r.EqualValues(prefix+prefix, actual.Value)
+			r.EqualValues(prefix, actual.ID)
+
+			var results []ContextTable
+			require.NoError(t, c.All(&results))
+
+			require.NoError(t, c.First(&expected))
+			require.NoError(t, c.Last(&expected))
+
+			r.NoError(c.Destroy(&expected))
+		})
+	}
+
+	t.Run("prefix=unknown", func(t *testing.T) {
+		r := require.New(t)
+		c := PDB.WithContext(context.WithValue(context.Background(), "prefix", "unknown"))
+		err := c.Create(&ContextTable{ID: "unknown"})
+		r.Error(err)
+
+		if !strings.Contains(err.Error(), "context_prefix_unknown_table") { // All other databases
+			t.Fatalf("Expected error to contain indicator that table does not exist but got: %s", err.Error())
+		}
+	})
+
+	t.Run("cache_busting", func(t *testing.T) {
+		r := require.New(t)
+
+		var expectedA, expectedB ContextTable
+		expectedA.ID = "expectedA"
+		expectedB.ID = "expectedB"
+
+		cA := PDB.WithContext(context.WithValue(context.Background(), "prefix", "a"))
+		r.NoError(cA.Create(&expectedA))
+
+		cB := PDB.WithContext(context.WithValue(context.Background(), "prefix", "b"))
+		r.NoError(cB.Create(&expectedB))
+
+		var actualA, actualB []ContextTable
+		r.NoError(cA.All(&actualA))
+		r.NoError(cB.All(&actualB))
+
+		r.Len(actualA, 1)
+		r.Len(actualB, 1)
+
+		r.NotEqual(actualA[0].ID, actualB[0].ID, "if these are equal context switching did not work")
+	})
+}
diff --git a/model_test.go b/model_test.go
index 545eb09..d0d0c23 100644
--- a/model_test.go
+++ b/model_test.go
@@ -1,9 +1,14 @@
 package pop
 
 import (
+	"context"
+	"fmt"
 	"testing"
 	"time"
 
+	"github.com/gobuffalo/pop/v5/testdata/models/ac"
+	"github.com/gobuffalo/pop/v5/testdata/models/bc"
+
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
@@ -12,26 +17,25 @@ import (
 )
 
 func Test_Model_TableName(t *testing.T) {
-	r := require.New(t)
-
-	m := Model{Value: User{}}
-	r.Equal(m.TableName(), "users")
-
-	m = Model{Value: &User{}}
-	r.Equal(m.TableName(), "users")
-
-	m = Model{Value: &Users{}}
-	r.Equal(m.TableName(), "users")
-
-	m = Model{Value: []User{}}
-	r.Equal(m.TableName(), "users")
-
-	m = Model{Value: &[]User{}}
-	r.Equal(m.TableName(), "users")
-
-	m = Model{Value: []*User{}}
-	r.Equal(m.TableName(), "users")
-
+	for k, v := range []interface{}{
+		User{},
+		&User{},
+
+		&Users{},
+		Users{},
+
+		[]*User{},
+		&[]*User{},
+
+		[]User{},
+		&[]User{},
+	} {
+		t.Run(fmt.Sprintf("case=%d", k), func(t *testing.T) {
+			r := require.New(t)
+			m := Model{Value: v}
+			r.Equal("users", m.TableName())
+		})
+	}
 }
 
 type tn struct{}
@@ -40,6 +44,12 @@ func (tn) TableName() string {
 	return "this is my table name"
 }
 
+type tnc struct{}
+
+func (tnc) TableName(ctx context.Context) string {
+	return ctx.Value("name").(string)
+}
+
 // A failing test case for #477
 func Test_TableNameCache(t *testing.T) {
 	r := assert.New(t)
@@ -49,12 +59,27 @@ func Test_TableNameCache(t *testing.T) {
 	r.Equal("userb", (&Model{Value: []b.User{}}).TableName())
 }
 
+// A failing test case for #477
+func Test_TableNameContextCache(t *testing.T) {
+	ctx := context.WithValue(context.Background(), "name", "context_table")
+
+	r := assert.New(t)
+	r.Equal("context_table_useras", (&Model{Value: ac.User{}, ctx: ctx}).TableName())
+	r.Equal("context_table_userbs", (&Model{Value: bc.User{}, ctx: ctx}).TableName())
+	r.Equal("context_table_useras", (&Model{Value: []ac.User{}, ctx: ctx}).TableName())
+	r.Equal("context_table_userbs", (&Model{Value: []bc.User{}, ctx: ctx}).TableName())
+}
+
 func Test_TableName(t *testing.T) {
 	r := require.New(t)
 
 	cases := []interface{}{
 		tn{},
+		&tn{},
 		[]tn{},
+		&[]tn{},
+		[]*tn{},
+		&[]*tn{},
 	}
 	for _, tc := range cases {
 		m := Model{Value: tc}
@@ -62,6 +87,22 @@ func Test_TableName(t *testing.T) {
 	}
 }
 
+func Test_TableNameContext(t *testing.T) {
+	r := require.New(t)
+
+	tn := "context_table_names"
+	ctx := context.WithValue(context.Background(), "name", tn)
+
+	cases := []interface{}{
+		tnc{},
+		[]tnc{},
+	}
+	for _, tc := range cases {
+		m := Model{Value: tc, ctx: ctx}
+		r.Equal(tn, m.TableName())
+	}
+}
+
 type TimeTimestamp struct {
 	ID        int       `db:"id"`
 	CreatedAt time.Time `db:"created_at"`
@@ -77,7 +118,7 @@ type UnixTimestamp struct {
 func Test_Touch_Time_Timestamp(t *testing.T) {
 	r := require.New(t)
 
-	m := Model{Value: &TimeTimestamp{}}
+	m := NewModel(&TimeTimestamp{}, context.Background())
 
 	// Override time.Now()
 	t0, _ := time.Parse(time.RFC3339, "2019-07-14T00:00:00Z")
@@ -101,7 +142,7 @@ func Test_Touch_Time_Timestamp_With_Existing_Value(t *testing.T) {
 
 	createdAt := nowFunc().Add(-36 * time.Hour)
 
-	m := Model{Value: &TimeTimestamp{CreatedAt: createdAt}}
+	m := NewModel(&TimeTimestamp{CreatedAt: createdAt}, context.Background())
 	m.touchCreatedAt()
 	m.touchUpdatedAt()
 	v := m.Value.(*TimeTimestamp)
@@ -112,7 +153,7 @@ func Test_Touch_Time_Timestamp_With_Existing_Value(t *testing.T) {
 func Test_Touch_Unix_Timestamp(t *testing.T) {
 	r := require.New(t)
 
-	m := Model{Value: &UnixTimestamp{}}
+	m := NewModel(&UnixTimestamp{}, context.Background())
 
 	// Override time.Now()
 	t0, _ := time.Parse(time.RFC3339, "2019-07-14T00:00:00Z")
@@ -136,7 +177,7 @@ func Test_Touch_Unix_Timestamp_With_Existing_Value(t *testing.T) {
 
 	createdAt := int(time.Now().Add(-36 * time.Hour).Unix())
 
-	m := Model{Value: &UnixTimestamp{CreatedAt: createdAt}}
+	m := NewModel(&UnixTimestamp{CreatedAt: createdAt}, context.Background())
 	m.touchCreatedAt()
 	m.touchUpdatedAt()
 	v := m.Value.(*UnixTimestamp)
@@ -159,3 +200,25 @@ func Test_IDField(t *testing.T) {
 	m = Model{Value: &testNormalID{ID: 1}}
 	r.Equal("id", m.IDField())
 }
+
+type testPrefixID struct {
+	ID int `db:"custom_id"`
+}
+
+func (t testPrefixID) TableName() string {
+	return "foo.bar"
+}
+
+func Test_WhereID(t *testing.T) {
+	r := require.New(t)
+	m := Model{Value: &testPrefixID{ID: 1}}
+
+	r.Equal("foo_bar.custom_id = ?", m.whereID())
+	r.Equal("foo_bar.custom_id = :custom_id", m.whereNamedID())
+
+	type testNormalID struct {
+		ID int
+	}
+	m = Model{Value: &testNormalID{ID: 1}}
+	r.Equal("id", m.IDField())
+}
diff --git a/pop_test.go b/pop_test.go
index 33efcbe..1d473de 100644
--- a/pop_test.go
+++ b/pop_test.go
@@ -138,14 +138,16 @@ type Book struct {
 }
 
 type Taxi struct {
-	ID        int       `db:"id"`
-	Model     string    `db:"model"`
-	UserID    nulls.Int `db:"user_id"`
-	AddressID nulls.Int `db:"address_id"`
-	Driver    *User     `belongs_to:"user" fk_id:"user_id"`
-	Address   Address   `belongs_to:"address"`
-	CreatedAt time.Time `db:"created_at"`
-	UpdatedAt time.Time `db:"updated_at"`
+	ID          int       `db:"id"`
+	Model       string    `db:"model"`
+	UserID      nulls.Int `db:"user_id"`
+	AddressID   nulls.Int `db:"address_id"`
+	Driver      *User     `belongs_to:"user" fk_id:"user_id"`
+	Address     Address   `belongs_to:"address"`
+	ToAddressID *int      `db:"to_address_id"`
+	ToAddress   *Address  `belongs_to:"address"`
+	CreatedAt   time.Time `db:"created_at"`
+	UpdatedAt   time.Time `db:"updated_at"`
 }
 
 // Validate gets run every time you call a "Validate*" (ValidateAndSave, ValidateAndCreate, ValidateAndUpdate) method.
@@ -419,3 +421,8 @@ type CrookedSong struct {
 	CreatedAt time.Time `db:"created_at"`
 	UpdatedAt time.Time `db:"updated_at"`
 }
+
+type NonStandardID struct {
+	ID          int    `db:"pk"`
+	OutfacingID string `db:"id"`
+}
diff --git a/preload_associations_test.go b/preload_associations_test.go
index af1f392..9d735fd 100644
--- a/preload_associations_test.go
+++ b/preload_associations_test.go
@@ -231,3 +231,36 @@ func Test_New_Implementation_For_BelongsTo_Multiple_Fields(t *testing.T) {
 		SetEagerMode(EagerDefault)
 	})
 }
+
+func Test_New_Implementation_For_BelongsTo_Ptr_Field(t *testing.T) {
+	if PDB == nil {
+		t.Skip("skipping integration tests")
+	}
+	transaction(func(tx *Connection) {
+		a := require.New(t)
+		toAddress := Address{HouseNumber: 1, Street: "Destination Ave"}
+		a.NoError(tx.Create(&toAddress))
+
+		taxi := Taxi{ToAddressID: &toAddress.ID}
+		a.NoError(tx.Create(&taxi))
+
+		book1 := Book{TaxiID: nulls.NewInt(taxi.ID), Title: "My Book"}
+		a.NoError(tx.Create(&book1))
+
+		taxiNilToAddress := Taxi{ToAddressID: nil}
+		a.NoError(tx.Create(&taxiNilToAddress))
+
+		book2 := Book{TaxiID: nulls.NewInt(taxiNilToAddress.ID), Title: "Another Book"}
+		a.NoError(tx.Create(&book2))
+
+		SetEagerMode(EagerPreload)
+		books := []Book{}
+		a.NoError(tx.EagerPreload("Taxi.ToAddress").Order("created_at").All(&books))
+		a.Len(books, 2)
+		a.Equal(toAddress.Street, books[0].Taxi.ToAddress.Street)
+		a.NotNil(books[0].Taxi.ToAddressID)
+		a.Nil(books[1].Taxi.ToAddress)
+		a.Nil(books[1].Taxi.ToAddressID)
+		SetEagerMode(EagerDefault)
+	})
+}
diff --git a/query_test.go b/query_test.go
index 93da765..7df5a99 100644
--- a/query_test.go
+++ b/query_test.go
@@ -1,6 +1,7 @@
 package pop
 
 import (
+	"context"
 	"fmt"
 	"testing"
 
@@ -13,7 +14,7 @@ func Test_Where(t *testing.T) {
 		t.Skip("skipping integration tests")
 	}
 	a := require.New(t)
-	m := &Model{Value: &Enemy{}}
+	m := NewModel(new(Enemy), context.Background())
 
 	q := PDB.Where("id = ?", 1)
 	sql, _ := q.ToSQL(m)
@@ -107,7 +108,7 @@ func Test_Order(t *testing.T) {
 	}
 	a := require.New(t)
 
-	m := &Model{Value: &Enemy{}}
+	m := NewModel(&Enemy{}, context.Background())
 	q := PDB.Order("id desc")
 	sql, _ := q.ToSQL(m)
 	a.Equal(ts("SELECT enemies.A FROM enemies AS enemies ORDER BY id desc"), sql)
@@ -123,7 +124,7 @@ func Test_GroupBy(t *testing.T) {
 	}
 	a := require.New(t)
 
-	m := &Model{Value: &Enemy{}}
+	m := NewModel(&Enemy{}, context.Background())
 	q := PDB.Q()
 	q.GroupBy("A")
 	sql, _ := q.ToSQL(m)
@@ -159,7 +160,7 @@ func Test_ToSQL(t *testing.T) {
 	}
 	a := require.New(t)
 	transaction(func(tx *Connection) {
-		user := &Model{Value: &User{}}
+		user := NewModel(&User{}, tx.Context())
 
 		s := "SELECT name as full_name, users.alive, users.bio, users.birth_date, users.created_at, users.email, users.id, users.name, users.price, users.updated_at, users.user_name FROM users AS users"
 
@@ -171,10 +172,10 @@ func Test_ToSQL(t *testing.T) {
 		q, _ = query.ToSQL(user)
 		a.Equal(fmt.Sprintf("%s ORDER BY id desc", s), q)
 
-		q, _ = query.ToSQL(&Model{Value: &User{}, As: "u"})
+		q, _ = query.ToSQL(&Model{Value: &User{}, As: "u", ctx: tx.Context()})
 		a.Equal("SELECT name as full_name, u.alive, u.bio, u.birth_date, u.created_at, u.email, u.id, u.name, u.price, u.updated_at, u.user_name FROM users AS u ORDER BY id desc", q)
 
-		q, _ = query.ToSQL(&Model{Value: &Family{}})
+		q, _ = query.ToSQL(&Model{Value: &Family{}, ctx: tx.Context()})
 		a.Equal("SELECT family_members.created_at, family_members.first_name, family_members.id, family_members.last_name, family_members.updated_at FROM family.members AS family_members ORDER BY id desc", q)
 
 		query = tx.Where("id = 1")
@@ -262,7 +263,7 @@ func Test_ToSQLInjection(t *testing.T) {
 	}
 	a := require.New(t)
 	transaction(func(tx *Connection) {
-		user := &Model{Value: &User{}}
+		user := NewModel(new(User), tx.Context())
 		query := tx.Where("name = '?'", "\\\u0027 or 1=1 limit 1;\n-- ")
 		q, _ := query.ToSQL(user)
 		a.NotEqual("SELECT * FROM users AS users WHERE name = '\\'' or 1=1 limit 1;\n-- '", q)
diff --git a/scopes_test.go b/scopes_test.go
index 0e22b76..f393ffb 100644
--- a/scopes_test.go
+++ b/scopes_test.go
@@ -1,6 +1,7 @@
 package pop
 
 import (
+	"context"
 	"testing"
 
 	"github.com/stretchr/testify/require"
@@ -13,7 +14,7 @@ func Test_Scopes(t *testing.T) {
 	r := require.New(t)
 	oql := "SELECT enemies.A FROM enemies AS enemies"
 
-	m := &Model{Value: &Enemy{}}
+	m := NewModel(new(Enemy), context.Background())
 
 	q := PDB.Q()
 	s, _ := q.ToSQL(m)
diff --git a/slices/map_test.go b/slices/map_test.go
index e5d4963..265d994 100644
--- a/slices/map_test.go
+++ b/slices/map_test.go
@@ -24,3 +24,14 @@ func Test_Map_MarshalJSON(t *testing.T) {
 	r.NoError(err)
 	r.Equal([]byte(`{"a":"b"}`), b)
 }
+
+func Test_Map_UnMarshalJSON_uninitialized_map_does_not_panic(t *testing.T) {
+	r := require.New(t)
+
+	maps := make([]Map, 0)
+	r.NotPanics(func() {
+		err := json.Unmarshal([]byte(`[{"a": "b"}]`), &maps)
+		r.NoError(err)
+		r.Len(maps, 1)
+	})
+}
diff --git a/testdata/migrations/20181104135856_taxis.up.fizz b/testdata/migrations/20181104135856_taxis.up.fizz
index 9cf8523..a11185c 100644
--- a/testdata/migrations/20181104135856_taxis.up.fizz
+++ b/testdata/migrations/20181104135856_taxis.up.fizz
@@ -2,6 +2,7 @@ create_table("taxis") {
   t.Column("id", "int", {primary: true})
   t.Column("model", "string", {})
   t.Column("user_id", "int", {"null": true})
-  t.Column("address_id", "int",{"null":true})
+  t.Column("address_id", "int", {"null":true})
+  t.Column("to_address_id", "int", {"null":true})
   t.Timestamps()
 }
\ No newline at end of file
diff --git a/testdata/migrations/20201028153041_non_standard_id.down.fizz b/testdata/migrations/20201028153041_non_standard_id.down.fizz
new file mode 100644
index 0000000..5c56284
--- /dev/null
+++ b/testdata/migrations/20201028153041_non_standard_id.down.fizz
@@ -0,0 +1,1 @@
+drop_table("non_standard_ids")
diff --git a/testdata/migrations/20201028153041_non_standard_id.up.fizz b/testdata/migrations/20201028153041_non_standard_id.up.fizz
new file mode 100644
index 0000000..7590e4b
--- /dev/null
+++ b/testdata/migrations/20201028153041_non_standard_id.up.fizz
@@ -0,0 +1,6 @@
+create_table("non_standard_ids") {
+    t.Column("pk", "int", { primary: true })
+    t.Column("id", "string", {})
+
+    t.DisableTimestamps()
+}
diff --git a/testdata/migrations/20210104145901_context_tables.down.fizz b/testdata/migrations/20210104145901_context_tables.down.fizz
new file mode 100644
index 0000000..d0f82ee
--- /dev/null
+++ b/testdata/migrations/20210104145901_context_tables.down.fizz
@@ -0,0 +1,2 @@
+drop_table("context_prefix_a_table")
+drop_table("context_prefix_b_table")
diff --git a/testdata/migrations/20210104145901_context_tables.up.fizz b/testdata/migrations/20210104145901_context_tables.up.fizz
new file mode 100644
index 0000000..ae94796
--- /dev/null
+++ b/testdata/migrations/20210104145901_context_tables.up.fizz
@@ -0,0 +1,9 @@
+create_table("context_prefix_a_table") {
+    t.Column("id", "string", { primary: true })
+    t.Column("value", "string")
+}
+
+create_table("context_prefix_b_table") {
+    t.Column("id", "string", { primary: true })
+    t.Column("value", "string")
+}
diff --git a/testdata/models/ac/user.go b/testdata/models/ac/user.go
new file mode 100644
index 0000000..92335a1
--- /dev/null
+++ b/testdata/models/ac/user.go
@@ -0,0 +1,9 @@
+package ac
+
+import "context"
+
+type User struct{}
+
+func (u User) TableName(ctx context.Context) string {
+	return ctx.Value("name").(string) + "_useras"
+}
diff --git a/testdata/models/bc/user.go b/testdata/models/bc/user.go
new file mode 100644
index 0000000..4b1c625
--- /dev/null
+++ b/testdata/models/bc/user.go
@@ -0,0 +1,9 @@
+package bc
+
+import "context"
+
+type User struct{}
+
+func (u User) TableName(ctx context.Context) string {
+	return ctx.Value("name").(string) + "_userbs"
+}