ozzo-dbx
ozzo-dbx copied to clipboard
Updating Table Overrides null values in struct
Hi.
I am developer transitioning from Node.js to go and it has been smooth. But, I'm finding it hard to update a model without overwriting the role in the db with the null data. Example:
I've a User Struct:
// User represents a user's record
type User struct {
ID int `json:"id" db:"pk,id"`
Firstname string `json:"firstname" db:"firstname"`
Lastname string `json:"lastname" db:"lastname"`
Email string `json:"email" db:"email"`
IsDeleted bool `json:"isDeleted" db:"isDeleted"`
Password string `json:"-" db:"password"`
Country string `json:"country" db:"country"`
State string `json:"state" db:"state"`
FacebookToken *string `json:"fbToken" db:"fbToken"`
TwitterToken *string `json:"twitterToken" db:"twitterToken"`
CreatedAt time.Time `json:"createdAt" db:"createdAt"`
UpdatedAt time.Time `json:"updatedAt" db:"updatedAt"`
}
// Validate helps with validating the user field
func (u User) Validate() error {
return validation.ValidateStruct(&u,
validation.Field(&u.Firstname, validation.Required, validation.Length(0, 120)),
validation.Field(&u.Lastname, validation.Required, validation.Length(0, 120)),
validation.Field(&u.Password, validation.Required, validation.Length(8, 120), is.Alphanumeric),
validation.Field(&u.Email, validation.Required, is.Email),
validation.Field(&u.Country, validation.Required),
validation.Field(&u.State, validation.Required),
)
}
// IsPassValid - Verify if password is valid
func (u User) IsPassValid(pass string) error {
return bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(pass))
}
// GenerateHash - Helper function to help generate password hash. It returns error if hasing fails, and user if successful.
func (u *User) GenerateHash() error {
bytes, err := bcrypt.GenerateFromPassword([]byte(u.Password), 14)
u.Password = string(bytes)
return err
}
// TableName - Utilized by ozzo-dbx for referencing main table name
func (u User) TableName() string {
return "users"
}
So, to create a user, I did:
func userManipulation(db *dbx.DB) {
user := User{
Firstname: randomdata.FirstName(randomdata.Male),
Lastname: randomdata.LastName(),
Email: randomdata.Email(),
Password: "password",
Country: randomdata.Country(randomdata.FullCountry),
State: randomdata.State(randomdata.Large),
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
if err := user.Validate(); err != nil {
panic(err)
}
if err := user.GenerateHash(); err != nil {
panic(err)
}
if err := db.Model(&user).Insert(); err != nil {
// panic(err)
processErr(err)
}
fmt.Println(user)
}
And this gives me:
{
"id": 2,
"firstname": "William",
"lastname": "White",
"email": "[email protected]",
"isDeleted": false,
"country": "Rwanda",
"state": "Missouri",
"fbToken": null,
"twitterToken": null,
"createdAt": "2018-05-01T11:21:18.080069+01:00",
"updatedAt": "2018-05-01T11:21:18.08007+01:00"
}
But, whenever I try to update just a specific field, such as in this case:
func userUpdateManipulation(db *dbx.DB) {
user := User{
ID: 1,
Firstname: randomdata.FirstName(randomdata.Female),
UpdatedAt: time.Now(),
}
if err := db.Model(&user).Update(); err != nil {
panic(err)
}
byte, err := json.Marshal(user)
if err != nil {
panic(err)
}
fmt.Println(string(byte))
}
The data I didn't add are overwritten. How can I update without hardcoding which fields needs to be updated.
{
"id": 2,
"firstname": "Addison",
"lastname": "",
"email": "",
"isDeleted": false,
"country": "",
"state": "",
"fbToken": null,
"twitterToken": null,
"createdAt": "0001-01-01T00:00:00Z",
"updatedAt": "2018-05-01T11:38:52.036203+01:00"
}
This is what I had to do to fix that. If you think there is a better way, please, do let me know:
// ExcludeData - Parameters to exclude while updating
func (u User) ExcludeData() []string {
mainExlude := []string{}
if len(u.Firstname) <= 0 { mainExlude = append(mainExlude, "Firstname") }
if len(u.Lastname) <= 0 { mainExlude = append(mainExlude, "Lastname") }
if len(u.Email) <= 0 { mainExlude = append(mainExlude, "Email") }
if len(u.Password) <= 0 { mainExlude = append(mainExlude, "Password") }
if len(u.Country) <= 0 { mainExlude = append(mainExlude, "Country") }
if len(u.State) <= 0 { mainExlude = append(mainExlude, "Country") }
if u.FacebookToken == nil { mainExlude = append(mainExlude, "FacebookToken") }
if u.TwitterToken == nil { mainExlude = append(mainExlude, "TwitterToken") }
if time.Time.IsZero(u.CreatedAt) { mainExlude = append(mainExlude, "CreatedAt") }
if time.Time.IsZero(u.UpdatedAt) { mainExlude = append(mainExlude, "UpdatedAt") }
mainExlude = append(mainExlude, "IsDeleted")
return mainExlude
}
What would you suggest I do to get this done. Thanks
The update works as a crud operation so it updates all the data available in the object. To update only specific fields you would have to define them as described in https://github.com/go-ozzo/ozzo-dbx/blob/master/README.md#building-data-manipulation-queries . Alternatively load the data into a struct, update the fields there and write it all back using a common update ()