Golang MySQL Scan only populate results if all fields passed - go

Golang mysql row.Scan(&pointerAddress) not populating fields.
Showing me this when i send request so omitempty does work.
If I fill all User struct fields to row.Scan(&user.Email, etc...) only then show values in result.
Code :
type User struct{
Id int `json:"id"`
Email string `json:"email"`
Password string `json:"password"`
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
RememberToken string `json:"remember_token"`
Phone int64 `json:"phone"`
AgreeTerms int8 `json:"agree_terms"`
AllowPromotionalOffers int8 `json:"allow_promotional_offers"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
LandlordId int64 `json:"landlord_id"`
RenterId int64 `json:"renter_id"`
}
My Login Function :
func Login(w http.ResponseWriter, r *http.Request) {
user := models.User{}
err := json.NewDecoder(r.Body).Decode(&user)
if err != nil {
fmt.Println(err)
}
row, err := database.DB.Query("SELECT * FROM users WHERE email=?", user.Email)
if err != nil {
fmt.Errorf("%w", err)
}
defer row.Close()
for row.Next() {
err = row.Scan(&user.Id, &user.Email, &user.Password)
if err != nil {
fmt.Errorf("%w", err)
}
}
err = json.NewEncoder(w).Encode(user)
if err != nil {
fmt.Errorf("%w", err)
}
}

Try to initialize your struct inside the for statement, if your resultset is a query rows (maybe a slices of structs):
var users []*models.User
for row.Next() {
user := new(models.User)
err = row.Scan(&user.Id, &user.Email, &user.Password)
users = append(users, user)
if err != nil {
fmt.Errorf("%w", err)
}
}
......
You don't need to loop if you're using a QueryRow resultSet
row, err := database.DB.QueryRow("SELECT id, email, password FROM users WHERE email=?",
user.Email).Scan(&user.Id, &user.Email, &user.Password)
As the best practice, you should avoid using "*" if not essential. ;)
It's a little verbose but it works.

Related

How do I send user object as nil (null in json) in Golang?

I am writing some authentication using Golang, and I have the following code that populates the UserResponse if UserID is not nil, but when it IS nil, the sent UserResponse looks like this:
{
"user": {
"id": 0,
"email": ""
}
}
My code is
func (app *Application) init(w http.ResponseWriter, r *http.Request) {
userID := r.Context().Value("userID")
type UserResponse struct {
ID int `json:"id"`
Email string `json:"email"`
}
var user UserResponse
if userID != nil {
query := "select id, email from users where id = ?"
row := app.Db.QueryRow(query, userID)
err := row.Scan(&user.ID, &user.Email)
if err != nil {
fmt.Println(err.Error())
}
fmt.Println(user)
}
app.writeJSON(w, http.StatusOK, user, "user")
}
it should be like this
{
userID := r.Context().Value("userID")
type User struct {
ID int `json:"id"`
Email string `json:"email"`
}
type Response struct {
User *User `json:"user"`
}
var resp Response
if userID != nil {
user = &User{}
query := "select id, email from users where id = ?"
row := app.Db.QueryRow(query, userID)
err := row.Scan(&user.ID, &user.Email)
if err != nil {
WriteJSON(w, http.StatusInternalServerError, resp)
return
}
resp.User = user
}
WriteJSON(w, http.StatusOK, resp)
}
I found a solution. First I created an interface{} and populated it either nil or with a user depending on whether UserID is present. However, I wanted a better cleaner solution and came up with this.
var user *UserResponse
if userID != nil {
user = &UserResponse{}
query := "select id, email from users where id = ?"
row := app.Db.QueryRow(query, userID)
err := row.Scan(&user.ID, &user.Email)
if err != nil {
fmt.Println(err.Error())
}
fmt.Println(user)
}

use same struct for both create and update operations in go lang using gin and gorm

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.

How To Use Embedding To Get Data From A Table

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.

Read flattened entity from cloud datastore in golang

func (db *dataStore) AddAcceptance(ctx context.Context, req *acceptance.PolicyAcceptance) (uint64, error) {
accpKey := datastore.IncompleteKey("Acceptance", nil)
key, err := db.Put(context.Background(), accpKey, req);
if err != nil {
log.Fatalf("Failed to save Acceptance: %v", err)
}
accpKey = key
val := uint64(accpKey.ID)
return val, err
}
type PolicyAcceptance struct {
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
PolicyNumber int64 `protobuf:"varint,2,opt,name=policyNumber" json:"policyNumber,omitempty"`
Version string `protobuf:"bytes,3,opt,name=version" json:"version,omitempty"`
SignerData *SignerData `protobuf:"bytes,4,opt,name=signerData" json:"signerData,omitempty" datastore:",flatten"`
GroupID int64 `protobuf:"varint,5,opt,name=groupID" json:"groupID,omitempty"`
LocationID int64 `protobuf:"varint,6,opt,name=locationID" json:"locationID,omitempty"`
BusinessId int64 `protobuf:"varint,7,opt,name=businessId" json:"businessId,omitempty"`
AcceptedDate *google_protobuf.Timestamp `protobuf:"bytes,8,opt,name=acceptedDate" json:"acceptedDate,omitempty" datastore:",flatten"`
IssuerName string `protobuf:"bytes,9,opt,name=issuerName" json:"issuerName,omitempty"`
Place string `protobuf:"bytes,10,opt,name=place" json:"place,omitempty"`
}
type SignerData struct {
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
Email string `protobuf:"bytes,2,opt,name=email" json:"email,omitempty"`
Type string `protobuf:"bytes,3,opt,name=type" json:"type,omitempty"`
Id int64 `protobuf:"varint,4,opt,name=id" json:"id,omitempty"`
}
datastore:",flatten" saves data as flattened in data store. The property names becomes flattened with . like SignerData.Id as property name but when it's read from data store, how can I map it back to struct? It fails throwing an error like:
SignerData.Id could not be found as a key in struct. Error: No such
struct field.
func (db *dataStore) GetAcceptanceBySignerData(ctx context.Context, req *acceptance.SignerData) (*acceptance.ListOfPolicyAcceptance, error) {
query := datastore.NewQuery("Acceptance").Filter("SignerData.Id =", req.Id)
var accpArr acceptance.ListOfPolicyAcceptance
var err error
it := db.Run(ctx, query)
for {
var accept acceptance.PolicyAcceptance
_, err := it.Next(&accept)
if err == iterator.Done {
break
}
if err != nil {
log.Fatalf("Error fetching : %v", err)
}
accpArr.AcceptanceList = append(accpArr.AcceptanceList, &accept)
}
return &accpArr, err
}

Create one-to-one association in transaction

UserService.go
func (service UserService) Create(model *models.User) (*models.User, error) {
db, err := database.GetConnection()
if err != nil {
return nil, err
}
tx := db.Begin()
if tx.Error != nil {
fmt.Errorf("%v", err)
return nil, err
}
if err := tx.Create(model).Error; err != nil {
fmt.Errorf("%v", err)
tx.Rollback()
return nil, err
}
if err := tx.Save(model).Error; err != nil {
fmt.Errorf("%v", err)
tx.Rollback()
return nil, err
}
if err := service.UserProfileService.Create(tx, model); err != nil {
fmt.Errorf("%v", err)
tx.Rollback()
return nil, err
}
if err := tx.Commit().Error; err != nil {
fmt.Errorf("%v", err)
return nil, err
}
return model, nil
}
UserProfileService.go
func (UserProfileService) Create(tx *gorm.DB, user *models.User) error {
if err := tx.Create(models.UserProfile{User: user}).Error; err != nil {
return err
}
return nil
}
User.go
type User struct {
Id *uuid.UUID `json:"id" gorm:"not null;primary_key;type:uuid;default:uuid_generate_v4();"`
Name *string `json:"name" gorm:"not null;type:varchar(255);"`
CreatedAt time.Time `json:"createdAt" gorm:"type:timestamp;default:now()"`
UpdatedAt time.Time `json:"updatedAt" gorm:"type:timestamp;default:now()"`
DeletedAt *time.Time `json:"deleteAt,omitempty" gorm:"type:timestamp;"`
}
func (User) TableName() string {
return "users"
}
UserProfile.go
type UserProfile struct {
User *User `json:"id" gorm:"not null;primary_key;foreignkey:Id;type:uuid;default:uuid_generate_v4();"`
Id *uuid.UUID `json:"id" gorm:"not null;type:uuid"`
Description string `json:"description" gorm:"not null;type:varchar(255)"`
UpdatedAt time.Time `json:"updatedAt" gorm:"type:timestamp;default:now()"`
}
func (UserProfile) TableName() string {
return "users_profile"
}
So, every User must have just one UserProfile, that is why UserProfile's primary key is also a foreign key (User (id)). But when I try to make a request, I got this error: (using unaddressable value)
Any ideas on how to get this working?
The model that you are inserting in UserProfileService.go is not a pointer, which is why you are getting the unaddressable error.
func (UserProfileService) Create(tx *gorm.DB, user *models.User) error {
if err := tx.Create(*models.UserProfile{User: user}).Error; err != nil {
return err
}
return nil
}
That referencing in the tx.Create call will most likely fix your error. If not please post the full error.

Resources