stackunderflow
stackunderflow

Reputation: 1714

Increment struct value in Go

I'm expecting to see the visits increment with every GET request to /foo but it remains as 1. What am I doing wrong here?

package main

import (
    "log"

    "github.com/gofiber/fiber/v2"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
)

// Item model
type Item struct {
    gorm.Model
    UID    int64  `gorm:"primaryKey;autoIncrement"`
    Name   string `gorm:"index;not null"`
    Visits int32  `gorm:"default 0"`
}

func main() {
    db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})

    if err != nil {
        panic(err)
    }

    db.AutoMigrate(&Item{})
    db.Create(&Item{
        Name: "foo",
    })

    app := fiber.New(fiber.Config{})

    app.Get("/:name", func(c *fiber.Ctx) error {
        var i Item
        db.First(&i, "name = ?", c.Params("name"))

        if i.Name == "" {
            return c.Status(fiber.StatusNotFound).JSON(&fiber.Map{
                "message": "Not found",
            })
        }

        db.Model(&i).Update("visits", i.Visits+1)
        return c.JSON(i)
    })

    log.Println("Listening...")
    log.Fatal(app.Listen(":3000"))
}

Upvotes: 0

Views: 1617

Answers (1)

andersryanc
andersryanc

Reputation: 969

If you log the error like:

if err := db.Model(&i).Update("visits", i.Visits+1).Error; err != nil {
    fmt.Printf("update err != nil; %v\n", err)
}

You will see that it says: "WHERE conditions required". So, you can fix this like:

if err := db.Model(&i).Where("name = ?", i.Name).Update("visits", i.Visits+1).Error; err != nil {
    fmt.Printf("update err != nil; %v\n", err)
}

Here is some more details about Error Handling in GORM


EDIT: There is actually a larger issue at play here with your example. The issue is that you are defining UID as part of your Item model which conflicts with what gorm.Model provides. You can see in Declaring Models the following model definition:

// gorm.Model definition
type Model struct {
  ID        uint           `gorm:"primaryKey"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt gorm.DeletedAt `gorm:"index"`
}

which when added to your Item type / model, you would get:

type Item struct {
  // gorm.Model
  ID        uint           `gorm:"primaryKey"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt gorm.DeletedAt `gorm:"index"`
  // your fields
  UID    int64  `gorm:"primaryKey;autoIncrement"`
  Name   string `gorm:"index;not null"`
  Visits int32  `gorm:"default 0"`
}

It seems like this results in your database table being created in a weird state. You might notice in your returned JSON payload that both the ID AND UID are equal to 0. And as you start/stop the server multiple times and look at the additional records that are created (because of your db.Create() at the top) you end up with multiple Items with a name of "foo" all of which having ID and UID of 0... this is why GORM is then unable to update the item without a WHERE clause, since the primary keys are not properly set on the table.

If you remove UID from your model (or maybe even just remove "primaryKey" from it) you can then use the Update() method without the need for the where condition. So, your model should look like:

// Item model
type Item struct {
    gorm.Model
    Name   string `gorm:"index;not null"`
    Visits int32  `gorm:"default 0"`
}

After you make changes to your model / type, make sure to delete your test.db file in order to have the table re-created with the new / correct format.


Lastly, in regards to my original answer, you should also see the errors being logged to your console automatically by GORM without needing to specifically handle them like I suggested.

Upvotes: 1

Related Questions