Gorm Timestamps in Golang - go

type Base struct {
ID uint `gorm:"primary_key" json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *gorm.DeletedAt `sql:"index" json:"deleted_at" swaggertype:"primitive,string"`
CreatedByID uint `gorm:"column:created_by_id" json:"created_by_id"`
UpdatedByID uint `gorm:"column:updated_by_id" json:"updated_by_id"`
}
If I pass some values to created_at and updated_at it is taking up the current time as a default value and not taking up the value that I have passed. Is there any way I can make the gorm take up the values that I have passed.

Three ways
Define default tag for field with a database function to fill in the default. e.g. for MySQL database current_timestamp() can be used
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP()"`
Assign default values as suggested by #Guolei.
Embed gorm.Model in your struct instead, for automatic handling of ID, CreatedAt, UpdatedAt and DeletedAt.

if containing fileds: CreatedAt and UpdatedAt, gorm will use reflect to insert default value when executing.
also you could give the specific value.
info := Example{
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
_, err := DB.Insert(info)

An update to #s3vt's answer:
If you the first method they provided, you need to put the function without the parenthesis like this:
CreatedAt time.Time `gorm:"default:current_timestamp"`
I spent a lot of time trying to figure out the error I was getting so I thought I'd share it for the people who might face this!

Related

GO: import a struct and rename it in json

I have built a database in go with gorm. For this I created a struct and with this struct I created a table. So far so good. In the backend everything works, but in the frontend the problem is that the JSON which is called always returns the ID in upper case and swagger generates me an ID which is lower case. Is there a way in Go that I can overwrite the imported struct from gorm with a JSON identifier?
import "gorm.io/gorm"
type Report struct {
gorm.Model
CreatedBy User `gorm:"foreignKey:CreatedByUserID" json:"createdBy"`
Archived bool `json:"archived"`
}
This Struct gives me the following response
{
"ID": 8,
"CreatedAt": "2022-11-15T20:45:16.83+01:00",
"UpdatedAt": "2022-12-27T21:34:17.871+01:00",
"DeletedAt": null
"createdBy": {
"ID": 1,
"CreatedAt": "2022-11-15T20:02:17.497+01:00",
"UpdatedAt": "2022-11-15T20:02:17.497+01:00",
...
},
"archived": true,
}
Is there a way to make the ID lowercase (like Archived)? Or can I adjust it at swaggo so that it is generated in upper case.
What I have seen is that you can make the table without this gorm.Model and define all the attributes yourself. The problem is that I then have to create all the functionalities (delete, update, index, primary key, ...) of these columns myself.
You can use the mapstructure package.
mapstructure is a Go library for decoding generic map values to structures and vice versa
If you want to embed ID field from gorm.Model with custom json tag, and you want it to be on the same struct and not in a "substruct", you can use mapstructure:",squash" tag on the embedded model.
type Model struct {
ID uint `json:"id"`
}
type Report struct {
Model `mapstructure:",squash"`
Archived bool `json:"archived"`
}
func main() {
input := map[string]interface{}{
"id": 1,
"archived": false,
}
var report Report
if err := mapstructure.Decode(input, &report); err != nil {
panic(err)
}
fmt.Println("Report ID:", report.ID)
fmt.Println("Report ID via Model:", report.Model.ID)
}
As you can observe, with mapstructure.Decode method you can convert map to struct with the squash option, and you can then access ID of report directly. Note that you can still access report.Model and all its fields.
With mapstructure, you can make the ID lowercase as you wanted, and also accessable from the report struct directly, not only from a Model substruct.
I create my own gorm-model-struct:
type GormModel struct {
ID uint `gorm:"primarykey" json:"id"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"deletedAt"`
} //#name models.gormModel
I import this struct into the other structs:
type Report struct {
GormModel
CreatedBy User `gorm:"foreignKey:CreatedByUserID" json:"createdBy"`
Archived bool `json:"archived"`
}
Important is, that you add the json-key and set the attribute name.

GORM updating null field when calling Updates()?

According to GORM's docs:
Updates supports update with struct or map[string]interface{}, when
updating with struct it will only update non-zero fields by default
I have an entry in my database already for the Service with ID, abc123. I am attempting to take an object that looks like the one below:
Service{
ID: "abc123",
Name: "new service name",
CreatedAt: nil,
}
And use it to update the my existing record. But when I call:
tx.Model(&service).Updates(service)
the CreatedAt value in the database is overwritten with nil. How can I update my database record without overwritting the CreatedAt value?
Update: Below is my Service struct
type Service struct {
ID string `gorm:"not null;type:char(32);primary_key;column:id"`
Name string `json:"name" gorm:"size:50;not null;"`
CreatedAt *time.Time
UpdatedAt time.Time
DeletedAt *time.Time `gorm:"index"`
}
I've tried two different variations for my Service struct. the other is with CreatedAt being of type time.Time instead of *time.Time. With *time.Time it will overwrite the value in my DB with a null value. With time.Time it attempts to overwrite the value in the DB with an uninitialized time value and throws the error: Error 1292: Incorrect datetime value: '0000-00-00' for column 'created_at' at row 1
By default, gorm will not update default(zero) or nil values.
If you want to have control over it use something as I described below.
You can use nullable types provided by sql package or create your own.
type Model struct {
Amount *sql.NullFloat64
}
// amount will not be updated
gorm.Updates(Model{Amount: nil})
// amount will be updated as a null
gorm.Updates(Model{Amount: &sql.NullFloat64{}})
// amount will be updated as a 10.50
gorm.Updates(Model{Amount: &sql.NullFloat64{Float64: 10.50, Valid: true}})
A zero value, or a default value, for a time.Time field type inside a struct is time.Time{}. When using Updates, either don't populate the CreatedAt field, or assign time.Time{} value to it.
In the example below, the default or zero value is printed out for CreatedAt field in both cases.
package main
import (
"fmt"
"time"
)
type T struct {
CreatedAt time.Time
C int
S string
}
func main() {
fmt.Println(T{C: 1, S: "one"})
fmt.Println(T{C: 2, S: "two", CreatedAt: time.Time{}})
}
// {0001-01-01 00:00:00 +0000 UTC 1 one}
// {0001-01-01 00:00:00 +0000 UTC 2 two}
EDIT:
Also, I'm not sure how even CreatedAt: nil, compiles if the CreatedAt field is of time.Time type, and not *time.Time.
Since you've updated the Service struct and CreatedAt field type to *time.Time, following should work:
tx.Model(&service).Updates(Service{Name: service.Name}) // add all fields that you want to be updated.
// resulting query
// UPDATE services SET name = 'new service name' WHERE id = 'abc123';
An official GORM example is here
Additionally, you can omit the created_at field like this:
tx.Model(&service).Omit("created_at").Updates(service)
use map
https://gorm.io/docs/update.html#Updates-multiple-columns
tx.Model(&service).Updates(map[string]interface{}{"ID": "abc123", "Name": "new service name", "CreatedAt": nil})

How to set unique at struct Beego

How to set unique at the struct specific columns. first name
type User struct {
ID int64 `orm:"size(100)", pk`
Lastname string `orm:"size(100)"`
Firstname string `orm:"size(100)"`
Role string `orm:"size(100)"`
Created time.Time `orm:"size(100)"`
Updated time.Time `orm:"size(100)"`
}
I'm using "github.com/astaxie/beego/orm"
According to the documentation, you just add the word "unique" to the tag:
Add unique key for one field
Name string `orm:"unique"`
To combine tags, you must use a semicolon as documented here. For example:
Firstname string orm:"unique;size(100)"

Golang imported fields do not act the same as standard field declarations

I'm going to try to simplify the problem down rather than bring the whole project into scope so if you have questions I'll try to update with more information.
I have 3 structs I'm working with:
type Ticket struct{
ID bson.ObjectID `json:"id" bson:"_id"`
InteractionIDs []bson.ObjectId `json:"interactionIds" bson:"interactionIds"`
TicketNumber int `json:"ticketNumber" bson:"ticketNumber"`
Active bool `json:"active" bson:"active"`
// Other fields not included
}
type TicketInteraction struct{
ID bson.ObjectID `json:"id" bson:"_id"`
Active bool `json:"active" bson:"active"`
// Other fields not included
}
type TicketLookupTicket struct{
Ticket
Interactions []TicketInteraction `json:"interactions" bson:"interactions"`
}
Then I have an mgo pipe that I'm working with to 'join' the two collections together
var tickets []TicketLookupTicket
c := mongodb.NewCollectionSession("tickets")
defer c.Close()
pipe := c.Session.Pipe(
[]bson.M{
"$lookup": bson.M{
"from": "ticketInteractions",
"localField": "interactionIds",
"foreignField": "_id",
"as": "interactions",
}
},
)
pipe.All(&tickets)
Now tickets should be populated with the result from the database, but what is actually happening is only the interactions within each ticket has been populated. It ends up looking something like this:
[
{
ID: ObjectIdHex(""),
InteractionIDs: nil,
TicketNumber: 0,
Active: false,
// Other Fields, all with default values
Interactions: []{
{
ID: ObjectIdHex("5a441ffea1c9800148669cc7"),
Active: true,
// Other Fields, with appropriate values
}
}
}
]
Now if I manually declare some of the Ticket structs fields inside the TicketLookupTicket struct, those fields will start populating. Ex:
type TicketLookupTicket struct{
Ticket
ID bson.ObjectId `json:"id" bson:"_id"`
TicketNumber int `json:"ticketNumber" bson:"ticketNumber"`
Active bool `json:"active" bson:"active"`
Interactions []TicketInteraction `json:"interactions" bson:"interactions"`
}
Now ID, TicketNumber, and Active will start populating, but the remaining fields won't.
Can anyone explain why the imported Ticket fields aren't behaving the same as the declared fields?
Per the documentation, you need to add the inline tag to the field:
type TicketLookupTicket struct{
Ticket `bson:",inline"`
Interactions []TicketInteraction `json:"interactions" bson:"interactions"`
}
inline Inline the field, which must be a struct or a map.
Inlined structs are handled as if its fields were part
of the outer struct. An inlined map causes keys that do
not match any other struct field to be inserted in the
map rather than being discarded as usual.
By default, it assumes that the embedded field Ticket should be filled by an object at TicketLookupTicket.Ticket.

Struct embedding with sqlx not returning value from db

I have a struct with a time field that may be nil:
type Order struct {
...
PickupTime *time.Time `json:"-"`
}
I want to save this to DB with sqlx so I figure I need to use pq.NullTime as suggested here.
Instead of updating the Order object (I don't want to leak DB code into the model layer), I figured I could embedd Order in a PgOrder and alter the PickupTime type:
type PgOrder struct {
Order
PickupTime pq.NullTime
}
The problem is that when I simply update an Order in DB, then turn around and fetch that order, the returned PickupTime is empty.
// update
func (pg Postgres) UpdateOrderPickupTime(order *Order, pickupTime time.Time) error {
_, err := pg.Exec(`UPDATE orders SET pickup_time = $1 WHERE id = $2`, pickupTime, order.ID)
return err
}
// retrieve
func (pg Postgres) GetOrder(orderID DatabaseID) (*Order, error) {
pgOrder := PgOrder{}
err := pg.Get(&pgOrder, `SELECT * FROM orders WHERE id = $1`, orderID)
if err == sql.ErrNoRows {
return nil, nil
}
... // at this point pgOrder.PickupTime is 0001-01-01 00:00:00 +0000 UTC
}
If I put a breakpoint betwen updating and retrieving, I can inspect the DB and see that a value is being saved as 2017-04-20 12:05:37-04. So the problem must be in the retrieve portion. If I understand right from the docs, sqlx should be able to handle embedded structs.
It looks like you're shadowing PickupTime. If I'm reading the sqlx docs right that means it will store the value in the first one it found (in Order) and then when you read the one in PgOrder it's an uninitialized time.Time. You can check the Valid field of PgOrder.PickupTime to confirm this (it should be invalid).
If your field is a pointer to something, e.g. *time.Time, or *string you shouldn't need to use the NullXxx types. Those types are to be used when you have a non-nil field, e.g. time.Time, or string while it's corresponding column can be NULL.
If you want to ebmed your type anyway, to avoid potential shadowing already mentioned by #Dmitri Goldring, you can to tell sqlx to skip the field you don't want it to scan the column into. So just as you did with the json tag, you can do so with the db tag:
type Order struct {
...
PickupTime *time.Time `json:"-" db:"-"`
}
type PgOrder struct {
Order
PickupTime pq.NullTime
}

Resources