// user.go
package models
type User struct {
Id int `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
func (User) GetById(c echo.Context, id int) (*User, error) {
db := c.Get("DB").(*sqlx.DB)
user := User{}
err := db.Get(&user, fmt.Sprintf("SELECT id, created_at, updated_at FROM users WHERE id=%d", id))
if err != nil {
fmt.Println(err)
}
return &user, err
}
// main.go
package main
// Success
func fetch_success(c echo.Context) error {
user := models.User{}
user2, err := models.User.GetById(user, c, 5)
}
// Fail: : not enough arguments in call to method expression models.User.GetById
// have (echo.Context, number)
// want (models.User, echo.Context, int)
func fetch_failure(c echo.Context) error {
user, err := models.User.GetById(c, 5)
}
In the above code, argument definition for GetById is c echo.Context, id int. Just two argument is needed. But compiler alert me such as "not enough arguments in call to method expression models.User.GetById"
What's the problem?
You are calling a method GetById but not on an object. When Go calls a method, it supplies the object as a first parameter implicitly. It is similar to passing self reference in Python but syntactically it goes between func keyword and function name.
Rewrite it to be a function:
func GetUserById(c echo.Context, id int) (*User, error) {
db := c.Get("DB").(*sqlx.DB)
user := User{}
err := db.Get(&user, fmt.Sprintf("SELECT id, created_at, updated_at FROM users WHERE id=%d", id))
if err != nil {
fmt.Println(err)
}
return &user, err
}
and then call
user, err := models.GetUserById(c, 5)
Related
I am trying to test user registration with echo but I get an error.
Here is the specific error.
cannot use r.Create(id, name, password, email) (value of type *"gorm.io/gorm".DB) as error value in assignment: *"gorm.io/gorm".DB does not implement error
How can I resolve this error to make a correct test?
package test
import (
"regexp"
"testing"
sqlmock "github.com/DATA-DOG/go-sqlmock"
"github.com/...../model"
"github.com/jinzhu/gorm"
)
func getDBMock() (*gorm.DB, sqlmock.Sqlmock, error) {
db, mock, err := sqlmock.New()
if err != nil {
return nil, nil, err
}
gdb, err := gorm.Open("postgres", db)
if err != nil {
return nil, nil, err
}
return gdb, mock, nil
}
func TestCreate(t *testing.T) {
db, mock, err := getDBMock()
if err != nil {
t.Fatal(err)
}
defer db.Close()
db.LogMode(true)
r := model.Repository{DB: db}
id := "2222"
name := "BBBB"
password := "mfsdmSD34"
email := "dmvsdmvo#.com"
// Mock設定
mock.ExpectQuery(regexp.QuoteMeta(
`INSERT INTO "users" ("id","name","passowrd","email") VALUES ($1,$2,$3,$4)
RETURNING "users"."id"`)).WithArgs(id, name,password,email).
WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(id))
// 実行
err = r.Create(id, name,password,email)
if err != nil {
t.Fatal(err)
}
}
package model
type User struct {
ID int `json:"id" gorm:"praimaly_key"`
Name string `json:"name"`
Password string `json:"password"`
Email string `json:"email"`
}
func CreateUser(user *User) {
db.Create(user)
}
func FindUser(u *User) User {
var user User
db.Where(u).First(&user)
return user
}
package model
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var db *gorm.DB
func init() {
var err Error
dsn := "user:pass#tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
db.AutoMigrate(&User{})
db.AutoMigrate(&Todo{})
}
Disclaimer: go lang noob here
I've my user struct as
type User struct {
ID uint32 `json:"id"`
FirstName string `json:"firstName" binding:"required"`
LastName string `json:"lastName"`
Email string `json:"email" binding:"required,email,uniqueModelValue=users email"`
Active bool `json:"active"`
Password string `json:"password,omitempty" binding:"required,gte=8"`
UserType string `json:"userType" binding:"oneof=admin backoffice principal staff parent student"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
/POST /users handler
func Create(ctx *gin.Context) {
user := models.User{}
//validate
if err := ctx.ShouldBind(&user); err != nil {
response.Error(ctx, err)
return
}
db, _ := database.GetDB()
db.Create(&user)
// removing password from the response
user.Password = ""
response.Success(ctx, user)
}
I want to create an update handler using the same struct, is there any way I can perform using the same struct ?
Mind you, struct has required bindings on many fields firstName,email etc.
while updating, I might not pass these fields
I came up with something like
/PUT /users/ handler
func Update(ctx *gin.Context) {
userID := ctx.Param("userId")
user := models.User{}
db, _ := database.GetDB()
if err := db.First(&user, userID).Error; err != nil {
response.Error(ctx, err)
return
}
updateUser := models.User{}
if err := ctx.BindJSON(&updateUser); err != nil {
response.Error(ctx, err)
}
//fmt.Printf("%v", updateUser)
db.Model(&user).Updates(updateUser)
response.Success(ctx, user)
}
this is failing obviously due to required validations missing, if I try to update, let's say, just the lastName
You could try for this case to bind to the User struct you just pulled out of the database, like so:
func Update(ctx *gin.Context) {
userID := ctx.Param("userId")
user := models.User{}
db, _ := database.GetDB()
if err := db.First(&user, userID).Error; err != nil {
response.Error(ctx, err)
return
}
if err := ctx.BindJSON(&user); err != nil {
response.Error(ctx, err)
}
db.Model(&user).Updates(user)
response.Success(ctx, user)
}
That might work because the old values + the changes (written into the struct by BindJSON) might be able to pass validation.
Generally speaking, this pattern isn't going to help you for long in Go.
Using the same struct for two different purposes: representation of the entity model and representation of the messages in your API that pertain to the model, will end up giving you trouble sooner or later. These tend to be slightly different, as for example you might eventually have fields that are only exposed internally or, as you have encountered, you have validation that doesn't make sense for all use cases.
For this problem what would help you is to create a new struct to represent the update user message:
package messages
type UpdateUser struct {
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
... fields that are updatable with appropriate validation tags
}
func (u *UpdateUser) ToModel() *model.User {
return &model.User{
FirstName: u.FirstName,
LastName: u.LastName,
...
}
}
Then use that as to validate your request model, then turn it into a model.User for the update:
func Update(ctx *gin.Context) {
userID := ctx.Param("userId")
user := models.User{}
db, _ := database.GetDB()
if err := db.First(&user, userID).Error; err != nil {
response.Error(ctx, err)
return
}
updateUser := messages.UpdateUser{}
if err := ctx.BindJSON(&updateUser); err != nil {
response.Error(ctx, err)
}
//fmt.Printf("%v", updateUser)
db.Model(&user).Updates(updateUser.ToModel())
response.Success(ctx, user)
}
If you use an ORM like Gorm in your project, it is recommended to use viewmodel structs for your requests and responses. Because the structure of your database tables are mostly different then your rest api models. Data binding and validation are easier with viewmodel structs.
A Go noob's learning to use sqlc here. I'm trying to create a method that creates a user.
How do I return nil for a struct?
type user struct{}
func (u *user) Create(ctx context.Context, db *sql.DB) (pg.User, error) {
q := pg.New(db)
user, err := q.CreateUser(ctx, "Someone")
if err != nil {
return nil, err
// ^
// [compiler] [E] cannot convert nil (untyped nil value) to pg.User
}
return user, nil
}
This is the pg.User struct:
type User struct {
ID int64 `json:"id"`
// can not be empty
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Currently, I'm returning pointer instead, which gives no error:
func (u *user) Create(ctx context.Context, db *sql.DB) (*pg.User, error) {
q := pg.New(db)
var usr *pg.User
user, err := q.CreateUser(ctx, "Someone")
if err != nil {
return nil, err
}
fmt.Println(">> user:", user)
// >> user: {1 Someone 2020-09-14 18:36:05.94079 +0000 UTC 2020-09-14 18:36:05.94079 +0000 UTC}
usr = &user
fmt.Println(">> usr:", usr)
// >> usr: &{1 Someone 2020-09-14 18:36:05.94079 +0000 UTC 2020-09-14 18:36:05.94079 +0000 UTC}
return usr, nil
}
One more question though, which one is better, returning pointer or not?
Using a pointer return type is perfectly fine.
If you would rather return a non-pointer struct, then simply return a zero value of the struct:
return pg.User{}, err
or change your function signature to include return named variables (note: the use of = not := as the variables are already considered declared):
func (u *user) Create(ctx context.Context, db *sql.DB) (u pg.User, err error) {
q := pg.New(db)
u, err = q.CreateUser(ctx, "Someone")
return // will implicitly return u & err for you
}
and since the if err logic has been removed, the above can be reduced to a one-liner:
func (u *user) Create(ctx context.Context, db *sql.DB) (pg.User, error) {
return pg.New(db).CreateUser(ctx, "Someone")
}
How can I nest methods in Go?
Let's say, I have 2 files, each in a different package.
First file: handlers/user.go
type Resolver struct {
client *elastic.Client
index string
}
func (r *Resolver) CreateUser(ctx context.Context, name string) (*model.User, error) {
u, err := services.CreateUserService(ctx, r.client, r.index, name)
if err != nil {
return nil, err
}
return u, nil
}
and another file: services/user.go
func CreateUserService(ctx context.Context, client *elastic.Client, index string, name string) (*model.User, error) {
u := &model.User{
Name: name
}
//doing some other function call
err := dao.CreateUserDAO(ctx, client, index, s)
if err != nil {
return nil, err
}
return u, nil
}
This works fine. But I am still having to pass 2 parameters as r.client and r.index.
What I want to do, is make a call like
services.r.CreateUserService(ctx, name).
This makes my code more readable and less cluttered.
But I am not sure, how to change the services package to accommodate this change.
How can I do that?
If I understand correctly, try to create such a method.
func (r *Resolver) CreateUser(ctx context.Context, name string) (*model.User, error) {
u, err := r.CreateUserService(ctx, name)
if err != nil {
return nil, err
}
return u, nil
}
func (r *Resolver) CreateUserService(ctx context.Context, name string) (*model.User, error) {
return services.CreateUserService(ctx, r.client, r.index, name)
}
But I have no idea how to call like services.r.CreateUserService(ctx, name) unless services is a class which includes a Resolver, that's so weird.
I am a PHP programmer and I have just learned Golang for weeks. I am writing REST API to get post information from my 'posts' table. I have the FindPostByID method in posts_controller. This controller uses Post struct which is model
func (p *Post) FindPostByID(db *gorm.DB, pid uint64) (*Post, error) {
var err error
err = db.Debug().Model(&Post{}).Where("id = ?", pid).Take(&p).Error
if err != nil {
return &Post{}, err
}
if p.ID != 0 {
err = db.Debug().Model(&User{}).Where("id = ?", p.AuthorID).Take(&p.Author).Error
if err != nil {
return &Post{}, err
}
}
return p, nil
}
But now I want to add method FindByID into parent struct, such as ParentModel because when I have articles_controller I can use FindByID method in parent struct to get an article info. So I have the following code in parent struct
type ParentModel struct {
}
func (m *ParentModel) FindByID(db *gorm.DB, uid uint64) (*ParentModel, error) {
var err error
err = db.Debug().Model(ParentModel{}).Where("id = ?", uid).Take(&m).Error
if err != nil {
return &ParentModel{}, err
}
if gorm.IsRecordNotFoundError(err) {
return &ParentModel{}, errors.New("User Not Found")
}
return m, err
}
And I change in the Post struct like following:
type Post struct {
ParentModel
ID uint64 `gorm:"primary_key;auto_increment" json:"id"`
Title string `gorm:"size:255;not null;unique" json:"title"`
Content string `gorm:"size:255;not null;" json:"content"`
Author User `json:"author"`
AuthorID uint32 `sql:"type:int REFERENCES users(id)" json:"author_id"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
}
I change FindPostByID method in posts_controller as well:
func (server *Server) GetPost(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
pid, err := strconv.ParseUint(vars["id"], 10, 64)
if err != nil {
responses.ERROR(w, http.StatusBadRequest, err)
return
}
post := models.Post{}
postReceived, err := post.FindByID(server.DB, pid)
if err != nil {
responses.ERROR(w, http.StatusInternalServerError, err)
return
}
responses.JSON(w, http.StatusOK, postReceived)
}
When I run my program, have the err: Table 'golang_rest_api.parent_models' doesn't exist.
How I can do to use inheritance method like PHP language
The error Table 'golang_rest_api.parent_models' doesn't exist means that no table named parent_models exists in your database.
This is because gorm by default uses the pluralised camelcase name of your struct, which you supplied in gorm's Model() method, as its table's name.
Gorm provides in-built support for many kinds of associations which you can find here. I would also like to ask you to complete your ParentModel struct's definition. It's empty right now.