aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Kavon <me+git@alexkavon.com>2024-01-23 01:40:39 -0500
committerAlexander Kavon <me+git@alexkavon.com>2024-01-23 01:40:39 -0500
commit8510c36ded85740885e67b59ee2ec2360986c0a9 (patch)
treec8c4c70458bb65b0ee134f80cb364db851e9c7a2
parentf329e7f2095a57967bfe86563401236d53a15924 (diff)
new tables: tags, post_tags, generated sqlboiler tags model, updated posts/get template, updated posts table to take nullable description and user_id foreign key
-rw-r--r--migrations/003_create_posts_table.sql.sql3
-rw-r--r--migrations/004_create_tags_table.sql.sql24
-rw-r--r--src/main.go2
-rw-r--r--src/models/boil_relationship_test.go34
-rw-r--r--src/models/boil_suites_test.go18
-rw-r--r--src/models/boil_table_names.go12
-rw-r--r--src/models/posts.go518
-rw-r--r--src/models/posts_test.go433
-rw-r--r--src/models/psql_suites_test.go2
-rw-r--r--src/models/tags.go1469
-rw-r--r--src/models/tags_test.go1163
-rw-r--r--src/models/users.go383
-rw-r--r--src/models/users_test.go307
-rw-r--r--src/post/routes.go23
-rw-r--r--ui/pages/post/get.tmpl.html22
15 files changed, 4376 insertions, 37 deletions
diff --git a/migrations/003_create_posts_table.sql.sql b/migrations/003_create_posts_table.sql.sql
index 81dab8f..584b7b4 100644
--- a/migrations/003_create_posts_table.sql.sql
+++ b/migrations/003_create_posts_table.sql.sql
@@ -1,8 +1,9 @@
CREATE TABLE posts(
id SERIAL NOT NULL PRIMARY KEY,
title VARCHAR(100) NOT NULL,
- description TEXT NOT NULL,
+ description TEXT,
url VARCHAR(255) UNIQUE,
+ user_id INT NOT NULL REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
diff --git a/migrations/004_create_tags_table.sql.sql b/migrations/004_create_tags_table.sql.sql
new file mode 100644
index 0000000..0b184f8
--- /dev/null
+++ b/migrations/004_create_tags_table.sql.sql
@@ -0,0 +1,24 @@
+CREATE TABLE tags(
+ id SERIAL NOT NULL PRIMARY KEY,
+ tag VARCHAR(30) NOT NULL,
+ description TEXT NOT NULL,
+ user_id INT NOT NULL REFERENCES users(id),
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
+
+CREATE TABLE post_tags(
+ post_id INT NOT NULL REFERENCES posts(id) ON DELETE CASCADE,
+ tag_id INT NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
+ PRIMARY KEY (post_id, tag_id)
+);
+
+CREATE TRIGGER set_timestamp
+BEFORE UPDATE ON tags
+FOR EACH ROW
+EXECUTE PROCEDURE trigger_set_timestamp();
+
+---- create above / drop below ----
+
+DROP TABLE post_tags;
+DROP TABLE tags;
diff --git a/src/main.go b/src/main.go
index 4dc2caf..208944f 100644
--- a/src/main.go
+++ b/src/main.go
@@ -3,6 +3,7 @@ package main
import (
"gitlab.com/alexkavon/newsstand/src/conf"
"gitlab.com/alexkavon/newsstand/src/db"
+ "gitlab.com/alexkavon/newsstand/src/post"
"gitlab.com/alexkavon/newsstand/src/server"
"gitlab.com/alexkavon/newsstand/src/user"
)
@@ -18,6 +19,7 @@ func main() {
s.BuildUi()
routers := []server.Routes{
user.Routes,
+ post.Routes,
}
for _, r := range routers {
s.RegisterRoutes(r)
diff --git a/src/models/boil_relationship_test.go b/src/models/boil_relationship_test.go
index c4d831d..191d17c 100644
--- a/src/models/boil_relationship_test.go
+++ b/src/models/boil_relationship_test.go
@@ -7,7 +7,10 @@ import "testing"
// TestToOne tests cannot be run in parallel
// or deadlocks can occur.
-func TestToOne(t *testing.T) {}
+func TestToOne(t *testing.T) {
+ t.Run("PostToUserUsingUser", testPostToOneUserUsingUser)
+ t.Run("TagToUserUsingUser", testTagToOneUserUsingUser)
+}
// TestOneToOne tests cannot be run in parallel
// or deadlocks can occur.
@@ -15,11 +18,19 @@ func TestOneToOne(t *testing.T) {}
// TestToMany tests cannot be run in parallel
// or deadlocks can occur.
-func TestToMany(t *testing.T) {}
+func TestToMany(t *testing.T) {
+ t.Run("PostToTags", testPostToManyTags)
+ t.Run("TagToPosts", testTagToManyPosts)
+ t.Run("UserToPosts", testUserToManyPosts)
+ t.Run("UserToTags", testUserToManyTags)
+}
// TestToOneSet tests cannot be run in parallel
// or deadlocks can occur.
-func TestToOneSet(t *testing.T) {}
+func TestToOneSet(t *testing.T) {
+ t.Run("PostToUserUsingPosts", testPostToOneSetOpUserUsingUser)
+ t.Run("TagToUserUsingTags", testTagToOneSetOpUserUsingUser)
+}
// TestToOneRemove tests cannot be run in parallel
// or deadlocks can occur.
@@ -35,12 +46,23 @@ func TestOneToOneRemove(t *testing.T) {}
// TestToManyAdd tests cannot be run in parallel
// or deadlocks can occur.
-func TestToManyAdd(t *testing.T) {}
+func TestToManyAdd(t *testing.T) {
+ t.Run("PostToTags", testPostToManyAddOpTags)
+ t.Run("TagToPosts", testTagToManyAddOpPosts)
+ t.Run("UserToPosts", testUserToManyAddOpPosts)
+ t.Run("UserToTags", testUserToManyAddOpTags)
+}
// TestToManySet tests cannot be run in parallel
// or deadlocks can occur.
-func TestToManySet(t *testing.T) {}
+func TestToManySet(t *testing.T) {
+ t.Run("PostToTags", testPostToManySetOpTags)
+ t.Run("TagToPosts", testTagToManySetOpPosts)
+}
// TestToManyRemove tests cannot be run in parallel
// or deadlocks can occur.
-func TestToManyRemove(t *testing.T) {}
+func TestToManyRemove(t *testing.T) {
+ t.Run("PostToTags", testPostToManyRemoveOpTags)
+ t.Run("TagToPosts", testTagToManyRemoveOpPosts)
+}
diff --git a/src/models/boil_suites_test.go b/src/models/boil_suites_test.go
index d1e1758..33d14ea 100644
--- a/src/models/boil_suites_test.go
+++ b/src/models/boil_suites_test.go
@@ -13,87 +13,105 @@ import "testing"
// Separating the tests thusly grants avoidance of Postgres deadlocks.
func TestParent(t *testing.T) {
t.Run("Posts", testPosts)
+ t.Run("Tags", testTags)
t.Run("Users", testUsers)
}
func TestDelete(t *testing.T) {
t.Run("Posts", testPostsDelete)
+ t.Run("Tags", testTagsDelete)
t.Run("Users", testUsersDelete)
}
func TestQueryDeleteAll(t *testing.T) {
t.Run("Posts", testPostsQueryDeleteAll)
+ t.Run("Tags", testTagsQueryDeleteAll)
t.Run("Users", testUsersQueryDeleteAll)
}
func TestSliceDeleteAll(t *testing.T) {
t.Run("Posts", testPostsSliceDeleteAll)
+ t.Run("Tags", testTagsSliceDeleteAll)
t.Run("Users", testUsersSliceDeleteAll)
}
func TestExists(t *testing.T) {
t.Run("Posts", testPostsExists)
+ t.Run("Tags", testTagsExists)
t.Run("Users", testUsersExists)
}
func TestFind(t *testing.T) {
t.Run("Posts", testPostsFind)
+ t.Run("Tags", testTagsFind)
t.Run("Users", testUsersFind)
}
func TestBind(t *testing.T) {
t.Run("Posts", testPostsBind)
+ t.Run("Tags", testTagsBind)
t.Run("Users", testUsersBind)
}
func TestOne(t *testing.T) {
t.Run("Posts", testPostsOne)
+ t.Run("Tags", testTagsOne)
t.Run("Users", testUsersOne)
}
func TestAll(t *testing.T) {
t.Run("Posts", testPostsAll)
+ t.Run("Tags", testTagsAll)
t.Run("Users", testUsersAll)
}
func TestCount(t *testing.T) {
t.Run("Posts", testPostsCount)
+ t.Run("Tags", testTagsCount)
t.Run("Users", testUsersCount)
}
func TestHooks(t *testing.T) {
t.Run("Posts", testPostsHooks)
+ t.Run("Tags", testTagsHooks)
t.Run("Users", testUsersHooks)
}
func TestInsert(t *testing.T) {
t.Run("Posts", testPostsInsert)
t.Run("Posts", testPostsInsertWhitelist)
+ t.Run("Tags", testTagsInsert)
+ t.Run("Tags", testTagsInsertWhitelist)
t.Run("Users", testUsersInsert)
t.Run("Users", testUsersInsertWhitelist)
}
func TestReload(t *testing.T) {
t.Run("Posts", testPostsReload)
+ t.Run("Tags", testTagsReload)
t.Run("Users", testUsersReload)
}
func TestReloadAll(t *testing.T) {
t.Run("Posts", testPostsReloadAll)
+ t.Run("Tags", testTagsReloadAll)
t.Run("Users", testUsersReloadAll)
}
func TestSelect(t *testing.T) {
t.Run("Posts", testPostsSelect)
+ t.Run("Tags", testTagsSelect)
t.Run("Users", testUsersSelect)
}
func TestUpdate(t *testing.T) {
t.Run("Posts", testPostsUpdate)
+ t.Run("Tags", testTagsUpdate)
t.Run("Users", testUsersUpdate)
}
func TestSliceUpdateAll(t *testing.T) {
t.Run("Posts", testPostsSliceUpdateAll)
+ t.Run("Tags", testTagsSliceUpdateAll)
t.Run("Users", testUsersSliceUpdateAll)
}
diff --git a/src/models/boil_table_names.go b/src/models/boil_table_names.go
index c88a449..29a98ba 100644
--- a/src/models/boil_table_names.go
+++ b/src/models/boil_table_names.go
@@ -4,9 +4,13 @@
package models
var TableNames = struct {
- Posts string
- Users string
+ PostTags string
+ Posts string
+ Tags string
+ Users string
}{
- Posts: "posts",
- Users: "users",
+ PostTags: "post_tags",
+ Posts: "posts",
+ Tags: "tags",
+ Users: "users",
}
diff --git a/src/models/posts.go b/src/models/posts.go
index d644268..7fb455c 100644
--- a/src/models/posts.go
+++ b/src/models/posts.go
@@ -26,8 +26,9 @@ import (
type Post struct {
ID int `boil:"id" json:"id" toml:"id" yaml:"id"`
Title string `boil:"title" json:"title" toml:"title" yaml:"title"`
+ Description null.String `boil:"description" json:"description,omitempty" toml:"description" yaml:"description,omitempty"`
URL null.String `boil:"url" json:"url,omitempty" toml:"url" yaml:"url,omitempty"`
- Description string `boil:"description" json:"description" toml:"description" yaml:"description"`
+ UserID int `boil:"user_id" json:"user_id" toml:"user_id" yaml:"user_id"`
CreatedAt time.Time `boil:"created_at" json:"created_at" toml:"created_at" yaml:"created_at"`
UpdatedAt time.Time `boil:"updated_at" json:"updated_at" toml:"updated_at" yaml:"updated_at"`
@@ -38,15 +39,17 @@ type Post struct {
var PostColumns = struct {
ID string
Title string
- URL string
Description string
+ URL string
+ UserID string
CreatedAt string
UpdatedAt string
}{
ID: "id",
Title: "title",
- URL: "url",
Description: "description",
+ URL: "url",
+ UserID: "user_id",
CreatedAt: "created_at",
UpdatedAt: "updated_at",
}
@@ -54,15 +57,17 @@ var PostColumns = struct {
var PostTableColumns = struct {
ID string
Title string
- URL string
Description string
+ URL string
+ UserID string
CreatedAt string
UpdatedAt string
}{
ID: "posts.id",
Title: "posts.title",
- URL: "posts.url",
Description: "posts.description",
+ URL: "posts.url",
+ UserID: "posts.user_id",
CreatedAt: "posts.created_at",
UpdatedAt: "posts.updated_at",
}
@@ -193,25 +198,34 @@ func (w whereHelpertime_Time) GTE(x time.Time) qm.QueryMod {
var PostWhere = struct {
ID whereHelperint
Title whereHelperstring
+ Description whereHelpernull_String
URL whereHelpernull_String
- Description whereHelperstring
+ UserID whereHelperint
CreatedAt whereHelpertime_Time
UpdatedAt whereHelpertime_Time
}{
ID: whereHelperint{field: "\"posts\".\"id\""},
Title: whereHelperstring{field: "\"posts\".\"title\""},
+ Description: whereHelpernull_String{field: "\"posts\".\"description\""},
URL: whereHelpernull_String{field: "\"posts\".\"url\""},
- Description: whereHelperstring{field: "\"posts\".\"description\""},
+ UserID: whereHelperint{field: "\"posts\".\"user_id\""},
CreatedAt: whereHelpertime_Time{field: "\"posts\".\"created_at\""},
UpdatedAt: whereHelpertime_Time{field: "\"posts\".\"updated_at\""},
}
// PostRels is where relationship names are stored.
var PostRels = struct {
-}{}
+ User string
+ Tags string
+}{
+ User: "User",
+ Tags: "Tags",
+}
// postR is where relationships are stored.
type postR struct {
+ User *User `boil:"User" json:"User" toml:"User" yaml:"User"`
+ Tags TagSlice `boil:"Tags" json:"Tags" toml:"Tags" yaml:"Tags"`
}
// NewStruct creates a new relationship struct
@@ -219,13 +233,27 @@ func (*postR) NewStruct() *postR {
return &postR{}
}
+func (r *postR) GetUser() *User {
+ if r == nil {
+ return nil
+ }
+ return r.User
+}
+
+func (r *postR) GetTags() TagSlice {
+ if r == nil {
+ return nil
+ }
+ return r.Tags
+}
+
// postL is where Load methods for each relationship are stored.
type postL struct{}
var (
- postAllColumns = []string{"id", "title", "url", "description", "created_at", "updated_at"}
- postColumnsWithoutDefault = []string{"title", "description"}
- postColumnsWithDefault = []string{"id", "url", "created_at", "updated_at"}
+ postAllColumns = []string{"id", "title", "description", "url", "user_id", "created_at", "updated_at"}
+ postColumnsWithoutDefault = []string{"title", "user_id"}
+ postColumnsWithDefault = []string{"id", "description", "url", "created_at", "updated_at"}
postPrimaryKeyColumns = []string{"id"}
postGeneratedColumns = []string{}
)
@@ -535,6 +563,474 @@ func (q postQuery) Exists(ctx context.Context, exec boil.ContextExecutor) (bool,
return count > 0, nil
}
+// User pointed to by the foreign key.
+func (o *Post) User(mods ...qm.QueryMod) userQuery {
+ queryMods := []qm.QueryMod{
+ qm.Where("\"id\" = ?", o.UserID),
+ }
+
+ queryMods = append(queryMods, mods...)
+
+ return Users(queryMods...)
+}
+
+// Tags retrieves all the tag's Tags with an executor.
+func (o *Post) Tags(mods ...qm.QueryMod) tagQuery {
+ var queryMods []qm.QueryMod
+ if len(mods) != 0 {
+ queryMods = append(queryMods, mods...)
+ }
+
+ queryMods = append(queryMods,
+ qm.InnerJoin("\"post_tags\" on \"tags\".\"id\" = \"post_tags\".\"tag_id\""),
+ qm.Where("\"post_tags\".\"post_id\"=?", o.ID),
+ )
+
+ return Tags(queryMods...)
+}
+
+// LoadUser allows an eager lookup of values, cached into the
+// loaded structs of the objects. This is for an N-1 relationship.
+func (postL) LoadUser(ctx context.Context, e boil.ContextExecutor, singular bool, maybePost interface{}, mods queries.Applicator) error {
+ var slice []*Post
+ var object *Post
+
+ if singular {
+ var ok bool
+ object, ok = maybePost.(*Post)
+ if !ok {
+ object = new(Post)
+ ok = queries.SetFromEmbeddedStruct(&object, &maybePost)
+ if !ok {
+ return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", object, maybePost))
+ }
+ }
+ } else {
+ s, ok := maybePost.(*[]*Post)
+ if ok {
+ slice = *s
+ } else {
+ ok = queries.SetFromEmbeddedStruct(&slice, maybePost)
+ if !ok {
+ return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", slice, maybePost))
+ }
+ }
+ }
+
+ args := make(map[interface{}]struct{})
+ if singular {
+ if object.R == nil {
+ object.R = &postR{}
+ }
+ args[object.UserID] = struct{}{}
+
+ } else {
+ for _, obj := range slice {
+ if obj.R == nil {
+ obj.R = &postR{}
+ }
+
+ args[obj.UserID] = struct{}{}
+
+ }
+ }
+
+ if len(args) == 0 {
+ return nil
+ }
+
+ argsSlice := make([]interface{}, len(args))
+ i := 0
+ for arg := range args {
+ argsSlice[i] = arg
+ i++
+ }
+
+ query := NewQuery(
+ qm.From(`users`),
+ qm.WhereIn(`users.id in ?`, argsSlice...),
+ )
+ if mods != nil {
+ mods.Apply(query)
+ }
+
+ results, err := query.QueryContext(ctx, e)
+ if err != nil {
+ return errors.Wrap(err, "failed to eager load User")
+ }
+
+ var resultSlice []*User
+ if err = queries.Bind(results, &resultSlice); err != nil {
+ return errors.Wrap(err, "failed to bind eager loaded slice User")
+ }
+
+ if err = results.Close(); err != nil {
+ return errors.Wrap(err, "failed to close results of eager load for users")
+ }
+ if err = results.Err(); err != nil {
+ return errors.Wrap(err, "error occurred during iteration of eager loaded relations for users")
+ }
+
+ if len(userAfterSelectHooks) != 0 {
+ for _, obj := range resultSlice {
+ if err := obj.doAfterSelectHooks(ctx, e); err != nil {
+ return err
+ }
+ }
+ }
+
+ if len(resultSlice) == 0 {
+ return nil
+ }
+
+ if singular {
+ foreign := resultSlice[0]
+ object.R.User = foreign
+ if foreign.R == nil {
+ foreign.R = &userR{}
+ }
+ foreign.R.Posts = append(foreign.R.Posts, object)
+ return nil
+ }
+
+ for _, local := range slice {
+ for _, foreign := range resultSlice {
+ if local.UserID == foreign.ID {
+ local.R.User = foreign
+ if foreign.R == nil {
+ foreign.R = &userR{}
+ }
+ foreign.R.Posts = append(foreign.R.Posts, local)
+ break
+ }
+ }
+ }
+
+ return nil
+}
+
+// LoadTags allows an eager lookup of values, cached into the
+// loaded structs of the objects. This is for a 1-M or N-M relationship.
+func (postL) LoadTags(ctx context.Context, e boil.ContextExecutor, singular bool, maybePost interface{}, mods queries.Applicator) error {
+ var slice []*Post
+ var object *Post
+
+ if singular {
+ var ok bool
+ object, ok = maybePost.(*Post)
+ if !ok {
+ object = new(Post)
+ ok = queries.SetFromEmbeddedStruct(&object, &maybePost)
+ if !ok {
+ return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", object, maybePost))
+ }
+ }
+ } else {
+ s, ok := maybePost.(*[]*Post)
+ if ok {
+ slice = *s
+ } else {
+ ok = queries.SetFromEmbeddedStruct(&slice, maybePost)
+ if !ok {
+ return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", slice, maybePost))
+ }
+ }
+ }
+
+ args := make(map[interface{}]struct{})
+ if singular {
+ if object.R == nil {
+ object.R = &postR{}
+ }
+ args[object.ID] = struct{}{}
+ } else {
+ for _, obj := range slice {
+ if obj.R == nil {
+ obj.R = &postR{}
+ }
+ args[obj.ID] = struct{}{}
+ }
+ }
+
+ if len(args) == 0 {
+ return nil
+ }
+
+ argsSlice := make([]interface{}, len(args))
+ i := 0
+ for arg := range args {
+ argsSlice[i] = arg
+ i++
+ }
+
+ query := NewQuery(
+ qm.Select("\"tags\".\"id\", \"tags\".\"tag\", \"tags\".\"description\", \"tags\".\"user_id\", \"tags\".\"created_at\", \"tags\".\"updated_at\", \"a\".\"post_id\""),
+ qm.From("\"tags\""),
+ qm.InnerJoin("\"post_tags\" as \"a\" on \"tags\".\"id\" = \"a\".\"tag_id\""),
+ qm.WhereIn("\"a\".\"post_id\" in ?", argsSlice...),
+ )
+ if mods != nil {
+ mods.Apply(query)
+ }
+
+ results, err := query.QueryContext(ctx, e)
+ if err != nil {
+ return errors.Wrap(err, "failed to eager load tags")
+ }
+
+ var resultSlice []*Tag
+
+ var localJoinCols []int
+ for results.Next() {
+ one := new(Tag)
+ var localJoinCol int
+
+ err = results.Scan(&one.ID, &one.Tag, &one.Description, &one.UserID, &one.CreatedAt, &one.UpdatedAt, &localJoinCol)
+ if err != nil {
+ return errors.Wrap(err, "failed to scan eager loaded results for tags")
+ }
+ if err = results.Err(); err != nil {
+ return errors.Wrap(err, "failed to plebian-bind eager loaded slice tags")
+ }
+
+ resultSlice = append(resultSlice, one)
+ localJoinCols = append(localJoinCols, localJoinCol)
+ }
+
+ if err = results.Close(); err != nil {
+ return errors.Wrap(err, "failed to close results in eager load on tags")
+ }
+ if err = results.Err(); err != nil {
+ return errors.Wrap(err, "error occurred during iteration of eager loaded relations for tags")
+ }
+
+ if len(tagAfterSelectHooks) != 0 {
+ for _, obj := range resultSlice {
+ if err := obj.doAfterSelectHooks(ctx, e); err != nil {
+ return err
+ }
+ }
+ }
+ if singular {
+ object.R.Tags = resultSlice
+ for _, foreign := range resultSlice {
+ if foreign.R == nil {
+ foreign.R = &tagR{}
+ }
+ foreign.R.Posts = append(foreign.R.Posts, object)
+ }
+ return nil
+ }
+
+ for i, foreign := range resultSlice {
+ localJoinCol := localJoinCols[i]
+ for _, local := range slice {
+ if local.ID == localJoinCol {
+ local.R.Tags = append(local.R.Tags, foreign)
+ if foreign.R == nil {
+ foreign.R = &tagR{}
+ }
+ foreign.R.Posts = append(foreign.R.Posts, local)
+ break
+ }
+ }
+ }
+
+ return nil
+}
+
+// SetUser of the post to the related item.
+// Sets o.R.User to related.
+// Adds o to related.R.Posts.
+func (o *Post) SetUser(ctx context.Context, exec boil.ContextExecutor, insert bool, related *User) error {
+ var err error
+ if insert {
+ if err = related.Insert(ctx, exec, boil.Infer()); err != nil {
+ return errors.Wrap(err, "failed to insert into foreign table")
+ }
+ }
+
+ updateQuery := fmt.Sprintf(
+ "UPDATE \"posts\" SET %s WHERE %s",
+ strmangle.SetParamNames("\"", "\"", 1, []string{"user_id"}),
+ strmangle.WhereClause("\"", "\"", 2, postPrimaryKeyColumns),
+ )
+ values := []interface{}{related.ID, o.ID}
+
+ if boil.IsDebug(ctx) {
+ writer := boil.DebugWriterFrom(ctx)
+ fmt.Fprintln(writer, updateQuery)
+ fmt.Fprintln(writer, values)
+ }
+ if _, err = exec.ExecContext(ctx, updateQuery, values...); err != nil {
+ return errors.Wrap(err, "failed to update local table")
+ }
+
+ o.UserID = related.ID
+ if o.R == nil {
+ o.R = &postR{
+ User: related,
+ }
+ } else {
+ o.R.User = related
+ }
+
+ if related.R == nil {
+ related.R = &userR{
+ Posts: PostSlice{o},
+ }
+ } else {
+ related.R.Posts = append(related.R.Posts, o)
+ }
+
+ return nil
+}
+
+// AddTags adds the given related objects to the existing relationships
+// of the post, optionally inserting them as new records.
+// Appends related to o.R.Tags.
+// Sets related.R.Posts appropriately.
+func (o *Post) AddTags(ctx context.Context, exec boil.ContextExecutor, insert bool, related ...*Tag) error {
+ var err error
+ for _, rel := range related {
+ if insert {
+ if err = rel.Insert(ctx, exec, boil.Infer()); err != nil {
+ return errors.Wrap(err, "failed to insert into foreign table")
+ }
+ }
+ }
+
+ for _, rel := range related {
+ query := "insert into \"post_tags\" (\"post_id\", \"tag_id\") values ($1, $2)"
+ values := []interface{}{o.ID, rel.ID}
+
+ if boil.IsDebug(ctx) {
+ writer := boil.DebugWriterFrom(ctx)
+ fmt.Fprintln(writer, query)
+ fmt.Fprintln(writer, values)
+ }
+ _, err = exec.ExecContext(ctx, query, values...)
+ if err != nil {
+ return errors.Wrap(err, "failed to insert into join table")
+ }
+ }
+ if o.R == nil {
+ o.R = &postR{
+ Tags: related,
+ }
+ } else {
+ o.R.Tags = append(o.R.Tags, related...)
+ }
+
+ for _, rel := range related {
+ if rel.R == nil {
+ rel.R = &tagR{
+ Posts: PostSlice{o},
+ }
+ } else {
+ rel.R.Posts = append(rel.R.Posts, o)
+ }
+ }
+ return nil
+}
+
+// SetTags removes all previously related items of the
+// post replacing them completely with the passed
+// in related items, optionally inserting them as new records.
+// Sets o.R.Posts's Tags accordingly.
+// Replaces o.R.Tags with related.
+// Sets related.R.Posts's Tags accordingly.
+func (o *Post) SetTags(ctx context.Context, exec boil.ContextExecutor, insert bool, related ...*Tag) error {
+ query := "delete from \"post_tags\" where \"post_id\" = $1"
+ values := []interface{}{o.ID}
+ if boil.IsDebug(ctx) {
+ writer := boil.DebugWriterFrom(ctx)
+ fmt.Fprintln(writer, query)
+ fmt.Fprintln(writer, values)
+ }
+ _, err := exec.ExecContext(ctx, query, values...)
+ if err != nil {
+ return errors.Wrap(err, "failed to remove relationships before set")
+ }
+
+ removeTagsFromPostsSlice(o, related)
+ if o.R != nil {
+ o.R.Tags = nil
+ }
+
+ return o.AddTags(ctx, exec, insert, related...)
+}
+
+// RemoveTags relationships from objects passed in.
+// Removes related items from R.Tags (uses pointer comparison, removal does not keep order)
+// Sets related.R.Posts.
+func (o *Post) RemoveTags(ctx context.Context, exec boil.ContextExecutor, related ...*Tag) error {
+ if len(related) == 0 {
+ return nil
+ }
+
+ var err error
+ query := fmt.Sprintf(
+ "delete from \"post_tags\" where \"post_id\" = $1 and \"tag_id\" in (%s)",
+ strmangle.Placeholders(dialect.UseIndexPlaceholders, len(related), 2, 1),
+ )
+ values := []interface{}{o.ID}
+ for _, rel := range related {
+ values = append(values, rel.ID)
+ }
+
+ if boil.IsDebug(ctx) {
+ writer := boil.DebugWriterFrom(ctx)
+ fmt.Fprintln(writer, query)
+ fmt.Fprintln(writer, values)
+ }
+ _, err = exec.ExecContext(ctx, query, values...)
+ if err != nil {
+ return errors.Wrap(err, "failed to remove relationships before set")
+ }
+ removeTagsFromPostsSlice(o, related)
+ if o.R == nil {
+ return nil
+ }
+
+ for _, rel := range related {
+ for i, ri := range o.R.Tags {
+ if rel != ri {
+ continue
+ }
+
+ ln := len(o.R.Tags)
+ if ln > 1 && i < ln-1 {
+ o.R.Tags[i] = o.R.Tags[ln-1]
+ }
+ o.R.Tags = o.R.Tags[:ln-1]
+ break
+ }
+ }
+
+ return nil
+}
+
+func removeTagsFromPostsSlice(o *Post, related []*Tag) {
+ for _, rel := range related {
+ if rel.R == nil {
+ continue
+ }
+ for i, ri := range rel.R.Posts {
+ if o.ID != ri.ID {
+ continue
+ }
+
+ ln := len(rel.R.Posts)
+ if ln > 1 && i < ln-1 {
+ rel.R.Posts[i] = rel.R.Posts[ln-1]
+ }
+ rel.R.Posts = rel.R.Posts[:ln-1]
+ break
+ }
+ }
+}
+
// Posts retrieves all the records using an executor.
func Posts(mods ...qm.QueryMod) postQuery {
mods = append(mods, qm.From("\"posts\""))
diff --git a/src/models/posts_test.go b/src/models/posts_test.go
index 0df4258..578d483 100644
--- a/src/models/posts_test.go
+++ b/src/models/posts_test.go
@@ -494,6 +494,437 @@ func testPostsInsertWhitelist(t *testing.T) {
}
}
+func testPostToManyTags(t *testing.T) {
+ var err error
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+
+ var a Post
+ var b, c Tag
+
+ seed := randomize.NewSeed()
+ if err = randomize.Struct(seed, &a, postDBTypes, true, postColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Post struct: %s", err)
+ }
+
+ if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ if err = randomize.Struct(seed, &b, tagDBTypes, false, tagColumnsWithDefault...); err != nil {
+ t.Fatal(err)
+ }
+ if err = randomize.Struct(seed, &c, tagDBTypes, false, tagColumnsWithDefault...); err != nil {
+ t.Fatal(err)
+ }
+
+ if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+ if err = c.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = tx.Exec("insert into \"post_tags\" (\"post_id\", \"tag_id\") values ($1, $2)", a.ID, b.ID)
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = tx.Exec("insert into \"post_tags\" (\"post_id\", \"tag_id\") values ($1, $2)", a.ID, c.ID)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ check, err := a.Tags().All(ctx, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ bFound, cFound := false, false
+ for _, v := range check {
+ if v.ID == b.ID {
+ bFound = true
+ }
+ if v.ID == c.ID {
+ cFound = true
+ }
+ }
+
+ if !bFound {
+ t.Error("expected to find b")
+ }
+ if !cFound {
+ t.Error("expected to find c")
+ }
+
+ slice := PostSlice{&a}
+ if err = a.L.LoadTags(ctx, tx, false, (*[]*Post)(&slice), nil); err != nil {
+ t.Fatal(err)
+ }
+ if got := len(a.R.Tags); got != 2 {
+ t.Error("number of eager loaded records wrong, got:", got)
+ }
+
+ a.R.Tags = nil
+ if err = a.L.LoadTags(ctx, tx, true, &a, nil); err != nil {
+ t.Fatal(err)
+ }
+ if got := len(a.R.Tags); got != 2 {
+ t.Error("number of eager loaded records wrong, got:", got)
+ }
+
+ if t.Failed() {
+ t.Logf("%#v", check)
+ }
+}
+
+func testPostToManyAddOpTags(t *testing.T) {
+ var err error
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+
+ var a Post
+ var b, c, d, e Tag
+
+ seed := randomize.NewSeed()
+ if err = randomize.Struct(seed, &a, postDBTypes, false, strmangle.SetComplement(postPrimaryKeyColumns, postColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ foreigners := []*Tag{&b, &c, &d, &e}
+ for _, x := range foreigners {
+ if err = randomize.Struct(seed, x, tagDBTypes, false, strmangle.SetComplement(tagPrimaryKeyColumns, tagColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+ if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+ if err = c.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ foreignersSplitByInsertion := [][]*Tag{
+ {&b, &c},
+ {&d, &e},
+ }
+
+ for i, x := range foreignersSplitByInsertion {
+ err = a.AddTags(ctx, tx, i != 0, x...)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ first := x[0]
+ second := x[1]
+
+ if first.R.Posts[0] != &a {
+ t.Error("relationship was not added properly to the slice")
+ }
+ if second.R.Posts[0] != &a {
+ t.Error("relationship was not added properly to the slice")
+ }
+
+ if a.R.Tags[i*2] != first {
+ t.Error("relationship struct slice not set to correct value")
+ }
+ if a.R.Tags[i*2+1] != second {
+ t.Error("relationship struct slice not set to correct value")
+ }
+
+ count, err := a.Tags().Count(ctx, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if want := int64((i + 1) * 2); count != want {
+ t.Error("want", want, "got", count)
+ }
+ }
+}
+
+func testPostToManySetOpTags(t *testing.T) {
+ var err error
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+
+ var a Post
+ var b, c, d, e Tag
+
+ seed := randomize.NewSeed()
+ if err = randomize.Struct(seed, &a, postDBTypes, false, strmangle.SetComplement(postPrimaryKeyColumns, postColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ foreigners := []*Tag{&b, &c, &d, &e}
+ for _, x := range foreigners {
+ if err = randomize.Struct(seed, x, tagDBTypes, false, strmangle.SetComplement(tagPrimaryKeyColumns, tagColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ if err = a.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+ if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+ if err = c.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ err = a.SetTags(ctx, tx, false, &b, &c)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ count, err := a.Tags().Count(ctx, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if count != 2 {
+ t.Error("count was wrong:", count)
+ }
+
+ err = a.SetTags(ctx, tx, true, &d, &e)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ count, err = a.Tags().Count(ctx, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if count != 2 {
+ t.Error("count was wrong:", count)
+ }
+
+ // The following checks cannot be implemented since we have no handle
+ // to these when we call Set(). Leaving them here as wishful thinking
+ // and to let people know there's dragons.
+ //
+ // if len(b.R.Posts) != 0 {
+ // t.Error("relationship was not removed properly from the slice")
+ // }
+ // if len(c.R.Posts) != 0 {
+ // t.Error("relationship was not removed properly from the slice")
+ // }
+ if d.R.Posts[0] != &a {
+ t.Error("relationship was not added properly to the slice")
+ }
+ if e.R.Posts[0] != &a {
+ t.Error("relationship was not added properly to the slice")
+ }
+
+ if a.R.Tags[0] != &d {
+ t.Error("relationship struct slice not set to correct value")
+ }
+ if a.R.Tags[1] != &e {
+ t.Error("relationship struct slice not set to correct value")
+ }
+}
+
+func testPostToManyRemoveOpTags(t *testing.T) {
+ var err error
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+
+ var a Post
+ var b, c, d, e Tag
+
+ seed := randomize.NewSeed()
+ if err = randomize.Struct(seed, &a, postDBTypes, false, strmangle.SetComplement(postPrimaryKeyColumns, postColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ foreigners := []*Tag{&b, &c, &d, &e}
+ for _, x := range foreigners {
+ if err = randomize.Struct(seed, x, tagDBTypes, false, strmangle.SetComplement(tagPrimaryKeyColumns, tagColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ err = a.AddTags(ctx, tx, true, foreigners...)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ count, err := a.Tags().Count(ctx, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if count != 4 {
+ t.Error("count was wrong:", count)
+ }
+
+ err = a.RemoveTags(ctx, tx, foreigners[:2]...)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ count, err = a.Tags().Count(ctx, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if count != 2 {
+ t.Error("count was wrong:", count)
+ }
+
+ if len(b.R.Posts) != 0 {
+ t.Error("relationship was not removed properly from the slice")
+ }
+ if len(c.R.Posts) != 0 {
+ t.Error("relationship was not removed properly from the slice")
+ }
+ if d.R.Posts[0] != &a {
+ t.Error("relationship was not added properly to the foreign struct")
+ }
+ if e.R.Posts[0] != &a {
+ t.Error("relationship was not added properly to the foreign struct")
+ }
+
+ if len(a.R.Tags) != 2 {
+ t.Error("should have preserved two relationships")
+ }
+
+ // Removal doesn't do a stable deletion for performance so we have to flip the order
+ if a.R.Tags[1] != &d {
+ t.Error("relationship to d should have been preserved")
+ }
+ if a.R.Tags[0] != &e {
+ t.Error("relationship to e should have been preserved")
+ }
+}
+
+func testPostToOneUserUsingUser(t *testing.T) {
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+
+ var local Post
+ var foreign User
+
+ seed := randomize.NewSeed()
+ if err := randomize.Struct(seed, &local, postDBTypes, false, postColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Post struct: %s", err)
+ }
+ if err := randomize.Struct(seed, &foreign, userDBTypes, false, userColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize User struct: %s", err)
+ }
+
+ if err := foreign.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ local.UserID = foreign.ID
+ if err := local.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ check, err := local.User().One(ctx, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if check.ID != foreign.ID {
+ t.Errorf("want: %v, got %v", foreign.ID, check.ID)
+ }
+
+ ranAfterSelectHook := false
+ AddUserHook(boil.AfterSelectHook, func(ctx context.Context, e boil.ContextExecutor, o *User) error {
+ ranAfterSelectHook = true
+ return nil
+ })
+
+ slice := PostSlice{&local}
+ if err = local.L.LoadUser(ctx, tx, false, (*[]*Post)(&slice), nil); err != nil {
+ t.Fatal(err)
+ }
+ if local.R.User == nil {
+ t.Error("struct should have been eager loaded")
+ }
+
+ local.R.User = nil
+ if err = local.L.LoadUser(ctx, tx, true, &local, nil); err != nil {
+ t.Fatal(err)
+ }
+ if local.R.User == nil {
+ t.Error("struct should have been eager loaded")
+ }
+
+ if !ranAfterSelectHook {
+ t.Error("failed to run AfterSelect hook for relationship")
+ }
+}
+
+func testPostToOneSetOpUserUsingUser(t *testing.T) {
+ var err error
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+
+ var a Post
+ var b, c User
+
+ seed := randomize.NewSeed()
+ if err = randomize.Struct(seed, &a, postDBTypes, false, strmangle.SetComplement(postPrimaryKeyColumns, postColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ if err = randomize.Struct(seed, &b, userDBTypes, false, strmangle.SetComplement(userPrimaryKeyColumns, userColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ if err = randomize.Struct(seed, &c, userDBTypes, false, strmangle.SetComplement(userPrimaryKeyColumns, userColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+ if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ for i, x := range []*User{&b, &c} {
+ err = a.SetUser(ctx, tx, i != 0, x)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if a.R.User != x {
+ t.Error("relationship struct not set to correct value")
+ }
+
+ if x.R.Posts[0] != &a {
+ t.Error("failed to append to foreign relationship struct")
+ }
+ if a.UserID != x.ID {
+ t.Error("foreign key was wrong value", a.UserID)
+ }
+
+ zero := reflect.Zero(reflect.TypeOf(a.UserID))
+ reflect.Indirect(reflect.ValueOf(&a.UserID)).Set(zero)
+
+ if err = a.Reload(ctx, tx); err != nil {
+ t.Fatal("failed to reload", err)
+ }
+
+ if a.UserID != x.ID {
+ t.Error("foreign key was wrong value", a.UserID, x.ID)
+ }
+ }
+}
+
func testPostsReload(t *testing.T) {
t.Parallel()
@@ -568,7 +999,7 @@ func testPostsSelect(t *testing.T) {
}
var (
- postDBTypes = map[string]string{`ID`: `integer`, `Title`: `character varying`, `URL`: `character varying`, `Description`: `text`, `CreatedAt`: `timestamp with time zone`, `UpdatedAt`: `timestamp with time zone`}
+ postDBTypes = map[string]string{`ID`: `integer`, `Title`: `character varying`, `Description`: `text`, `URL`: `character varying`, `UserID`: `integer`, `CreatedAt`: `timestamp with time zone`, `UpdatedAt`: `timestamp with time zone`}
_ = bytes.MinRead
)
diff --git a/src/models/psql_suites_test.go b/src/models/psql_suites_test.go
index c552b41..b49f957 100644
--- a/src/models/psql_suites_test.go
+++ b/src/models/psql_suites_test.go
@@ -8,5 +8,7 @@ import "testing"
func TestUpsert(t *testing.T) {
t.Run("Posts", testPostsUpsert)
+ t.Run("Tags", testTagsUpsert)
+
t.Run("Users", testUsersUpsert)
}
diff --git a/src/models/tags.go b/src/models/tags.go
new file mode 100644
index 0000000..707aa59
--- /dev/null
+++ b/src/models/tags.go
@@ -0,0 +1,1469 @@
+// Code generated by SQLBoiler 4.16.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT.
+// This file is meant to be re-generated in place and/or deleted at any time.
+
+package models
+
+import (
+ "context"
+ "database/sql"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/friendsofgo/errors"
+ "github.com/volatiletech/sqlboiler/v4/boil"
+ "github.com/volatiletech/sqlboiler/v4/queries"
+ "github.com/volatiletech/sqlboiler/v4/queries/qm"
+ "github.com/volatiletech/sqlboiler/v4/queries/qmhelper"
+ "github.com/volatiletech/strmangle"
+)
+
+// Tag is an object representing the database table.
+type Tag struct {
+ ID int `boil:"id" json:"id" toml:"id" yaml:"id"`
+ Tag string `boil:"tag" json:"tag" toml:"tag" yaml:"tag"`
+ Description string `boil:"description" json:"description" toml:"description" yaml:"description"`
+ UserID int `boil:"user_id" json:"user_id" toml:"user_id" yaml:"user_id"`
+ CreatedAt time.Time `boil:"created_at" json:"created_at" toml:"created_at" yaml:"created_at"`
+ UpdatedAt time.Time `boil:"updated_at" json:"updated_at" toml:"updated_at" yaml:"updated_at"`
+
+ R *tagR `boil:"-" json:"-" toml:"-" yaml:"-"`
+ L tagL `boil:"-" json:"-" toml:"-" yaml:"-"`
+}
+
+var TagColumns = struct {
+ ID string
+ Tag string
+ Description string
+ UserID string
+ CreatedAt string
+ UpdatedAt string
+}{
+ ID: "id",
+ Tag: "tag",
+ Description: "description",
+ UserID: "user_id",
+ CreatedAt: "created_at",
+ UpdatedAt: "updated_at",
+}
+
+var TagTableColumns = struct {
+ ID string
+ Tag string
+ Description string
+ UserID string
+ CreatedAt string
+ UpdatedAt string
+}{
+ ID: "tags.id",
+ Tag: "tags.tag",
+ Description: "tags.description",
+ UserID: "tags.user_id",
+ CreatedAt: "tags.created_at",
+ UpdatedAt: "tags.updated_at",
+}
+
+// Generated where
+
+var TagWhere = struct {
+ ID whereHelperint
+ Tag whereHelperstring
+ Description whereHelperstring
+ UserID whereHelperint
+ CreatedAt whereHelpertime_Time
+ UpdatedAt whereHelpertime_Time
+}{
+ ID: whereHelperint{field: "\"tags\".\"id\""},
+ Tag: whereHelperstring{field: "\"tags\".\"tag\""},
+ Description: whereHelperstring{field: "\"tags\".\"description\""},
+ UserID: whereHelperint{field: "\"tags\".\"user_id\""},
+ CreatedAt: whereHelpertime_Time{field: "\"tags\".\"created_at\""},
+ UpdatedAt: whereHelpertime_Time{field: "\"tags\".\"updated_at\""},
+}
+
+// TagRels is where relationship names are stored.
+var TagRels = struct {
+ User string
+ Posts string
+}{
+ User: "User",
+ Posts: "Posts",
+}
+
+// tagR is where relationships are stored.
+type tagR struct {
+ User *User `boil:"User" json:"User" toml:"User" yaml:"User"`
+ Posts PostSlice `boil:"Posts" json:"Posts" toml:"Posts" yaml:"Posts"`
+}
+
+// NewStruct creates a new relationship struct
+func (*tagR) NewStruct() *tagR {
+ return &tagR{}
+}
+
+func (r *tagR) GetUser() *User {
+ if r == nil {
+ return nil
+ }
+ return r.User
+}
+
+func (r *tagR) GetPosts() PostSlice {
+ if r == nil {
+ return nil
+ }
+ return r.Posts
+}
+
+// tagL is where Load methods for each relationship are stored.
+type tagL struct{}
+
+var (
+ tagAllColumns = []string{"id", "tag", "description", "user_id", "created_at", "updated_at"}
+ tagColumnsWithoutDefault = []string{"tag", "description", "user_id"}
+ tagColumnsWithDefault = []string{"id", "created_at", "updated_at"}
+ tagPrimaryKeyColumns = []string{"id"}
+ tagGeneratedColumns = []string{}
+)
+
+type (
+ // TagSlice is an alias for a slice of pointers to Tag.
+ // This should almost always be used instead of []Tag.
+ TagSlice []*Tag
+ // TagHook is the signature for custom Tag hook methods
+ TagHook func(context.Context, boil.ContextExecutor, *Tag) error
+
+ tagQuery struct {
+ *queries.Query
+ }
+)
+
+// Cache for insert, update and upsert
+var (
+ tagType = reflect.TypeOf(&Tag{})
+ tagMapping = queries.MakeStructMapping(tagType)
+ tagPrimaryKeyMapping, _ = queries.BindMapping(tagType, tagMapping, tagPrimaryKeyColumns)
+ tagInsertCacheMut sync.RWMutex
+ tagInsertCache = make(map[string]insertCache)
+ tagUpdateCacheMut sync.RWMutex
+ tagUpdateCache = make(map[string]updateCache)
+ tagUpsertCacheMut sync.RWMutex
+ tagUpsertCache = make(map[string]insertCache)
+)
+
+var (
+ // Force time package dependency for automated UpdatedAt/CreatedAt.
+ _ = time.Second
+ // Force qmhelper dependency for where clause generation (which doesn't
+ // always happen)
+ _ = qmhelper.Where
+)
+
+var tagAfterSelectMu sync.Mutex
+var tagAfterSelectHooks []TagHook
+
+var tagBeforeInsertMu sync.Mutex
+var tagBeforeInsertHooks []TagHook
+var tagAfterInsertMu sync.Mutex
+var tagAfterInsertHooks []TagHook
+
+var tagBeforeUpdateMu sync.Mutex
+var tagBeforeUpdateHooks []TagHook
+var tagAfterUpdateMu sync.Mutex
+var tagAfterUpdateHooks []TagHook
+
+var tagBeforeDeleteMu sync.Mutex
+var tagBeforeDeleteHooks []TagHook
+var tagAfterDeleteMu sync.Mutex
+var tagAfterDeleteHooks []TagHook
+
+var tagBeforeUpsertMu sync.Mutex
+var tagBeforeUpsertHooks []TagHook
+var tagAfterUpsertMu sync.Mutex
+var tagAfterUpsertHooks []TagHook
+
+// doAfterSelectHooks executes all "after Select" hooks.
+func (o *Tag) doAfterSelectHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
+ if boil.HooksAreSkipped(ctx) {
+ return nil
+ }
+
+ for _, hook := range tagAfterSelectHooks {
+ if err := hook(ctx, exec, o); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// doBeforeInsertHooks executes all "before insert" hooks.
+func (o *Tag) doBeforeInsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
+ if boil.HooksAreSkipped(ctx) {
+ return nil
+ }
+
+ for _, hook := range tagBeforeInsertHooks {
+ if err := hook(ctx, exec, o); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// doAfterInsertHooks executes all "after Insert" hooks.
+func (o *Tag) doAfterInsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
+ if boil.HooksAreSkipped(ctx) {
+ return nil
+ }
+
+ for _, hook := range tagAfterInsertHooks {
+ if err := hook(ctx, exec, o); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// doBeforeUpdateHooks executes all "before Update" hooks.
+func (o *Tag) doBeforeUpdateHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
+ if boil.HooksAreSkipped(ctx) {
+ return nil
+ }
+
+ for _, hook := range tagBeforeUpdateHooks {
+ if err := hook(ctx, exec, o); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// doAfterUpdateHooks executes all "after Update" hooks.
+func (o *Tag) doAfterUpdateHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
+ if boil.HooksAreSkipped(ctx) {
+ return nil
+ }
+
+ for _, hook := range tagAfterUpdateHooks {
+ if err := hook(ctx, exec, o); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// doBeforeDeleteHooks executes all "before Delete" hooks.
+func (o *Tag) doBeforeDeleteHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
+ if boil.HooksAreSkipped(ctx) {
+ return nil
+ }
+
+ for _, hook := range tagBeforeDeleteHooks {
+ if err := hook(ctx, exec, o); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// doAfterDeleteHooks executes all "after Delete" hooks.
+func (o *Tag) doAfterDeleteHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
+ if boil.HooksAreSkipped(ctx) {
+ return nil
+ }
+
+ for _, hook := range tagAfterDeleteHooks {
+ if err := hook(ctx, exec, o); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// doBeforeUpsertHooks executes all "before Upsert" hooks.
+func (o *Tag) doBeforeUpsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
+ if boil.HooksAreSkipped(ctx) {
+ return nil
+ }
+
+ for _, hook := range tagBeforeUpsertHooks {
+ if err := hook(ctx, exec, o); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// doAfterUpsertHooks executes all "after Upsert" hooks.
+func (o *Tag) doAfterUpsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
+ if boil.HooksAreSkipped(ctx) {
+ return nil
+ }
+
+ for _, hook := range tagAfterUpsertHooks {
+ if err := hook(ctx, exec, o); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// AddTagHook registers your hook function for all future operations.
+func AddTagHook(hookPoint boil.HookPoint, tagHook TagHook) {
+ switch hookPoint {
+ case boil.AfterSelectHook:
+ tagAfterSelectMu.Lock()
+ tagAfterSelectHooks = append(tagAfterSelectHooks, tagHook)
+ tagAfterSelectMu.Unlock()
+ case boil.BeforeInsertHook:
+ tagBeforeInsertMu.Lock()
+ tagBeforeInsertHooks = append(tagBeforeInsertHooks, tagHook)
+ tagBeforeInsertMu.Unlock()
+ case boil.AfterInsertHook:
+ tagAfterInsertMu.Lock()
+ tagAfterInsertHooks = append(tagAfterInsertHooks, tagHook)
+ tagAfterInsertMu.Unlock()
+ case boil.BeforeUpdateHook:
+ tagBeforeUpdateMu.Lock()
+ tagBeforeUpdateHooks = append(tagBeforeUpdateHooks, tagHook)
+ tagBeforeUpdateMu.Unlock()
+ case boil.AfterUpdateHook:
+ tagAfterUpdateMu.Lock()
+ tagAfterUpdateHooks = append(tagAfterUpdateHooks, tagHook)
+ tagAfterUpdateMu.Unlock()
+ case boil.BeforeDeleteHook:
+ tagBeforeDeleteMu.Lock()
+ tagBeforeDeleteHooks = append(tagBeforeDeleteHooks, tagHook)
+ tagBeforeDeleteMu.Unlock()
+ case boil.AfterDeleteHook:
+ tagAfterDeleteMu.Lock()
+ tagAfterDeleteHooks = append(tagAfterDeleteHooks, tagHook)
+ tagAfterDeleteMu.Unlock()
+ case boil.BeforeUpsertHook:
+ tagBeforeUpsertMu.Lock()
+ tagBeforeUpsertHooks = append(tagBeforeUpsertHooks, tagHook)
+ tagBeforeUpsertMu.Unlock()
+ case boil.AfterUpsertHook:
+ tagAfterUpsertMu.Lock()
+ tagAfterUpsertHooks = append(tagAfterUpsertHooks, tagHook)
+ tagAfterUpsertMu.Unlock()
+ }
+}
+
+// One returns a single tag record from the query.
+func (q tagQuery) One(ctx context.Context, exec boil.ContextExecutor) (*Tag, error) {
+ o := &Tag{}
+
+ queries.SetLimit(q.Query, 1)
+
+ err := q.Bind(ctx, exec, o)
+ if err != nil {
+ if errors.Is(err, sql.ErrNoRows) {
+ return nil, sql.ErrNoRows
+ }
+ return nil, errors.Wrap(err, "models: failed to execute a one query for tags")
+ }
+
+ if err := o.doAfterSelectHooks(ctx, exec); err != nil {
+ return o, err
+ }
+
+ return o, nil
+}
+
+// All returns all Tag records from the query.
+func (q tagQuery) All(ctx context.Context, exec boil.ContextExecutor) (TagSlice, error) {
+ var o []*Tag
+
+ err := q.Bind(ctx, exec, &o)
+ if err != nil {
+ return nil, errors.Wrap(err, "models: failed to assign all query results to Tag slice")
+ }
+
+ if len(tagAfterSelectHooks) != 0 {
+ for _, obj := range o {
+ if err := obj.doAfterSelectHooks(ctx, exec); err != nil {
+ return o, err
+ }
+ }
+ }
+
+ return o, nil
+}
+
+// Count returns the count of all Tag records in the query.
+func (q tagQuery) Count(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
+ var count int64
+
+ queries.SetSelect(q.Query, nil)
+ queries.SetCount(q.Query)
+
+ err := q.Query.QueryRowContext(ctx, exec).Scan(&count)
+ if err != nil {
+ return 0, errors.Wrap(err, "models: failed to count tags rows")
+ }
+
+ return count, nil
+}
+
+// Exists checks if the row exists in the table.
+func (q tagQuery) Exists(ctx context.Context, exec boil.ContextExecutor) (bool, error) {
+ var count int64
+
+ queries.SetSelect(q.Query, nil)
+ queries.SetCount(q.Query)
+ queries.SetLimit(q.Query, 1)
+
+ err := q.Query.QueryRowContext(ctx, exec).Scan(&count)
+ if err != nil {
+ return false, errors.Wrap(err, "models: failed to check if tags exists")
+ }
+
+ return count > 0, nil
+}
+
+// User pointed to by the foreign key.
+func (o *Tag) User(mods ...qm.QueryMod) userQuery {
+ queryMods := []qm.QueryMod{
+ qm.Where("\"id\" = ?", o.UserID),
+ }
+
+ queryMods = append(queryMods, mods...)
+
+ return Users(queryMods...)
+}
+
+// Posts retrieves all the post's Posts with an executor.
+func (o *Tag) Posts(mods ...qm.QueryMod) postQuery {
+ var queryMods []qm.QueryMod
+ if len(mods) != 0 {
+ queryMods = append(queryMods, mods...)
+ }
+
+ queryMods = append(queryMods,
+ qm.InnerJoin("\"post_tags\" on \"posts\".\"id\" = \"post_tags\".\"post_id\""),
+ qm.Where("\"post_tags\".\"tag_id\"=?", o.ID),
+ )
+
+ return Posts(queryMods...)
+}
+
+// LoadUser allows an eager lookup of values, cached into the
+// loaded structs of the objects. This is for an N-1 relationship.
+func (tagL) LoadUser(ctx context.Context, e boil.ContextExecutor, singular bool, maybeTag interface{}, mods queries.Applicator) error {
+ var slice []*Tag
+ var object *Tag
+
+ if singular {
+ var ok bool
+ object, ok = maybeTag.(*Tag)
+ if !ok {
+ object = new(Tag)
+ ok = queries.SetFromEmbeddedStruct(&object, &maybeTag)
+ if !ok {
+ return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", object, maybeTag))
+ }
+ }
+ } else {
+ s, ok := maybeTag.(*[]*Tag)
+ if ok {
+ slice = *s
+ } else {
+ ok = queries.SetFromEmbeddedStruct(&slice, maybeTag)
+ if !ok {
+ return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", slice, maybeTag))
+ }
+ }
+ }
+
+ args := make(map[interface{}]struct{})
+ if singular {
+ if object.R == nil {
+ object.R = &tagR{}
+ }
+ args[object.UserID] = struct{}{}
+
+ } else {
+ for _, obj := range slice {
+ if obj.R == nil {
+ obj.R = &tagR{}
+ }
+
+ args[obj.UserID] = struct{}{}
+
+ }
+ }
+
+ if len(args) == 0 {
+ return nil
+ }
+
+ argsSlice := make([]interface{}, len(args))
+ i := 0
+ for arg := range args {
+ argsSlice[i] = arg
+ i++
+ }
+
+ query := NewQuery(
+ qm.From(`users`),
+ qm.WhereIn(`users.id in ?`, argsSlice...),
+ )
+ if mods != nil {
+ mods.Apply(query)
+ }
+
+ results, err := query.QueryContext(ctx, e)
+ if err != nil {
+ return errors.Wrap(err, "failed to eager load User")
+ }
+
+ var resultSlice []*User
+ if err = queries.Bind(results, &resultSlice); err != nil {
+ return errors.Wrap(err, "failed to bind eager loaded slice User")
+ }
+
+ if err = results.Close(); err != nil {
+ return errors.Wrap(err, "failed to close results of eager load for users")
+ }
+ if err = results.Err(); err != nil {
+ return errors.Wrap(err, "error occurred during iteration of eager loaded relations for users")
+ }
+
+ if len(userAfterSelectHooks) != 0 {
+ for _, obj := range resultSlice {
+ if err := obj.doAfterSelectHooks(ctx, e); err != nil {
+ return err
+ }
+ }
+ }
+
+ if len(resultSlice) == 0 {
+ return nil
+ }
+
+ if singular {
+ foreign := resultSlice[0]
+ object.R.User = foreign
+ if foreign.R == nil {
+ foreign.R = &userR{}
+ }
+ foreign.R.Tags = append(foreign.R.Tags, object)
+ return nil
+ }
+
+ for _, local := range slice {
+ for _, foreign := range resultSlice {
+ if local.UserID == foreign.ID {
+ local.R.User = foreign
+ if foreign.R == nil {
+ foreign.R = &userR{}
+ }
+ foreign.R.Tags = append(foreign.R.Tags, local)
+ break
+ }
+ }
+ }
+
+ return nil
+}
+
+// LoadPosts allows an eager lookup of values, cached into the
+// loaded structs of the objects. This is for a 1-M or N-M relationship.
+func (tagL) LoadPosts(ctx context.Context, e boil.ContextExecutor, singular bool, maybeTag interface{}, mods queries.Applicator) error {
+ var slice []*Tag
+ var object *Tag
+
+ if singular {
+ var ok bool
+ object, ok = maybeTag.(*Tag)
+ if !ok {
+ object = new(Tag)
+ ok = queries.SetFromEmbeddedStruct(&object, &maybeTag)
+ if !ok {
+ return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", object, maybeTag))
+ }
+ }
+ } else {
+ s, ok := maybeTag.(*[]*Tag)
+ if ok {
+ slice = *s
+ } else {
+ ok = queries.SetFromEmbeddedStruct(&slice, maybeTag)
+ if !ok {
+ return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", slice, maybeTag))
+ }
+ }
+ }
+
+ args := make(map[interface{}]struct{})
+ if singular {
+ if object.R == nil {
+ object.R = &tagR{}
+ }
+ args[object.ID] = struct{}{}
+ } else {
+ for _, obj := range slice {
+ if obj.R == nil {
+ obj.R = &tagR{}
+ }
+ args[obj.ID] = struct{}{}
+ }
+ }
+
+ if len(args) == 0 {
+ return nil
+ }
+
+ argsSlice := make([]interface{}, len(args))
+ i := 0
+ for arg := range args {
+ argsSlice[i] = arg
+ i++
+ }
+
+ query := NewQuery(
+ qm.Select("\"posts\".\"id\", \"posts\".\"title\", \"posts\".\"description\", \"posts\".\"url\", \"posts\".\"user_id\", \"posts\".\"created_at\", \"posts\".\"updated_at\", \"a\".\"tag_id\""),
+ qm.From("\"posts\""),
+ qm.InnerJoin("\"post_tags\" as \"a\" on \"posts\".\"id\" = \"a\".\"post_id\""),
+ qm.WhereIn("\"a\".\"tag_id\" in ?", argsSlice...),
+ )
+ if mods != nil {
+ mods.Apply(query)
+ }
+
+ results, err := query.QueryContext(ctx, e)
+ if err != nil {
+ return errors.Wrap(err, "failed to eager load posts")
+ }
+
+ var resultSlice []*Post
+
+ var localJoinCols []int
+ for results.Next() {
+ one := new(Post)
+ var localJoinCol int
+
+ err = results.Scan(&one.ID, &one.Title, &one.Description, &one.URL, &one.UserID, &one.CreatedAt, &one.UpdatedAt, &localJoinCol)
+ if err != nil {
+ return errors.Wrap(err, "failed to scan eager loaded results for posts")
+ }
+ if err = results.Err(); err != nil {
+ return errors.Wrap(err, "failed to plebian-bind eager loaded slice posts")
+ }
+
+ resultSlice = append(resultSlice, one)
+ localJoinCols = append(localJoinCols, localJoinCol)
+ }
+
+ if err = results.Close(); err != nil {
+ return errors.Wrap(err, "failed to close results in eager load on posts")
+ }
+ if err = results.Err(); err != nil {
+ return errors.Wrap(err, "error occurred during iteration of eager loaded relations for posts")
+ }
+
+ if len(postAfterSelectHooks) != 0 {
+ for _, obj := range resultSlice {
+ if err := obj.doAfterSelectHooks(ctx, e); err != nil {
+ return err
+ }
+ }
+ }
+ if singular {
+ object.R.Posts = resultSlice
+ for _, foreign := range resultSlice {
+ if foreign.R == nil {
+ foreign.R = &postR{}
+ }
+ foreign.R.Tags = append(foreign.R.Tags, object)
+ }
+ return nil
+ }
+
+ for i, foreign := range resultSlice {
+ localJoinCol := localJoinCols[i]
+ for _, local := range slice {
+ if local.ID == localJoinCol {
+ local.R.Posts = append(local.R.Posts, foreign)
+ if foreign.R == nil {
+ foreign.R = &postR{}
+ }
+ foreign.R.Tags = append(foreign.R.Tags, local)
+ break
+ }
+ }
+ }
+
+ return nil
+}
+
+// SetUser of the tag to the related item.
+// Sets o.R.User to related.
+// Adds o to related.R.Tags.
+func (o *Tag) SetUser(ctx context.Context, exec boil.ContextExecutor, insert bool, related *User) error {
+ var err error
+ if insert {
+ if err = related.Insert(ctx, exec, boil.Infer()); err != nil {
+ return errors.Wrap(err, "failed to insert into foreign table")
+ }
+ }
+
+ updateQuery := fmt.Sprintf(
+ "UPDATE \"tags\" SET %s WHERE %s",
+ strmangle.SetParamNames("\"", "\"", 1, []string{"user_id"}),
+ strmangle.WhereClause("\"", "\"", 2, tagPrimaryKeyColumns),
+ )
+ values := []interface{}{related.ID, o.ID}
+
+ if boil.IsDebug(ctx) {
+ writer := boil.DebugWriterFrom(ctx)
+ fmt.Fprintln(writer, updateQuery)
+ fmt.Fprintln(writer, values)
+ }
+ if _, err = exec.ExecContext(ctx, updateQuery, values...); err != nil {
+ return errors.Wrap(err, "failed to update local table")
+ }
+
+ o.UserID = related.ID
+ if o.R == nil {
+ o.R = &tagR{
+ User: related,
+ }
+ } else {
+ o.R.User = related
+ }
+
+ if related.R == nil {
+ related.R = &userR{
+ Tags: TagSlice{o},
+ }
+ } else {
+ related.R.Tags = append(related.R.Tags, o)
+ }
+
+ return nil
+}
+
+// AddPosts adds the given related objects to the existing relationships
+// of the tag, optionally inserting them as new records.
+// Appends related to o.R.Posts.
+// Sets related.R.Tags appropriately.
+func (o *Tag) AddPosts(ctx context.Context, exec boil.ContextExecutor, insert bool, related ...*Post) error {
+ var err error
+ for _, rel := range related {
+ if insert {
+ if err = rel.Insert(ctx, exec, boil.Infer()); err != nil {
+ return errors.Wrap(err, "failed to insert into foreign table")
+ }
+ }
+ }
+
+ for _, rel := range related {
+ query := "insert into \"post_tags\" (\"tag_id\", \"post_id\") values ($1, $2)"
+ values := []interface{}{o.ID, rel.ID}
+
+ if boil.IsDebug(ctx) {
+ writer := boil.DebugWriterFrom(ctx)
+ fmt.Fprintln(writer, query)
+ fmt.Fprintln(writer, values)
+ }
+ _, err = exec.ExecContext(ctx, query, values...)
+ if err != nil {
+ return errors.Wrap(err, "failed to insert into join table")
+ }
+ }
+ if o.R == nil {
+ o.R = &tagR{
+ Posts: related,
+ }
+ } else {
+ o.R.Posts = append(o.R.Posts, related...)
+ }
+
+ for _, rel := range related {
+ if rel.R == nil {
+ rel.R = &postR{
+ Tags: TagSlice{o},
+ }
+ } else {
+ rel.R.Tags = append(rel.R.Tags, o)
+ }
+ }
+ return nil
+}
+
+// SetPosts removes all previously related items of the
+// tag replacing them completely with the passed
+// in related items, optionally inserting them as new records.
+// Sets o.R.Tags's Posts accordingly.
+// Replaces o.R.Posts with related.
+// Sets related.R.Tags's Posts accordingly.
+func (o *Tag) SetPosts(ctx context.Context, exec boil.ContextExecutor, insert bool, related ...*Post) error {
+ query := "delete from \"post_tags\" where \"tag_id\" = $1"
+ values := []interface{}{o.ID}
+ if boil.IsDebug(ctx) {
+ writer := boil.DebugWriterFrom(ctx)
+ fmt.Fprintln(writer, query)
+ fmt.Fprintln(writer, values)
+ }
+ _, err := exec.ExecContext(ctx, query, values...)
+ if err != nil {
+ return errors.Wrap(err, "failed to remove relationships before set")
+ }
+
+ removePostsFromTagsSlice(o, related)
+ if o.R != nil {
+ o.R.Posts = nil
+ }
+
+ return o.AddPosts(ctx, exec, insert, related...)
+}
+
+// RemovePosts relationships from objects passed in.
+// Removes related items from R.Posts (uses pointer comparison, removal does not keep order)
+// Sets related.R.Tags.
+func (o *Tag) RemovePosts(ctx context.Context, exec boil.ContextExecutor, related ...*Post) error {
+ if len(related) == 0 {
+ return nil
+ }
+
+ var err error
+ query := fmt.Sprintf(
+ "delete from \"post_tags\" where \"tag_id\" = $1 and \"post_id\" in (%s)",
+ strmangle.Placeholders(dialect.UseIndexPlaceholders, len(related), 2, 1),
+ )
+ values := []interface{}{o.ID}
+ for _, rel := range related {
+ values = append(values, rel.ID)
+ }
+
+ if boil.IsDebug(ctx) {
+ writer := boil.DebugWriterFrom(ctx)
+ fmt.Fprintln(writer, query)
+ fmt.Fprintln(writer, values)
+ }
+ _, err = exec.ExecContext(ctx, query, values...)
+ if err != nil {
+ return errors.Wrap(err, "failed to remove relationships before set")
+ }
+ removePostsFromTagsSlice(o, related)
+ if o.R == nil {
+ return nil
+ }
+
+ for _, rel := range related {
+ for i, ri := range o.R.Posts {
+ if rel != ri {
+ continue
+ }
+
+ ln := len(o.R.Posts)
+ if ln > 1 && i < ln-1 {
+ o.R.Posts[i] = o.R.Posts[ln-1]
+ }
+ o.R.Posts = o.R.Posts[:ln-1]
+ break
+ }
+ }
+
+ return nil
+}
+
+func removePostsFromTagsSlice(o *Tag, related []*Post) {
+ for _, rel := range related {
+ if rel.R == nil {
+ continue
+ }
+ for i, ri := range rel.R.Tags {
+ if o.ID != ri.ID {
+ continue
+ }
+
+ ln := len(rel.R.Tags)
+ if ln > 1 && i < ln-1 {
+ rel.R.Tags[i] = rel.R.Tags[ln-1]
+ }
+ rel.R.Tags = rel.R.Tags[:ln-1]
+ break
+ }
+ }
+}
+
+// Tags retrieves all the records using an executor.
+func Tags(mods ...qm.QueryMod) tagQuery {
+ mods = append(mods, qm.From("\"tags\""))
+ q := NewQuery(mods...)
+ if len(queries.GetSelect(q)) == 0 {
+ queries.SetSelect(q, []string{"\"tags\".*"})
+ }
+
+ return tagQuery{q}
+}
+
+// FindTag retrieves a single record by ID with an executor.
+// If selectCols is empty Find will return all columns.
+func FindTag(ctx context.Context, exec boil.ContextExecutor, iD int, selectCols ...string) (*Tag, error) {
+ tagObj := &Tag{}
+
+ sel := "*"
+ if len(selectCols) > 0 {
+ sel = strings.Join(strmangle.IdentQuoteSlice(dialect.LQ, dialect.RQ, selectCols), ",")
+ }
+ query := fmt.Sprintf(
+ "select %s from \"tags\" where \"id\"=$1", sel,
+ )
+
+ q := queries.Raw(query, iD)
+
+ err := q.Bind(ctx, exec, tagObj)
+ if err != nil {
+ if errors.Is(err, sql.ErrNoRows) {
+ return nil, sql.ErrNoRows
+ }
+ return nil, errors.Wrap(err, "models: unable to select from tags")
+ }
+
+ if err = tagObj.doAfterSelectHooks(ctx, exec); err != nil {
+ return tagObj, err
+ }
+
+ return tagObj, nil
+}
+
+// Insert a single record using an executor.
+// See boil.Columns.InsertColumnSet documentation to understand column list inference for inserts.
+func (o *Tag) Insert(ctx context.Context, exec boil.ContextExecutor, columns boil.Columns) error {
+ if o == nil {
+ return errors.New("models: no tags provided for insertion")
+ }
+
+ var err error
+ if !boil.TimestampsAreSkipped(ctx) {
+ currTime := time.Now().In(boil.GetLocation())
+
+ if o.CreatedAt.IsZero() {
+ o.CreatedAt = currTime
+ }
+ if o.UpdatedAt.IsZero() {
+ o.UpdatedAt = currTime
+ }
+ }
+
+ if err := o.doBeforeInsertHooks(ctx, exec); err != nil {
+ return err
+ }
+
+ nzDefaults := queries.NonZeroDefaultSet(tagColumnsWithDefault, o)
+
+ key := makeCacheKey(columns, nzDefaults)
+ tagInsertCacheMut.RLock()
+ cache, cached := tagInsertCache[key]
+ tagInsertCacheMut.RUnlock()
+
+ if !cached {
+ wl, returnColumns := columns.InsertColumnSet(
+ tagAllColumns,
+ tagColumnsWithDefault,
+ tagColumnsWithoutDefault,
+ nzDefaults,
+ )
+
+ cache.valueMapping, err = queries.BindMapping(tagType, tagMapping, wl)
+ if err != nil {
+ return err
+ }
+ cache.retMapping, err = queries.BindMapping(tagType, tagMapping, returnColumns)
+ if err != nil {
+ return err
+ }
+ if len(wl) != 0 {
+ cache.query = fmt.Sprintf("INSERT INTO \"tags\" (\"%s\") %%sVALUES (%s)%%s", strings.Join(wl, "\",\""), strmangle.Placeholders(dialect.UseIndexPlaceholders, len(wl), 1, 1))
+ } else {
+ cache.query = "INSERT INTO \"tags\" %sDEFAULT VALUES%s"
+ }
+
+ var queryOutput, queryReturning string
+
+ if len(cache.retMapping) != 0 {
+ queryReturning = fmt.Sprintf(" RETURNING \"%s\"", strings.Join(returnColumns, "\",\""))
+ }
+
+ cache.query = fmt.Sprintf(cache.query, queryOutput, queryReturning)
+ }
+
+ value := reflect.Indirect(reflect.ValueOf(o))
+ vals := queries.ValuesFromMapping(value, cache.valueMapping)
+
+ if boil.IsDebug(ctx) {
+ writer := boil.DebugWriterFrom(ctx)
+ fmt.Fprintln(writer, cache.query)
+ fmt.Fprintln(writer, vals)
+ }
+
+ if len(cache.retMapping) != 0 {
+ err = exec.QueryRowContext(ctx, cache.query, vals...).Scan(queries.PtrsFromMapping(value, cache.retMapping)...)
+ } else {
+ _, err = exec.ExecContext(ctx, cache.query, vals...)
+ }
+
+ if err != nil {
+ return errors.Wrap(err, "models: unable to insert into tags")
+ }
+
+ if !cached {
+ tagInsertCacheMut.Lock()
+ tagInsertCache[key] = cache
+ tagInsertCacheMut.Unlock()
+ }
+
+ return o.doAfterInsertHooks(ctx, exec)
+}
+
+// Update uses an executor to update the Tag.
+// See boil.Columns.UpdateColumnSet documentation to understand column list inference for updates.
+// Update does not automatically update the record in case of default values. Use .Reload() to refresh the records.
+func (o *Tag) Update(ctx context.Context, exec boil.ContextExecutor, columns boil.Columns) (int64, error) {
+ if !boil.TimestampsAreSkipped(ctx) {
+ currTime := time.Now().In(boil.GetLocation())
+
+ o.UpdatedAt = currTime
+ }
+
+ var err error
+ if err = o.doBeforeUpdateHooks(ctx, exec); err != nil {
+ return 0, err
+ }
+ key := makeCacheKey(columns, nil)
+ tagUpdateCacheMut.RLock()
+ cache, cached := tagUpdateCache[key]
+ tagUpdateCacheMut.RUnlock()
+
+ if !cached {
+ wl := columns.UpdateColumnSet(
+ tagAllColumns,
+ tagPrimaryKeyColumns,
+ )
+
+ if !columns.IsWhitelist() {
+ wl = strmangle.SetComplement(wl, []string{"created_at"})
+ }
+ if len(wl) == 0 {
+ return 0, errors.New("models: unable to update tags, could not build whitelist")
+ }
+
+ cache.query = fmt.Sprintf("UPDATE \"tags\" SET %s WHERE %s",
+ strmangle.SetParamNames("\"", "\"", 1, wl),
+ strmangle.WhereClause("\"", "\"", len(wl)+1, tagPrimaryKeyColumns),
+ )
+ cache.valueMapping, err = queries.BindMapping(tagType, tagMapping, append(wl, tagPrimaryKeyColumns...))
+ if err != nil {
+ return 0, err
+ }
+ }
+
+ values := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), cache.valueMapping)
+
+ if boil.IsDebug(ctx) {
+ writer := boil.DebugWriterFrom(ctx)
+ fmt.Fprintln(writer, cache.query)
+ fmt.Fprintln(writer, values)
+ }
+ var result sql.Result
+ result, err = exec.ExecContext(ctx, cache.query, values...)
+ if err != nil {
+ return 0, errors.Wrap(err, "models: unable to update tags row")
+ }
+
+ rowsAff, err := result.RowsAffected()
+ if err != nil {
+ return 0, errors.Wrap(err, "models: failed to get rows affected by update for tags")
+ }
+
+ if !cached {
+ tagUpdateCacheMut.Lock()
+ tagUpdateCache[key] = cache
+ tagUpdateCacheMut.Unlock()
+ }
+
+ return rowsAff, o.doAfterUpdateHooks(ctx, exec)
+}
+
+// UpdateAll updates all rows with the specified column values.
+func (q tagQuery) UpdateAll(ctx context.Context, exec boil.ContextExecutor, cols M) (int64, error) {
+ queries.SetUpdate(q.Query, cols)
+
+ result, err := q.Query.ExecContext(ctx, exec)
+ if err != nil {
+ return 0, errors.Wrap(err, "models: unable to update all for tags")
+ }
+
+ rowsAff, err := result.RowsAffected()
+ if err != nil {
+ return 0, errors.Wrap(err, "models: unable to retrieve rows affected for tags")
+ }
+
+ return rowsAff, nil
+}
+
+// UpdateAll updates all rows with the specified column values, using an executor.
+func (o TagSlice) UpdateAll(ctx context.Context, exec boil.ContextExecutor, cols M) (int64, error) {
+ ln := int64(len(o))
+ if ln == 0 {
+ return 0, nil
+ }
+
+ if len(cols) == 0 {
+ return 0, errors.New("models: update all requires at least one column argument")
+ }
+
+ colNames := make([]string, len(cols))
+ args := make([]interface{}, len(cols))
+
+ i := 0
+ for name, value := range cols {
+ colNames[i] = name
+ args[i] = value
+ i++
+ }
+
+ // Append all of the primary key values for each column
+ for _, obj := range o {
+ pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), tagPrimaryKeyMapping)
+ args = append(args, pkeyArgs...)
+ }
+
+ sql := fmt.Sprintf("UPDATE \"tags\" SET %s WHERE %s",
+ strmangle.SetParamNames("\"", "\"", 1, colNames),
+ strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), len(colNames)+1, tagPrimaryKeyColumns, len(o)))
+
+ if boil.IsDebug(ctx) {
+ writer := boil.DebugWriterFrom(ctx)
+ fmt.Fprintln(writer, sql)
+ fmt.Fprintln(writer, args...)
+ }
+ result, err := exec.ExecContext(ctx, sql, args...)
+ if err != nil {
+ return 0, errors.Wrap(err, "models: unable to update all in tag slice")
+ }
+
+ rowsAff, err := result.RowsAffected()
+ if err != nil {
+ return 0, errors.Wrap(err, "models: unable to retrieve rows affected all in update all tag")
+ }
+ return rowsAff, nil
+}
+
+// Upsert attempts an insert using an executor, and does an update or ignore on conflict.
+// See boil.Columns documentation for how to properly use updateColumns and insertColumns.
+func (o *Tag) Upsert(ctx context.Context, exec boil.ContextExecutor, updateOnConflict bool, conflictColumns []string, updateColumns, insertColumns boil.Columns, opts ...UpsertOptionFunc) error {
+ if o == nil {
+ return errors.New("models: no tags provided for upsert")
+ }
+ if !boil.TimestampsAreSkipped(ctx) {
+ currTime := time.Now().In(boil.GetLocation())
+
+ if o.CreatedAt.IsZero() {
+ o.CreatedAt = currTime
+ }
+ o.UpdatedAt = currTime
+ }
+
+ if err := o.doBeforeUpsertHooks(ctx, exec); err != nil {
+ return err
+ }
+
+ nzDefaults := queries.NonZeroDefaultSet(tagColumnsWithDefault, o)
+
+ // Build cache key in-line uglily - mysql vs psql problems
+ buf := strmangle.GetBuffer()
+ if updateOnConflict {
+ buf.WriteByte('t')
+ } else {
+ buf.WriteByte('f')
+ }
+ buf.WriteByte('.')
+ for _, c := range conflictColumns {
+ buf.WriteString(c)
+ }
+ buf.WriteByte('.')
+ buf.WriteString(strconv.Itoa(updateColumns.Kind))
+ for _, c := range updateColumns.Cols {
+ buf.WriteString(c)
+ }
+ buf.WriteByte('.')
+ buf.WriteString(strconv.Itoa(insertColumns.Kind))
+ for _, c := range insertColumns.Cols {
+ buf.WriteString(c)
+ }
+ buf.WriteByte('.')
+ for _, c := range nzDefaults {
+ buf.WriteString(c)
+ }
+ key := buf.String()
+ strmangle.PutBuffer(buf)
+
+ tagUpsertCacheMut.RLock()
+ cache, cached := tagUpsertCache[key]
+ tagUpsertCacheMut.RUnlock()
+
+ var err error
+
+ if !cached {
+ insert, _ := insertColumns.InsertColumnSet(
+ tagAllColumns,
+ tagColumnsWithDefault,
+ tagColumnsWithoutDefault,
+ nzDefaults,
+ )
+
+ update := updateColumns.UpdateColumnSet(
+ tagAllColumns,
+ tagPrimaryKeyColumns,
+ )
+
+ if updateOnConflict && len(update) == 0 {
+ return errors.New("models: unable to upsert tags, could not build update column list")
+ }
+
+ ret := strmangle.SetComplement(tagAllColumns, strmangle.SetIntersect(insert, update))
+
+ conflict := conflictColumns
+ if len(conflict) == 0 && updateOnConflict && len(update) != 0 {
+ if len(tagPrimaryKeyColumns) == 0 {
+ return errors.New("models: unable to upsert tags, could not build conflict column list")
+ }
+
+ conflict = make([]string, len(tagPrimaryKeyColumns))
+ copy(conflict, tagPrimaryKeyColumns)
+ }
+ cache.query = buildUpsertQueryPostgres(dialect, "\"tags\"", updateOnConflict, ret, update, conflict, insert, opts...)
+
+ cache.valueMapping, err = queries.BindMapping(tagType, tagMapping, insert)
+ if err != nil {
+ return err
+ }
+ if len(ret) != 0 {
+ cache.retMapping, err = queries.BindMapping(tagType, tagMapping, ret)
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ value := reflect.Indirect(reflect.ValueOf(o))
+ vals := queries.ValuesFromMapping(value, cache.valueMapping)
+ var returns []interface{}
+ if len(cache.retMapping) != 0 {
+ returns = queries.PtrsFromMapping(value, cache.retMapping)
+ }
+
+ if boil.IsDebug(ctx) {
+ writer := boil.DebugWriterFrom(ctx)
+ fmt.Fprintln(writer, cache.query)
+ fmt.Fprintln(writer, vals)
+ }
+ if len(cache.retMapping) != 0 {
+ err = exec.QueryRowContext(ctx, cache.query, vals...).Scan(returns...)
+ if errors.Is(err, sql.ErrNoRows) {
+ err = nil // Postgres doesn't return anything when there's no update
+ }
+ } else {
+ _, err = exec.ExecContext(ctx, cache.query, vals...)
+ }
+ if err != nil {
+ return errors.Wrap(err, "models: unable to upsert tags")
+ }
+
+ if !cached {
+ tagUpsertCacheMut.Lock()
+ tagUpsertCache[key] = cache
+ tagUpsertCacheMut.Unlock()
+ }
+
+ return o.doAfterUpsertHooks(ctx, exec)
+}
+
+// Delete deletes a single Tag record with an executor.
+// Delete will match against the primary key column to find the record to delete.
+func (o *Tag) Delete(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
+ if o == nil {
+ return 0, errors.New("models: no Tag provided for delete")
+ }
+
+ if err := o.doBeforeDeleteHooks(ctx, exec); err != nil {
+ return 0, err
+ }
+
+ args := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), tagPrimaryKeyMapping)
+ sql := "DELETE FROM \"tags\" WHERE \"id\"=$1"
+
+ if boil.IsDebug(ctx) {
+ writer := boil.DebugWriterFrom(ctx)
+ fmt.Fprintln(writer, sql)
+ fmt.Fprintln(writer, args...)
+ }
+ result, err := exec.ExecContext(ctx, sql, args...)
+ if err != nil {
+ return 0, errors.Wrap(err, "models: unable to delete from tags")
+ }
+
+ rowsAff, err := result.RowsAffected()
+ if err != nil {
+ return 0, errors.Wrap(err, "models: failed to get rows affected by delete for tags")
+ }
+
+ if err := o.doAfterDeleteHooks(ctx, exec); err != nil {
+ return 0, err
+ }
+
+ return rowsAff, nil
+}
+
+// DeleteAll deletes all matching rows.
+func (q tagQuery) DeleteAll(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
+ if q.Query == nil {
+ return 0, errors.New("models: no tagQuery provided for delete all")
+ }
+
+ queries.SetDelete(q.Query)
+
+ result, err := q.Query.ExecContext(ctx, exec)
+ if err != nil {
+ return 0, errors.Wrap(err, "models: unable to delete all from tags")
+ }
+
+ rowsAff, err := result.RowsAffected()
+ if err != nil {
+ return 0, errors.Wrap(err, "models: failed to get rows affected by deleteall for tags")
+ }
+
+ return rowsAff, nil
+}
+
+// DeleteAll deletes all rows in the slice, using an executor.
+func (o TagSlice) DeleteAll(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
+ if len(o) == 0 {
+ return 0, nil
+ }
+
+ if len(tagBeforeDeleteHooks) != 0 {
+ for _, obj := range o {
+ if err := obj.doBeforeDeleteHooks(ctx, exec); err != nil {
+ return 0, err
+ }
+ }
+ }
+
+ var args []interface{}
+ for _, obj := range o {
+ pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), tagPrimaryKeyMapping)
+ args = append(args, pkeyArgs...)
+ }
+
+ sql := "DELETE FROM \"tags\" WHERE " +
+ strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 1, tagPrimaryKeyColumns, len(o))
+
+ if boil.IsDebug(ctx) {
+ writer := boil.DebugWriterFrom(ctx)
+ fmt.Fprintln(writer, sql)
+ fmt.Fprintln(writer, args)
+ }
+ result, err := exec.ExecContext(ctx, sql, args...)
+ if err != nil {
+ return 0, errors.Wrap(err, "models: unable to delete all from tag slice")
+ }
+
+ rowsAff, err := result.RowsAffected()
+ if err != nil {
+ return 0, errors.Wrap(err, "models: failed to get rows affected by deleteall for tags")
+ }
+
+ if len(tagAfterDeleteHooks) != 0 {
+ for _, obj := range o {
+ if err := obj.doAfterDeleteHooks(ctx, exec); err != nil {
+ return 0, err
+ }
+ }
+ }
+
+ return rowsAff, nil
+}
+
+// Reload refetches the object from the database
+// using the primary keys with an executor.
+func (o *Tag) Reload(ctx context.Context, exec boil.ContextExecutor) error {
+ ret, err := FindTag(ctx, exec, o.ID)
+ if err != nil {
+ return err
+ }
+
+ *o = *ret
+ return nil
+}
+
+// ReloadAll refetches every row with matching primary key column values
+// and overwrites the original object slice with the newly updated slice.
+func (o *TagSlice) ReloadAll(ctx context.Context, exec boil.ContextExecutor) error {
+ if o == nil || len(*o) == 0 {
+ return nil
+ }
+
+ slice := TagSlice{}
+ var args []interface{}
+ for _, obj := range *o {
+ pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), tagPrimaryKeyMapping)
+ args = append(args, pkeyArgs...)
+ }
+
+ sql := "SELECT \"tags\".* FROM \"tags\" WHERE " +
+ strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 1, tagPrimaryKeyColumns, len(*o))
+
+ q := queries.Raw(sql, args...)
+
+ err := q.Bind(ctx, exec, &slice)
+ if err != nil {
+ return errors.Wrap(err, "models: unable to reload all in TagSlice")
+ }
+
+ *o = slice
+
+ return nil
+}
+
+// TagExists checks if the Tag row exists.
+func TagExists(ctx context.Context, exec boil.ContextExecutor, iD int) (bool, error) {
+ var exists bool
+ sql := "select exists(select 1 from \"tags\" where \"id\"=$1 limit 1)"
+
+ if boil.IsDebug(ctx) {
+ writer := boil.DebugWriterFrom(ctx)
+ fmt.Fprintln(writer, sql)
+ fmt.Fprintln(writer, iD)
+ }
+ row := exec.QueryRowContext(ctx, sql, iD)
+
+ err := row.Scan(&exists)
+ if err != nil {
+ return false, errors.Wrap(err, "models: unable to check if tags exists")
+ }
+
+ return exists, nil
+}
+
+// Exists checks if the Tag row exists.
+func (o *Tag) Exists(ctx context.Context, exec boil.ContextExecutor) (bool, error) {
+ return TagExists(ctx, exec, o.ID)
+}
diff --git a/src/models/tags_test.go b/src/models/tags_test.go
new file mode 100644
index 0000000..16ecfdf
--- /dev/null
+++ b/src/models/tags_test.go
@@ -0,0 +1,1163 @@
+// Code generated by SQLBoiler 4.16.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT.
+// This file is meant to be re-generated in place and/or deleted at any time.
+
+package models
+
+import (
+ "bytes"
+ "context"
+ "reflect"
+ "testing"
+
+ "github.com/volatiletech/randomize"
+ "github.com/volatiletech/sqlboiler/v4/boil"
+ "github.com/volatiletech/sqlboiler/v4/queries"
+ "github.com/volatiletech/strmangle"
+)
+
+var (
+ // Relationships sometimes use the reflection helper queries.Equal/queries.Assign
+ // so force a package dependency in case they don't.
+ _ = queries.Equal
+)
+
+func testTags(t *testing.T) {
+ t.Parallel()
+
+ query := Tags()
+
+ if query.Query == nil {
+ t.Error("expected a query, got nothing")
+ }
+}
+
+func testTagsDelete(t *testing.T) {
+ t.Parallel()
+
+ seed := randomize.NewSeed()
+ var err error
+ o := &Tag{}
+ if err = randomize.Struct(seed, o, tagDBTypes, true, tagColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+ if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Error(err)
+ }
+
+ if rowsAff, err := o.Delete(ctx, tx); err != nil {
+ t.Error(err)
+ } else if rowsAff != 1 {
+ t.Error("should only have deleted one row, but affected:", rowsAff)
+ }
+
+ count, err := Tags().Count(ctx, tx)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if count != 0 {
+ t.Error("want zero records, got:", count)
+ }
+}
+
+func testTagsQueryDeleteAll(t *testing.T) {
+ t.Parallel()
+
+ seed := randomize.NewSeed()
+ var err error
+ o := &Tag{}
+ if err = randomize.Struct(seed, o, tagDBTypes, true, tagColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+ if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Error(err)
+ }
+
+ if rowsAff, err := Tags().DeleteAll(ctx, tx); err != nil {
+ t.Error(err)
+ } else if rowsAff != 1 {
+ t.Error("should only have deleted one row, but affected:", rowsAff)
+ }
+
+ count, err := Tags().Count(ctx, tx)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if count != 0 {
+ t.Error("want zero records, got:", count)
+ }
+}
+
+func testTagsSliceDeleteAll(t *testing.T) {
+ t.Parallel()
+
+ seed := randomize.NewSeed()
+ var err error
+ o := &Tag{}
+ if err = randomize.Struct(seed, o, tagDBTypes, true, tagColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+ if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Error(err)
+ }
+
+ slice := TagSlice{o}
+
+ if rowsAff, err := slice.DeleteAll(ctx, tx); err != nil {
+ t.Error(err)
+ } else if rowsAff != 1 {
+ t.Error("should only have deleted one row, but affected:", rowsAff)
+ }
+
+ count, err := Tags().Count(ctx, tx)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if count != 0 {
+ t.Error("want zero records, got:", count)
+ }
+}
+
+func testTagsExists(t *testing.T) {
+ t.Parallel()
+
+ seed := randomize.NewSeed()
+ var err error
+ o := &Tag{}
+ if err = randomize.Struct(seed, o, tagDBTypes, true, tagColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+ if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Error(err)
+ }
+
+ e, err := TagExists(ctx, tx, o.ID)
+ if err != nil {
+ t.Errorf("Unable to check if Tag exists: %s", err)
+ }
+ if !e {
+ t.Errorf("Expected TagExists to return true, but got false.")
+ }
+}
+
+func testTagsFind(t *testing.T) {
+ t.Parallel()
+
+ seed := randomize.NewSeed()
+ var err error
+ o := &Tag{}
+ if err = randomize.Struct(seed, o, tagDBTypes, true, tagColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+ if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Error(err)
+ }
+
+ tagFound, err := FindTag(ctx, tx, o.ID)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if tagFound == nil {
+ t.Error("want a record, got nil")
+ }
+}
+
+func testTagsBind(t *testing.T) {
+ t.Parallel()
+
+ seed := randomize.NewSeed()
+ var err error
+ o := &Tag{}
+ if err = randomize.Struct(seed, o, tagDBTypes, true, tagColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+ if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Error(err)
+ }
+
+ if err = Tags().Bind(ctx, tx, o); err != nil {
+ t.Error(err)
+ }
+}
+
+func testTagsOne(t *testing.T) {
+ t.Parallel()
+
+ seed := randomize.NewSeed()
+ var err error
+ o := &Tag{}
+ if err = randomize.Struct(seed, o, tagDBTypes, true, tagColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+ if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Error(err)
+ }
+
+ if x, err := Tags().One(ctx, tx); err != nil {
+ t.Error(err)
+ } else if x == nil {
+ t.Error("expected to get a non nil record")
+ }
+}
+
+func testTagsAll(t *testing.T) {
+ t.Parallel()
+
+ seed := randomize.NewSeed()
+ var err error
+ tagOne := &Tag{}
+ tagTwo := &Tag{}
+ if err = randomize.Struct(seed, tagOne, tagDBTypes, false, tagColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+ if err = randomize.Struct(seed, tagTwo, tagDBTypes, false, tagColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+ if err = tagOne.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Error(err)
+ }
+ if err = tagTwo.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Error(err)
+ }
+
+ slice, err := Tags().All(ctx, tx)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if len(slice) != 2 {
+ t.Error("want 2 records, got:", len(slice))
+ }
+}
+
+func testTagsCount(t *testing.T) {
+ t.Parallel()
+
+ var err error
+ seed := randomize.NewSeed()
+ tagOne := &Tag{}
+ tagTwo := &Tag{}
+ if err = randomize.Struct(seed, tagOne, tagDBTypes, false, tagColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+ if err = randomize.Struct(seed, tagTwo, tagDBTypes, false, tagColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+ if err = tagOne.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Error(err)
+ }
+ if err = tagTwo.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Error(err)
+ }
+
+ count, err := Tags().Count(ctx, tx)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if count != 2 {
+ t.Error("want 2 records, got:", count)
+ }
+}
+
+func tagBeforeInsertHook(ctx context.Context, e boil.ContextExecutor, o *Tag) error {
+ *o = Tag{}
+ return nil
+}
+
+func tagAfterInsertHook(ctx context.Context, e boil.ContextExecutor, o *Tag) error {
+ *o = Tag{}
+ return nil
+}
+
+func tagAfterSelectHook(ctx context.Context, e boil.ContextExecutor, o *Tag) error {
+ *o = Tag{}
+ return nil
+}
+
+func tagBeforeUpdateHook(ctx context.Context, e boil.ContextExecutor, o *Tag) error {
+ *o = Tag{}
+ return nil
+}
+
+func tagAfterUpdateHook(ctx context.Context, e boil.ContextExecutor, o *Tag) error {
+ *o = Tag{}
+ return nil
+}
+
+func tagBeforeDeleteHook(ctx context.Context, e boil.ContextExecutor, o *Tag) error {
+ *o = Tag{}
+ return nil
+}
+
+func tagAfterDeleteHook(ctx context.Context, e boil.ContextExecutor, o *Tag) error {
+ *o = Tag{}
+ return nil
+}
+
+func tagBeforeUpsertHook(ctx context.Context, e boil.ContextExecutor, o *Tag) error {
+ *o = Tag{}
+ return nil
+}
+
+func tagAfterUpsertHook(ctx context.Context, e boil.ContextExecutor, o *Tag) error {
+ *o = Tag{}
+ return nil
+}
+
+func testTagsHooks(t *testing.T) {
+ t.Parallel()
+
+ var err error
+
+ ctx := context.Background()
+ empty := &Tag{}
+ o := &Tag{}
+
+ seed := randomize.NewSeed()
+ if err = randomize.Struct(seed, o, tagDBTypes, false); err != nil {
+ t.Errorf("Unable to randomize Tag object: %s", err)
+ }
+
+ AddTagHook(boil.BeforeInsertHook, tagBeforeInsertHook)
+ if err = o.doBeforeInsertHooks(ctx, nil); err != nil {
+ t.Errorf("Unable to execute doBeforeInsertHooks: %s", err)
+ }
+ if !reflect.DeepEqual(o, empty) {
+ t.Errorf("Expected BeforeInsertHook function to empty object, but got: %#v", o)
+ }
+ tagBeforeInsertHooks = []TagHook{}
+
+ AddTagHook(boil.AfterInsertHook, tagAfterInsertHook)
+ if err = o.doAfterInsertHooks(ctx, nil); err != nil {
+ t.Errorf("Unable to execute doAfterInsertHooks: %s", err)
+ }
+ if !reflect.DeepEqual(o, empty) {
+ t.Errorf("Expected AfterInsertHook function to empty object, but got: %#v", o)
+ }
+ tagAfterInsertHooks = []TagHook{}
+
+ AddTagHook(boil.AfterSelectHook, tagAfterSelectHook)
+ if err = o.doAfterSelectHooks(ctx, nil); err != nil {
+ t.Errorf("Unable to execute doAfterSelectHooks: %s", err)
+ }
+ if !reflect.DeepEqual(o, empty) {
+ t.Errorf("Expected AfterSelectHook function to empty object, but got: %#v", o)
+ }
+ tagAfterSelectHooks = []TagHook{}
+
+ AddTagHook(boil.BeforeUpdateHook, tagBeforeUpdateHook)
+ if err = o.doBeforeUpdateHooks(ctx, nil); err != nil {
+ t.Errorf("Unable to execute doBeforeUpdateHooks: %s", err)
+ }
+ if !reflect.DeepEqual(o, empty) {
+ t.Errorf("Expected BeforeUpdateHook function to empty object, but got: %#v", o)
+ }
+ tagBeforeUpdateHooks = []TagHook{}
+
+ AddTagHook(boil.AfterUpdateHook, tagAfterUpdateHook)
+ if err = o.doAfterUpdateHooks(ctx, nil); err != nil {
+ t.Errorf("Unable to execute doAfterUpdateHooks: %s", err)
+ }
+ if !reflect.DeepEqual(o, empty) {
+ t.Errorf("Expected AfterUpdateHook function to empty object, but got: %#v", o)
+ }
+ tagAfterUpdateHooks = []TagHook{}
+
+ AddTagHook(boil.BeforeDeleteHook, tagBeforeDeleteHook)
+ if err = o.doBeforeDeleteHooks(ctx, nil); err != nil {
+ t.Errorf("Unable to execute doBeforeDeleteHooks: %s", err)
+ }
+ if !reflect.DeepEqual(o, empty) {
+ t.Errorf("Expected BeforeDeleteHook function to empty object, but got: %#v", o)
+ }
+ tagBeforeDeleteHooks = []TagHook{}
+
+ AddTagHook(boil.AfterDeleteHook, tagAfterDeleteHook)
+ if err = o.doAfterDeleteHooks(ctx, nil); err != nil {
+ t.Errorf("Unable to execute doAfterDeleteHooks: %s", err)
+ }
+ if !reflect.DeepEqual(o, empty) {
+ t.Errorf("Expected AfterDeleteHook function to empty object, but got: %#v", o)
+ }
+ tagAfterDeleteHooks = []TagHook{}
+
+ AddTagHook(boil.BeforeUpsertHook, tagBeforeUpsertHook)
+ if err = o.doBeforeUpsertHooks(ctx, nil); err != nil {
+ t.Errorf("Unable to execute doBeforeUpsertHooks: %s", err)
+ }
+ if !reflect.DeepEqual(o, empty) {
+ t.Errorf("Expected BeforeUpsertHook function to empty object, but got: %#v", o)
+ }
+ tagBeforeUpsertHooks = []TagHook{}
+
+ AddTagHook(boil.AfterUpsertHook, tagAfterUpsertHook)
+ if err = o.doAfterUpsertHooks(ctx, nil); err != nil {
+ t.Errorf("Unable to execute doAfterUpsertHooks: %s", err)
+ }
+ if !reflect.DeepEqual(o, empty) {
+ t.Errorf("Expected AfterUpsertHook function to empty object, but got: %#v", o)
+ }
+ tagAfterUpsertHooks = []TagHook{}
+}
+
+func testTagsInsert(t *testing.T) {
+ t.Parallel()
+
+ seed := randomize.NewSeed()
+ var err error
+ o := &Tag{}
+ if err = randomize.Struct(seed, o, tagDBTypes, true, tagColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+ if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Error(err)
+ }
+
+ count, err := Tags().Count(ctx, tx)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if count != 1 {
+ t.Error("want one record, got:", count)
+ }
+}
+
+func testTagsInsertWhitelist(t *testing.T) {
+ t.Parallel()
+
+ seed := randomize.NewSeed()
+ var err error
+ o := &Tag{}
+ if err = randomize.Struct(seed, o, tagDBTypes, true); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+ if err = o.Insert(ctx, tx, boil.Whitelist(tagColumnsWithoutDefault...)); err != nil {
+ t.Error(err)
+ }
+
+ count, err := Tags().Count(ctx, tx)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if count != 1 {
+ t.Error("want one record, got:", count)
+ }
+}
+
+func testTagToManyPosts(t *testing.T) {
+ var err error
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+
+ var a Tag
+ var b, c Post
+
+ seed := randomize.NewSeed()
+ if err = randomize.Struct(seed, &a, tagDBTypes, true, tagColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ if err = randomize.Struct(seed, &b, postDBTypes, false, postColumnsWithDefault...); err != nil {
+ t.Fatal(err)
+ }
+ if err = randomize.Struct(seed, &c, postDBTypes, false, postColumnsWithDefault...); err != nil {
+ t.Fatal(err)
+ }
+
+ if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+ if err = c.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = tx.Exec("insert into \"post_tags\" (\"tag_id\", \"post_id\") values ($1, $2)", a.ID, b.ID)
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = tx.Exec("insert into \"post_tags\" (\"tag_id\", \"post_id\") values ($1, $2)", a.ID, c.ID)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ check, err := a.Posts().All(ctx, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ bFound, cFound := false, false
+ for _, v := range check {
+ if v.ID == b.ID {
+ bFound = true
+ }
+ if v.ID == c.ID {
+ cFound = true
+ }
+ }
+
+ if !bFound {
+ t.Error("expected to find b")
+ }
+ if !cFound {
+ t.Error("expected to find c")
+ }
+
+ slice := TagSlice{&a}
+ if err = a.L.LoadPosts(ctx, tx, false, (*[]*Tag)(&slice), nil); err != nil {
+ t.Fatal(err)
+ }
+ if got := len(a.R.Posts); got != 2 {
+ t.Error("number of eager loaded records wrong, got:", got)
+ }
+
+ a.R.Posts = nil
+ if err = a.L.LoadPosts(ctx, tx, true, &a, nil); err != nil {
+ t.Fatal(err)
+ }
+ if got := len(a.R.Posts); got != 2 {
+ t.Error("number of eager loaded records wrong, got:", got)
+ }
+
+ if t.Failed() {
+ t.Logf("%#v", check)
+ }
+}
+
+func testTagToManyAddOpPosts(t *testing.T) {
+ var err error
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+
+ var a Tag
+ var b, c, d, e Post
+
+ seed := randomize.NewSeed()
+ if err = randomize.Struct(seed, &a, tagDBTypes, false, strmangle.SetComplement(tagPrimaryKeyColumns, tagColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ foreigners := []*Post{&b, &c, &d, &e}
+ for _, x := range foreigners {
+ if err = randomize.Struct(seed, x, postDBTypes, false, strmangle.SetComplement(postPrimaryKeyColumns, postColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+ if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+ if err = c.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ foreignersSplitByInsertion := [][]*Post{
+ {&b, &c},
+ {&d, &e},
+ }
+
+ for i, x := range foreignersSplitByInsertion {
+ err = a.AddPosts(ctx, tx, i != 0, x...)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ first := x[0]
+ second := x[1]
+
+ if first.R.Tags[0] != &a {
+ t.Error("relationship was not added properly to the slice")
+ }
+ if second.R.Tags[0] != &a {
+ t.Error("relationship was not added properly to the slice")
+ }
+
+ if a.R.Posts[i*2] != first {
+ t.Error("relationship struct slice not set to correct value")
+ }
+ if a.R.Posts[i*2+1] != second {
+ t.Error("relationship struct slice not set to correct value")
+ }
+
+ count, err := a.Posts().Count(ctx, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if want := int64((i + 1) * 2); count != want {
+ t.Error("want", want, "got", count)
+ }
+ }
+}
+
+func testTagToManySetOpPosts(t *testing.T) {
+ var err error
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+
+ var a Tag
+ var b, c, d, e Post
+
+ seed := randomize.NewSeed()
+ if err = randomize.Struct(seed, &a, tagDBTypes, false, strmangle.SetComplement(tagPrimaryKeyColumns, tagColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ foreigners := []*Post{&b, &c, &d, &e}
+ for _, x := range foreigners {
+ if err = randomize.Struct(seed, x, postDBTypes, false, strmangle.SetComplement(postPrimaryKeyColumns, postColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ if err = a.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+ if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+ if err = c.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ err = a.SetPosts(ctx, tx, false, &b, &c)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ count, err := a.Posts().Count(ctx, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if count != 2 {
+ t.Error("count was wrong:", count)
+ }
+
+ err = a.SetPosts(ctx, tx, true, &d, &e)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ count, err = a.Posts().Count(ctx, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if count != 2 {
+ t.Error("count was wrong:", count)
+ }
+
+ // The following checks cannot be implemented since we have no handle
+ // to these when we call Set(). Leaving them here as wishful thinking
+ // and to let people know there's dragons.
+ //
+ // if len(b.R.Tags) != 0 {
+ // t.Error("relationship was not removed properly from the slice")
+ // }
+ // if len(c.R.Tags) != 0 {
+ // t.Error("relationship was not removed properly from the slice")
+ // }
+ if d.R.Tags[0] != &a {
+ t.Error("relationship was not added properly to the slice")
+ }
+ if e.R.Tags[0] != &a {
+ t.Error("relationship was not added properly to the slice")
+ }
+
+ if a.R.Posts[0] != &d {
+ t.Error("relationship struct slice not set to correct value")
+ }
+ if a.R.Posts[1] != &e {
+ t.Error("relationship struct slice not set to correct value")
+ }
+}
+
+func testTagToManyRemoveOpPosts(t *testing.T) {
+ var err error
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+
+ var a Tag
+ var b, c, d, e Post
+
+ seed := randomize.NewSeed()
+ if err = randomize.Struct(seed, &a, tagDBTypes, false, strmangle.SetComplement(tagPrimaryKeyColumns, tagColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ foreigners := []*Post{&b, &c, &d, &e}
+ for _, x := range foreigners {
+ if err = randomize.Struct(seed, x, postDBTypes, false, strmangle.SetComplement(postPrimaryKeyColumns, postColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ err = a.AddPosts(ctx, tx, true, foreigners...)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ count, err := a.Posts().Count(ctx, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if count != 4 {
+ t.Error("count was wrong:", count)
+ }
+
+ err = a.RemovePosts(ctx, tx, foreigners[:2]...)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ count, err = a.Posts().Count(ctx, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if count != 2 {
+ t.Error("count was wrong:", count)
+ }
+
+ if len(b.R.Tags) != 0 {
+ t.Error("relationship was not removed properly from the slice")
+ }
+ if len(c.R.Tags) != 0 {
+ t.Error("relationship was not removed properly from the slice")
+ }
+ if d.R.Tags[0] != &a {
+ t.Error("relationship was not added properly to the foreign struct")
+ }
+ if e.R.Tags[0] != &a {
+ t.Error("relationship was not added properly to the foreign struct")
+ }
+
+ if len(a.R.Posts) != 2 {
+ t.Error("should have preserved two relationships")
+ }
+
+ // Removal doesn't do a stable deletion for performance so we have to flip the order
+ if a.R.Posts[1] != &d {
+ t.Error("relationship to d should have been preserved")
+ }
+ if a.R.Posts[0] != &e {
+ t.Error("relationship to e should have been preserved")
+ }
+}
+
+func testTagToOneUserUsingUser(t *testing.T) {
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+
+ var local Tag
+ var foreign User
+
+ seed := randomize.NewSeed()
+ if err := randomize.Struct(seed, &local, tagDBTypes, false, tagColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+ if err := randomize.Struct(seed, &foreign, userDBTypes, false, userColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize User struct: %s", err)
+ }
+
+ if err := foreign.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ local.UserID = foreign.ID
+ if err := local.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ check, err := local.User().One(ctx, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if check.ID != foreign.ID {
+ t.Errorf("want: %v, got %v", foreign.ID, check.ID)
+ }
+
+ ranAfterSelectHook := false
+ AddUserHook(boil.AfterSelectHook, func(ctx context.Context, e boil.ContextExecutor, o *User) error {
+ ranAfterSelectHook = true
+ return nil
+ })
+
+ slice := TagSlice{&local}
+ if err = local.L.LoadUser(ctx, tx, false, (*[]*Tag)(&slice), nil); err != nil {
+ t.Fatal(err)
+ }
+ if local.R.User == nil {
+ t.Error("struct should have been eager loaded")
+ }
+
+ local.R.User = nil
+ if err = local.L.LoadUser(ctx, tx, true, &local, nil); err != nil {
+ t.Fatal(err)
+ }
+ if local.R.User == nil {
+ t.Error("struct should have been eager loaded")
+ }
+
+ if !ranAfterSelectHook {
+ t.Error("failed to run AfterSelect hook for relationship")
+ }
+}
+
+func testTagToOneSetOpUserUsingUser(t *testing.T) {
+ var err error
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+
+ var a Tag
+ var b, c User
+
+ seed := randomize.NewSeed()
+ if err = randomize.Struct(seed, &a, tagDBTypes, false, strmangle.SetComplement(tagPrimaryKeyColumns, tagColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ if err = randomize.Struct(seed, &b, userDBTypes, false, strmangle.SetComplement(userPrimaryKeyColumns, userColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ if err = randomize.Struct(seed, &c, userDBTypes, false, strmangle.SetComplement(userPrimaryKeyColumns, userColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+ if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ for i, x := range []*User{&b, &c} {
+ err = a.SetUser(ctx, tx, i != 0, x)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if a.R.User != x {
+ t.Error("relationship struct not set to correct value")
+ }
+
+ if x.R.Tags[0] != &a {
+ t.Error("failed to append to foreign relationship struct")
+ }
+ if a.UserID != x.ID {
+ t.Error("foreign key was wrong value", a.UserID)
+ }
+
+ zero := reflect.Zero(reflect.TypeOf(a.UserID))
+ reflect.Indirect(reflect.ValueOf(&a.UserID)).Set(zero)
+
+ if err = a.Reload(ctx, tx); err != nil {
+ t.Fatal("failed to reload", err)
+ }
+
+ if a.UserID != x.ID {
+ t.Error("foreign key was wrong value", a.UserID, x.ID)
+ }
+ }
+}
+
+func testTagsReload(t *testing.T) {
+ t.Parallel()
+
+ seed := randomize.NewSeed()
+ var err error
+ o := &Tag{}
+ if err = randomize.Struct(seed, o, tagDBTypes, true, tagColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+ if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Error(err)
+ }
+
+ if err = o.Reload(ctx, tx); err != nil {
+ t.Error(err)
+ }
+}
+
+func testTagsReloadAll(t *testing.T) {
+ t.Parallel()
+
+ seed := randomize.NewSeed()
+ var err error
+ o := &Tag{}
+ if err = randomize.Struct(seed, o, tagDBTypes, true, tagColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+ if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Error(err)
+ }
+
+ slice := TagSlice{o}
+
+ if err = slice.ReloadAll(ctx, tx); err != nil {
+ t.Error(err)
+ }
+}
+
+func testTagsSelect(t *testing.T) {
+ t.Parallel()
+
+ seed := randomize.NewSeed()
+ var err error
+ o := &Tag{}
+ if err = randomize.Struct(seed, o, tagDBTypes, true, tagColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+ if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Error(err)
+ }
+
+ slice, err := Tags().All(ctx, tx)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if len(slice) != 1 {
+ t.Error("want one record, got:", len(slice))
+ }
+}
+
+var (
+ tagDBTypes = map[string]string{`ID`: `integer`, `Tag`: `character varying`, `Description`: `text`, `UserID`: `integer`, `CreatedAt`: `timestamp with time zone`, `UpdatedAt`: `timestamp with time zone`}
+ _ = bytes.MinRead
+)
+
+func testTagsUpdate(t *testing.T) {
+ t.Parallel()
+
+ if 0 == len(tagPrimaryKeyColumns) {
+ t.Skip("Skipping table with no primary key columns")
+ }
+ if len(tagAllColumns) == len(tagPrimaryKeyColumns) {
+ t.Skip("Skipping table with only primary key columns")
+ }
+
+ seed := randomize.NewSeed()
+ var err error
+ o := &Tag{}
+ if err = randomize.Struct(seed, o, tagDBTypes, true, tagColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+ if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Error(err)
+ }
+
+ count, err := Tags().Count(ctx, tx)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if count != 1 {
+ t.Error("want one record, got:", count)
+ }
+
+ if err = randomize.Struct(seed, o, tagDBTypes, true, tagPrimaryKeyColumns...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ if rowsAff, err := o.Update(ctx, tx, boil.Infer()); err != nil {
+ t.Error(err)
+ } else if rowsAff != 1 {
+ t.Error("should only affect one row but affected", rowsAff)
+ }
+}
+
+func testTagsSliceUpdateAll(t *testing.T) {
+ t.Parallel()
+
+ if len(tagAllColumns) == len(tagPrimaryKeyColumns) {
+ t.Skip("Skipping table with only primary key columns")
+ }
+
+ seed := randomize.NewSeed()
+ var err error
+ o := &Tag{}
+ if err = randomize.Struct(seed, o, tagDBTypes, true, tagColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+ if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Error(err)
+ }
+
+ count, err := Tags().Count(ctx, tx)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if count != 1 {
+ t.Error("want one record, got:", count)
+ }
+
+ if err = randomize.Struct(seed, o, tagDBTypes, true, tagPrimaryKeyColumns...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ // Remove Primary keys and unique columns from what we plan to update
+ var fields []string
+ if strmangle.StringSliceMatch(tagAllColumns, tagPrimaryKeyColumns) {
+ fields = tagAllColumns
+ } else {
+ fields = strmangle.SetComplement(
+ tagAllColumns,
+ tagPrimaryKeyColumns,
+ )
+ }
+
+ value := reflect.Indirect(reflect.ValueOf(o))
+ typ := reflect.TypeOf(o).Elem()
+ n := typ.NumField()
+
+ updateMap := M{}
+ for _, col := range fields {
+ for i := 0; i < n; i++ {
+ f := typ.Field(i)
+ if f.Tag.Get("boil") == col {
+ updateMap[col] = value.Field(i).Interface()
+ }
+ }
+ }
+
+ slice := TagSlice{o}
+ if rowsAff, err := slice.UpdateAll(ctx, tx, updateMap); err != nil {
+ t.Error(err)
+ } else if rowsAff != 1 {
+ t.Error("wanted one record updated but got", rowsAff)
+ }
+}
+
+func testTagsUpsert(t *testing.T) {
+ t.Parallel()
+
+ if len(tagAllColumns) == len(tagPrimaryKeyColumns) {
+ t.Skip("Skipping table with only primary key columns")
+ }
+
+ seed := randomize.NewSeed()
+ var err error
+ // Attempt the INSERT side of an UPSERT
+ o := Tag{}
+ if err = randomize.Struct(seed, &o, tagDBTypes, true); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+ if err = o.Upsert(ctx, tx, false, nil, boil.Infer(), boil.Infer()); err != nil {
+ t.Errorf("Unable to upsert Tag: %s", err)
+ }
+
+ count, err := Tags().Count(ctx, tx)
+ if err != nil {
+ t.Error(err)
+ }
+ if count != 1 {
+ t.Error("want one record, got:", count)
+ }
+
+ // Attempt the UPDATE side of an UPSERT
+ if err = randomize.Struct(seed, &o, tagDBTypes, false, tagPrimaryKeyColumns...); err != nil {
+ t.Errorf("Unable to randomize Tag struct: %s", err)
+ }
+
+ if err = o.Upsert(ctx, tx, true, nil, boil.Infer(), boil.Infer()); err != nil {
+ t.Errorf("Unable to upsert Tag: %s", err)
+ }
+
+ count, err = Tags().Count(ctx, tx)
+ if err != nil {
+ t.Error(err)
+ }
+ if count != 1 {
+ t.Error("want one record, got:", count)
+ }
+}
diff --git a/src/models/users.go b/src/models/users.go
index ca9bcf8..bdb6a31 100644
--- a/src/models/users.go
+++ b/src/models/users.go
@@ -87,10 +87,17 @@ var UserWhere = struct {
// UserRels is where relationship names are stored.
var UserRels = struct {
-}{}
+ Posts string
+ Tags string
+}{
+ Posts: "Posts",
+ Tags: "Tags",
+}
// userR is where relationships are stored.
type userR struct {
+ Posts PostSlice `boil:"Posts" json:"Posts" toml:"Posts" yaml:"Posts"`
+ Tags TagSlice `boil:"Tags" json:"Tags" toml:"Tags" yaml:"Tags"`
}
// NewStruct creates a new relationship struct
@@ -98,6 +105,20 @@ func (*userR) NewStruct() *userR {
return &userR{}
}
+func (r *userR) GetPosts() PostSlice {
+ if r == nil {
+ return nil
+ }
+ return r.Posts
+}
+
+func (r *userR) GetTags() TagSlice {
+ if r == nil {
+ return nil
+ }
+ return r.Tags
+}
+
// userL is where Load methods for each relationship are stored.
type userL struct{}
@@ -414,6 +435,366 @@ func (q userQuery) Exists(ctx context.Context, exec boil.ContextExecutor) (bool,
return count > 0, nil
}
+// Posts retrieves all the post's Posts with an executor.
+func (o *User) Posts(mods ...qm.QueryMod) postQuery {
+ var queryMods []qm.QueryMod
+ if len(mods) != 0 {
+ queryMods = append(queryMods, mods...)
+ }
+
+ queryMods = append(queryMods,
+ qm.Where("\"posts\".\"user_id\"=?", o.ID),
+ )
+
+ return Posts(queryMods...)
+}
+
+// Tags retrieves all the tag's Tags with an executor.
+func (o *User) Tags(mods ...qm.QueryMod) tagQuery {
+ var queryMods []qm.QueryMod
+ if len(mods) != 0 {
+ queryMods = append(queryMods, mods...)
+ }
+
+ queryMods = append(queryMods,
+ qm.Where("\"tags\".\"user_id\"=?", o.ID),
+ )
+
+ return Tags(queryMods...)
+}
+
+// LoadPosts allows an eager lookup of values, cached into the
+// loaded structs of the objects. This is for a 1-M or N-M relationship.
+func (userL) LoadPosts(ctx context.Context, e boil.ContextExecutor, singular bool, maybeUser interface{}, mods queries.Applicator) error {
+ var slice []*User
+ var object *User
+
+ if singular {
+ var ok bool
+ object, ok = maybeUser.(*User)
+ if !ok {
+ object = new(User)
+ ok = queries.SetFromEmbeddedStruct(&object, &maybeUser)
+ if !ok {
+ return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", object, maybeUser))
+ }
+ }
+ } else {
+ s, ok := maybeUser.(*[]*User)
+ if ok {
+ slice = *s
+ } else {
+ ok = queries.SetFromEmbeddedStruct(&slice, maybeUser)
+ if !ok {
+ return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", slice, maybeUser))
+ }
+ }
+ }
+
+ args := make(map[interface{}]struct{})
+ if singular {
+ if object.R == nil {
+ object.R = &userR{}
+ }
+ args[object.ID] = struct{}{}
+ } else {
+ for _, obj := range slice {
+ if obj.R == nil {
+ obj.R = &userR{}
+ }
+ args[obj.ID] = struct{}{}
+ }
+ }
+
+ if len(args) == 0 {
+ return nil
+ }
+
+ argsSlice := make([]interface{}, len(args))
+ i := 0
+ for arg := range args {
+ argsSlice[i] = arg
+ i++
+ }
+
+ query := NewQuery(
+ qm.From(`posts`),
+ qm.WhereIn(`posts.user_id in ?`, argsSlice...),
+ )
+ if mods != nil {
+ mods.Apply(query)
+ }
+
+ results, err := query.QueryContext(ctx, e)
+ if err != nil {
+ return errors.Wrap(err, "failed to eager load posts")
+ }
+
+ var resultSlice []*Post
+ if err = queries.Bind(results, &resultSlice); err != nil {
+ return errors.Wrap(err, "failed to bind eager loaded slice posts")
+ }
+
+ if err = results.Close(); err != nil {
+ return errors.Wrap(err, "failed to close results in eager load on posts")
+ }
+ if err = results.Err(); err != nil {
+ return errors.Wrap(err, "error occurred during iteration of eager loaded relations for posts")
+ }
+
+ if len(postAfterSelectHooks) != 0 {
+ for _, obj := range resultSlice {
+ if err := obj.doAfterSelectHooks(ctx, e); err != nil {
+ return err
+ }
+ }
+ }
+ if singular {
+ object.R.Posts = resultSlice
+ for _, foreign := range resultSlice {
+ if foreign.R == nil {
+ foreign.R = &postR{}
+ }
+ foreign.R.User = object
+ }
+ return nil
+ }
+
+ for _, foreign := range resultSlice {
+ for _, local := range slice {
+ if local.ID == foreign.UserID {
+ local.R.Posts = append(local.R.Posts, foreign)
+ if foreign.R == nil {
+ foreign.R = &postR{}
+ }
+ foreign.R.User = local
+ break
+ }
+ }
+ }
+
+ return nil
+}
+
+// LoadTags allows an eager lookup of values, cached into the
+// loaded structs of the objects. This is for a 1-M or N-M relationship.
+func (userL) LoadTags(ctx context.Context, e boil.ContextExecutor, singular bool, maybeUser interface{}, mods queries.Applicator) error {
+ var slice []*User
+ var object *User
+
+ if singular {
+ var ok bool
+ object, ok = maybeUser.(*User)
+ if !ok {
+ object = new(User)
+ ok = queries.SetFromEmbeddedStruct(&object, &maybeUser)
+ if !ok {
+ return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", object, maybeUser))
+ }
+ }
+ } else {
+ s, ok := maybeUser.(*[]*User)
+ if ok {
+ slice = *s
+ } else {
+ ok = queries.SetFromEmbeddedStruct(&slice, maybeUser)
+ if !ok {
+ return errors.New(fmt.Sprintf("failed to set %T from embedded struct %T", slice, maybeUser))
+ }
+ }
+ }
+
+ args := make(map[interface{}]struct{})
+ if singular {
+ if object.R == nil {
+ object.R = &userR{}
+ }
+ args[object.ID] = struct{}{}
+ } else {
+ for _, obj := range slice {
+ if obj.R == nil {
+ obj.R = &userR{}
+ }
+ args[obj.ID] = struct{}{}
+ }
+ }
+
+ if len(args) == 0 {
+ return nil
+ }
+
+ argsSlice := make([]interface{}, len(args))
+ i := 0
+ for arg := range args {
+ argsSlice[i] = arg
+ i++
+ }
+
+ query := NewQuery(
+ qm.From(`tags`),
+ qm.WhereIn(`tags.user_id in ?`, argsSlice...),
+ )
+ if mods != nil {
+ mods.Apply(query)
+ }
+
+ results, err := query.QueryContext(ctx, e)
+ if err != nil {
+ return errors.Wrap(err, "failed to eager load tags")
+ }
+
+ var resultSlice []*Tag
+ if err = queries.Bind(results, &resultSlice); err != nil {
+ return errors.Wrap(err, "failed to bind eager loaded slice tags")
+ }
+
+ if err = results.Close(); err != nil {
+ return errors.Wrap(err, "failed to close results in eager load on tags")
+ }
+ if err = results.Err(); err != nil {
+ return errors.Wrap(err, "error occurred during iteration of eager loaded relations for tags")
+ }
+
+ if len(tagAfterSelectHooks) != 0 {
+ for _, obj := range resultSlice {
+ if err := obj.doAfterSelectHooks(ctx, e); err != nil {
+ return err
+ }
+ }
+ }
+ if singular {
+ object.R.Tags = resultSlice
+ for _, foreign := range resultSlice {
+ if foreign.R == nil {
+ foreign.R = &tagR{}
+ }
+ foreign.R.User = object
+ }
+ return nil
+ }
+
+ for _, foreign := range resultSlice {
+ for _, local := range slice {
+ if local.ID == foreign.UserID {
+ local.R.Tags = append(local.R.Tags, foreign)
+ if foreign.R == nil {
+ foreign.R = &tagR{}
+ }
+ foreign.R.User = local
+ break
+ }
+ }
+ }
+
+ return nil
+}
+
+// AddPosts adds the given related objects to the existing relationships
+// of the user, optionally inserting them as new records.
+// Appends related to o.R.Posts.
+// Sets related.R.User appropriately.
+func (o *User) AddPosts(ctx context.Context, exec boil.ContextExecutor, insert bool, related ...*Post) error {
+ var err error
+ for _, rel := range related {
+ if insert {
+ rel.UserID = o.ID
+ if err = rel.Insert(ctx, exec, boil.Infer()); err != nil {
+ return errors.Wrap(err, "failed to insert into foreign table")
+ }
+ } else {
+ updateQuery := fmt.Sprintf(
+ "UPDATE \"posts\" SET %s WHERE %s",
+ strmangle.SetParamNames("\"", "\"", 1, []string{"user_id"}),
+ strmangle.WhereClause("\"", "\"", 2, postPrimaryKeyColumns),
+ )
+ values := []interface{}{o.ID, rel.ID}
+
+ if boil.IsDebug(ctx) {
+ writer := boil.DebugWriterFrom(ctx)
+ fmt.Fprintln(writer, updateQuery)
+ fmt.Fprintln(writer, values)
+ }
+ if _, err = exec.ExecContext(ctx, updateQuery, values...); err != nil {
+ return errors.Wrap(err, "failed to update foreign table")
+ }
+
+ rel.UserID = o.ID
+ }
+ }
+
+ if o.R == nil {
+ o.R = &userR{
+ Posts: related,
+ }
+ } else {
+ o.R.Posts = append(o.R.Posts, related...)
+ }
+
+ for _, rel := range related {
+ if rel.R == nil {
+ rel.R = &postR{
+ User: o,
+ }
+ } else {
+ rel.R.User = o
+ }
+ }
+ return nil
+}
+
+// AddTags adds the given related objects to the existing relationships
+// of the user, optionally inserting them as new records.
+// Appends related to o.R.Tags.
+// Sets related.R.User appropriately.
+func (o *User) AddTags(ctx context.Context, exec boil.ContextExecutor, insert bool, related ...*Tag) error {
+ var err error
+ for _, rel := range related {
+ if insert {
+ rel.UserID = o.ID
+ if err = rel.Insert(ctx, exec, boil.Infer()); err != nil {
+ return errors.Wrap(err, "failed to insert into foreign table")
+ }
+ } else {
+ updateQuery := fmt.Sprintf(
+ "UPDATE \"tags\" SET %s WHERE %s",
+ strmangle.SetParamNames("\"", "\"", 1, []string{"user_id"}),
+ strmangle.WhereClause("\"", "\"", 2, tagPrimaryKeyColumns),
+ )
+ values := []interface{}{o.ID, rel.ID}
+
+ if boil.IsDebug(ctx) {
+ writer := boil.DebugWriterFrom(ctx)
+ fmt.Fprintln(writer, updateQuery)
+ fmt.Fprintln(writer, values)
+ }
+ if _, err = exec.ExecContext(ctx, updateQuery, values...); err != nil {
+ return errors.Wrap(err, "failed to update foreign table")
+ }
+
+ rel.UserID = o.ID
+ }
+ }
+
+ if o.R == nil {
+ o.R = &userR{
+ Tags: related,
+ }
+ } else {
+ o.R.Tags = append(o.R.Tags, related...)
+ }
+
+ for _, rel := range related {
+ if rel.R == nil {
+ rel.R = &tagR{
+ User: o,
+ }
+ } else {
+ rel.R.User = o
+ }
+ }
+ return nil
+}
+
// Users retrieves all the records using an executor.
func Users(mods ...qm.QueryMod) userQuery {
mods = append(mods, qm.From("\"users\""))
diff --git a/src/models/users_test.go b/src/models/users_test.go
index 878ae32..db6ca4c 100644
--- a/src/models/users_test.go
+++ b/src/models/users_test.go
@@ -494,6 +494,313 @@ func testUsersInsertWhitelist(t *testing.T) {
}
}
+func testUserToManyPosts(t *testing.T) {
+ var err error
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+
+ var a User
+ var b, c Post
+
+ seed := randomize.NewSeed()
+ if err = randomize.Struct(seed, &a, userDBTypes, true, userColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize User struct: %s", err)
+ }
+
+ if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ if err = randomize.Struct(seed, &b, postDBTypes, false, postColumnsWithDefault...); err != nil {
+ t.Fatal(err)
+ }
+ if err = randomize.Struct(seed, &c, postDBTypes, false, postColumnsWithDefault...); err != nil {
+ t.Fatal(err)
+ }
+
+ b.UserID = a.ID
+ c.UserID = a.ID
+
+ if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+ if err = c.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ check, err := a.Posts().All(ctx, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ bFound, cFound := false, false
+ for _, v := range check {
+ if v.UserID == b.UserID {
+ bFound = true
+ }
+ if v.UserID == c.UserID {
+ cFound = true
+ }
+ }
+
+ if !bFound {
+ t.Error("expected to find b")
+ }
+ if !cFound {
+ t.Error("expected to find c")
+ }
+
+ slice := UserSlice{&a}
+ if err = a.L.LoadPosts(ctx, tx, false, (*[]*User)(&slice), nil); err != nil {
+ t.Fatal(err)
+ }
+ if got := len(a.R.Posts); got != 2 {
+ t.Error("number of eager loaded records wrong, got:", got)
+ }
+
+ a.R.Posts = nil
+ if err = a.L.LoadPosts(ctx, tx, true, &a, nil); err != nil {
+ t.Fatal(err)
+ }
+ if got := len(a.R.Posts); got != 2 {
+ t.Error("number of eager loaded records wrong, got:", got)
+ }
+
+ if t.Failed() {
+ t.Logf("%#v", check)
+ }
+}
+
+func testUserToManyTags(t *testing.T) {
+ var err error
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+
+ var a User
+ var b, c Tag
+
+ seed := randomize.NewSeed()
+ if err = randomize.Struct(seed, &a, userDBTypes, true, userColumnsWithDefault...); err != nil {
+ t.Errorf("Unable to randomize User struct: %s", err)
+ }
+
+ if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ if err = randomize.Struct(seed, &b, tagDBTypes, false, tagColumnsWithDefault...); err != nil {
+ t.Fatal(err)
+ }
+ if err = randomize.Struct(seed, &c, tagDBTypes, false, tagColumnsWithDefault...); err != nil {
+ t.Fatal(err)
+ }
+
+ b.UserID = a.ID
+ c.UserID = a.ID
+
+ if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+ if err = c.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ check, err := a.Tags().All(ctx, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ bFound, cFound := false, false
+ for _, v := range check {
+ if v.UserID == b.UserID {
+ bFound = true
+ }
+ if v.UserID == c.UserID {
+ cFound = true
+ }
+ }
+
+ if !bFound {
+ t.Error("expected to find b")
+ }
+ if !cFound {
+ t.Error("expected to find c")
+ }
+
+ slice := UserSlice{&a}
+ if err = a.L.LoadTags(ctx, tx, false, (*[]*User)(&slice), nil); err != nil {
+ t.Fatal(err)
+ }
+ if got := len(a.R.Tags); got != 2 {
+ t.Error("number of eager loaded records wrong, got:", got)
+ }
+
+ a.R.Tags = nil
+ if err = a.L.LoadTags(ctx, tx, true, &a, nil); err != nil {
+ t.Fatal(err)
+ }
+ if got := len(a.R.Tags); got != 2 {
+ t.Error("number of eager loaded records wrong, got:", got)
+ }
+
+ if t.Failed() {
+ t.Logf("%#v", check)
+ }
+}
+
+func testUserToManyAddOpPosts(t *testing.T) {
+ var err error
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+
+ var a User
+ var b, c, d, e Post
+
+ seed := randomize.NewSeed()
+ if err = randomize.Struct(seed, &a, userDBTypes, false, strmangle.SetComplement(userPrimaryKeyColumns, userColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ foreigners := []*Post{&b, &c, &d, &e}
+ for _, x := range foreigners {
+ if err = randomize.Struct(seed, x, postDBTypes, false, strmangle.SetComplement(postPrimaryKeyColumns, postColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+ if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+ if err = c.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ foreignersSplitByInsertion := [][]*Post{
+ {&b, &c},
+ {&d, &e},
+ }
+
+ for i, x := range foreignersSplitByInsertion {
+ err = a.AddPosts(ctx, tx, i != 0, x...)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ first := x[0]
+ second := x[1]
+
+ if a.ID != first.UserID {
+ t.Error("foreign key was wrong value", a.ID, first.UserID)
+ }
+ if a.ID != second.UserID {
+ t.Error("foreign key was wrong value", a.ID, second.UserID)
+ }
+
+ if first.R.User != &a {
+ t.Error("relationship was not added properly to the foreign slice")
+ }
+ if second.R.User != &a {
+ t.Error("relationship was not added properly to the foreign slice")
+ }
+
+ if a.R.Posts[i*2] != first {
+ t.Error("relationship struct slice not set to correct value")
+ }
+ if a.R.Posts[i*2+1] != second {
+ t.Error("relationship struct slice not set to correct value")
+ }
+
+ count, err := a.Posts().Count(ctx, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if want := int64((i + 1) * 2); count != want {
+ t.Error("want", want, "got", count)
+ }
+ }
+}
+func testUserToManyAddOpTags(t *testing.T) {
+ var err error
+
+ ctx := context.Background()
+ tx := MustTx(boil.BeginTx(ctx, nil))
+ defer func() { _ = tx.Rollback() }()
+
+ var a User
+ var b, c, d, e Tag
+
+ seed := randomize.NewSeed()
+ if err = randomize.Struct(seed, &a, userDBTypes, false, strmangle.SetComplement(userPrimaryKeyColumns, userColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ foreigners := []*Tag{&b, &c, &d, &e}
+ for _, x := range foreigners {
+ if err = randomize.Struct(seed, x, tagDBTypes, false, strmangle.SetComplement(tagPrimaryKeyColumns, tagColumnsWithoutDefault)...); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+ if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+ if err = c.Insert(ctx, tx, boil.Infer()); err != nil {
+ t.Fatal(err)
+ }
+
+ foreignersSplitByInsertion := [][]*Tag{
+ {&b, &c},
+ {&d, &e},
+ }
+
+ for i, x := range foreignersSplitByInsertion {
+ err = a.AddTags(ctx, tx, i != 0, x...)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ first := x[0]
+ second := x[1]
+
+ if a.ID != first.UserID {
+ t.Error("foreign key was wrong value", a.ID, first.UserID)
+ }
+ if a.ID != second.UserID {
+ t.Error("foreign key was wrong value", a.ID, second.UserID)
+ }
+
+ if first.R.User != &a {
+ t.Error("relationship was not added properly to the foreign slice")
+ }
+ if second.R.User != &a {
+ t.Error("relationship was not added properly to the foreign slice")
+ }
+
+ if a.R.Tags[i*2] != first {
+ t.Error("relationship struct slice not set to correct value")
+ }
+ if a.R.Tags[i*2+1] != second {
+ t.Error("relationship struct slice not set to correct value")
+ }
+
+ count, err := a.Tags().Count(ctx, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if want := int64((i + 1) * 2); count != want {
+ t.Error("want", want, "got", count)
+ }
+ }
+}
+
func testUsersReload(t *testing.T) {
t.Parallel()
diff --git a/src/post/routes.go b/src/post/routes.go
index 09457d3..7714920 100644
--- a/src/post/routes.go
+++ b/src/post/routes.go
@@ -4,9 +4,11 @@ import (
"fmt"
"log"
"net/http"
+ "strconv"
"github.com/go-chi/chi/v5"
"github.com/volatiletech/sqlboiler/v4/boil"
+ . "github.com/volatiletech/sqlboiler/v4/queries/qm"
"gitlab.com/alexkavon/newsstand/src/models"
"gitlab.com/alexkavon/newsstand/src/server"
"gitlab.com/alexkavon/newsstand/src/sessions"
@@ -30,7 +32,7 @@ var Routes = server.Routes{
server.Route{
Name: "Get",
Method: "GET",
- Path: "/p/{:id}",
+ Path: "/p/{id}",
HandlerFunc: Get,
},
}
@@ -45,8 +47,8 @@ func Store(s *server.Server) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var post models.Post
post.Title = r.PostFormValue("title")
- post.Url = r.PostFormValue("url")
- post.Description = r.PostFormValue("description")
+ post.Description.SetValid(r.PostFormValue("description"))
+ post.URL.SetValid(r.PostFormValue("url"))
// validate post
// process post, look for spamminess, bad url
@@ -65,6 +67,19 @@ func Store(s *server.Server) http.HandlerFunc {
func Get(s *server.Server) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
- post := models.FindPost(r.Context(), s.Db.ToSqlDb(), chi.URLParam(r, "id"))
+ postId, err := strconv.Atoi(chi.URLParam(r, "id"))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ post, err := models.Posts(
+ Where("id = ?", postId),
+ Load("User"),
+ Load("Tags"),
+ ).One(r.Context(), s.Db.ToSqlDb())
+ if err != nil {
+ log.Fatal(err)
+ }
+ s.Ui.Render(w, r, "post/get", map[string]any{"post": post})
}
}
diff --git a/ui/pages/post/get.tmpl.html b/ui/pages/post/get.tmpl.html
index f705f65..b32bd1c 100644
--- a/ui/pages/post/get.tmpl.html
+++ b/ui/pages/post/get.tmpl.html
@@ -1,19 +1,23 @@
-{{define "title"}}{{.Title}}{{end}}
+{{define "title"}}{{.post.Title}}{{end}}
{{define "main"}}
- <a class="title" href="{{.Url}}" target="_blank">{{.Title}}</a>
- <span class="post-tags">{{.Tags}}</span>
- <span class="post-domain">{{.UrlDomainName}}</span>
- <span class="post-author">by <a href="u/{{.Username}}">{{.Username}}</a></span>
- <span class="post-date">{{.CreatedAt}}</span>
+ <a class="title" href="{{.post.URL.String}}" target="_blank">{{.post.Title}}</a>
+ <span class="post-tags">
+ {{range $tag := .post.R.Tags}}
+ <span class="post-tag-{{$tag.Tag}}">{{$tag.Tag}}</span>
+ {{end}}
+ </span>
+ <span class="post-domain">{{.post.URL.String}} (Pretty URL)</span>
+ <span class="post-author">by <a href="u/{{.post.R.User.ID}}">{{.post.R.User.Username}}</a></span>
+ <span class="post-date">{{.post.CreatedAt.Format "Jan 02, 2006 15:04"}}</span>
<span class="actions">
<span class="action-flag">Flag</span>
<span class="action-hide">Hide</span>
</span>
- {{ if and .Description not .Url }}
- <p>{{.Description}}</p>
+ {{ if .post.Description.Valid }}
+ <p>{{.post.Description.String}}</p>
{{end}}
- <form action="/p/{{.ID}}/comment" method="POST">
+ <form action="/p/{{.post.ID}}/comment" method="POST">
<textarea placeholder="This better be good." name="comment"></textarea>
<button type="submit">Yup</button>
<button class="preview" type="button">Preview</button>