Retrieve deleted rows on deletion - go

Is there a way to retrieve the rows deleted when calling Delete()?
I'd like to avoid using 'SELECT ... FOR UPDATE' to first get the list of rows I'm deleting.
type MyModel struct {
gorm.Model
....
}
res := db.Where("updated_at < ?", expirationDate).
Set("gorm:save_associations", false).
Delete(&MyModel{})
I noticed there is a res.Value attribute but it seems to be the empty struct I pass as argument of Delete().

Your query should be this way instead. db.Where does not return the struct. It modifies the pointer passed as parameter.
var res MyModel{}
db.Where("updated_at < ?", expirationDate).
Delete(&res)

Related

GORM unable to query all records using .Find()

I am trying to write a function to query all results that match a set of conditions and save them in a struct slice.
// Queries the database for the given set of fields and some string conditions specified as a map
func QueryAllRecords(db *gorm.DB, outputObject interface{}, conditions map[string]interface{}) {
result := db.Where(conditions).Find(&outputObject)
if result.Error != nil {
panic(result.Error)
}
log.Println(Utils.CreateLogMessage("Queried all records", outputObject))
}
According to the GORM docs (https://gorm.io/docs/query.html#Retrieving-all-objects), I can query all records using the .Find() function and then specify the struct where the output of the query will be saved.
This is where I make my function call to QueryAllRecords:
var outputObject []Models.Product
conditions := map[string]interface{}{"name": "Sample Product"}
DB.QueryAllRecords(db, outputObject, conditions)
fmt.Println(outputObject)
When I try to print outputObject, I get an empty an empty slice []. It seems like the .Find(&outputObject) is not saving the result in the slice like I want it to. I can successfully print outputObject within the function itself, but not after it has returned.
Use the DB.QueryAllRecords(db, outputObject, conditions) instead

Reflection to get field tag

In Go is there a good way to use reflection to get a field tag by just wrapping a field in a function from the reflect library?
I am basically trying to create a thin data access object that allows be to get the column name in the db without hard coding it all over the place.
Below is the struct with db column names as tags.
// Table Structures
type CusipTableRow struct {
Id int64 `db:"id"`
Cusip string `db:"cusip"`
Symbol string `db:"symbol"`
Active int8 `db:"active"`
Added_Time int32 `db:"added_timestamp"`
Description string `db:"description"`
Exchange string `db:"exchange"`
AssetType string `db:"asset_type"`
}
I am seeking a suggestion other than downloading another library on how to use reflection to essentially make a call like this to return a string with the tag value.
var row CusipTableRow
row.GetColumnName(row.Id) //Return column name based on tag.
I was considering possibly trying to use a map[address] fieldTag, but did not have luck getting that to work perhaps due to not fully having a grasp of the unsafe package. If that approach would have worked I was thinking a call like this could have worked:
row.GetColumnName(&row.Id) //Return column name based on tag.
You can get field tag given the address of the struct and the address of the field. Unsafe shenanigans are not required.
func GetColumnName(pstruct interface{}, pfield interface{}) string {
v := reflect.ValueOf(pstruct).Elem()
for i := 0; i < v.NumField(); i++ {
if v.Field(i).Addr().Interface() == pfield {
return v.Type().Field(i).Tag.Get("db")
}
}
panic("field not in struct")
}
Example use:
var v CusipTableRow
fmt.Println(GetColumnName(&v, &v.Added_Time)) // prints added_timestamp
Run it on the Go Playground.

How to fetch last record in gorm?

I'm working on a golang application in which I need to fetch last record from the table so I'm not able to do it. I have models mention below:-
type SQLTransaction struct {
Id int `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Version uint64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty" gorm:"primaryKey"`
Hash string `protobuf:"bytes,3,opt,name=hash,proto3" json:"hash,omitempty"`
VmStatusId int `protobuf:"bytes,6,opt,name=vm_status_id,proto3" json:"vm_status_id,omitempty"`
}
Here is my gorm function in which I want to fetch the last record
func (mgr *manager) GetLastTxn() (int, error) {
var versionId int
resp := mgr.connection.Table("transactions").Last("", "version")
return versionId, resp.Error
}
Error:-
model value required
[0.053ms] [rows:0] SELECT * FROM `transactions` WHERE version ORDER BY `transactions`. DESC LIMIT 1
0 model value required
How can I achieve it please any help. Thanks in advance.
There are a couple of issues in your example that should be handled.
First, the error model value required suggests that the Last method needs a model to store the result. If you want the entire record with all the data you can do it like this:
var lastVersion SQLTransaction
resp := mgr.connection.Table("transactions").Last(&lastVersion)
If you only want the ID of the record, you do something like this:
var lastVersion struct {
ID int
}
resp := mgr.connection.Table("transactions").Last(&lastVersion)
Next, you don't need Last("", "version") because it doesn't do what you think it does. The first parameter is a pointer to your resulting object (which for all go-gorm methods always should be an object or a slice). The second one is a condition for the WHERE clause. So the correct syntax, if you don't need an additional WHERE clause, is:
resp := mgr.connection.Table("transactions").Last(&lastVersion)
If you look at the code of the Last method, it considers the primary key when it executes the method, so you don't even need any additional parameters or conditions.

How to display all records using struct

I am trying to fetch all data from a table. It returns all data but it is displaying only the last record. I am using GORM and GIN with Golang.
I tried to create a struct and pass that struct to the Find method.
type MpCountry struct{
id uint
Iso string
Name string
Nicename string
Iso3 string
Numcode uint
phonecode uint
}
Code:
countries:= DbModel.MpCountry{}
DbModel.DB.Debug().Find(&countries)
fmt.Println(countries)
fmt.Println("test")
log.Printf("%+v", countries)
return &countries
Output
SELECT * FROM `mp_countries`
[239 rows affected or returned ]
{id:0 Iso:ZW Name:ZIMBABWE Nicename:Zimbabwe Iso3:ZWE Numcode:716 phonecode:0}
You are only passing in a struct, not a slice of structs. It is therefore replacing the values in the struct, until it reaches the last record.
Instead, pass in a pointer to a slice:
countries := &[]DbModel.MpCountry{}
DbModel.DB.Debug().Find(countries)

Golang variable struct field

This may require reflection but I'm not sure. I am trying to loop through an array of required fields in a struct. If any of those fields are nil I want to throw an error essentially. I've got the basic form down but I realized I don't know how in Go to pass a struct field name via a variabel
imagine you have a struct called EmailTemplate and it has a field called template_id
In this case I want to know if EmailTemplate.TemplateId is nil
emailDef.Fields is a string array ["TemplateId"]
I want to check if those fields are in the EmailTemplate struct and if they are nil
for field := range emailDef.Fields {
fmt.Println(emailDef.Fields[field])
if EmailTemplate.[emailDef.Fields[field]] == nil {
missingField := true
}
}
is along the lines of what I am thinking but I know that is wrong as a struct isn't an array. emailDef.Fields[field] would be equivalent to TemplateId
Your loop stuff doesn't make a whole lot of sense to me so I'll give a general example with a single field in a string called field. If you have a slice or array of fields you want to check for then you'll want to range over that using the current value for field.
import "reflect"
st := reflect.TypeOf(EmailTemplate)
v, ok := st.FieldByName(field)
if ok {
// field existed on EmailTemplate, now check if it's nil
if v.IsNil() {
// the field field on instance EmailTemplate was nil, do something
}
}
Now assuming you have a list of fields you need to check are all non-nil, then just add a loop like so;
for field := range requiredFields {
st := reflect.TypeOf(EmailTemplate)
v, ok := st.FieldByName(field)
if ok {
// field existed on EmailTemplate, now check if it's nil
if v.IsNil() {
// the field field on instance EmailTemplate was nil, do something
// maybe raise error since the field was nil
} else {
//the field wasn't found at all, probably time to raise an error
}
}
}
reflect package docs are here; https://golang.org/pkg/reflect/

Resources