Query with 'has one' association (one-to-one) - go

I'm playing a bit with Gorm while I'm trying to decide which ORM library fit the most for my needs.
Important to mention that I'm currently working with Sqlite.
Following the guide I created two structs:
type Color struct {
gorm.Model
UserID uint
Name string
}
//User struct define a basic user model
type User struct {
gorm.Model
Username string
Email string
FirstName string
LastName string
Password string
CreationDate time.Time
DOB time.Time
IgnoreMe int `gorm:"-"` // Ignore this field
Color Color `gorm:"foreignkey:ColorRefer"`
ColorRefer uint
}
when I'm creating a DB with
func CreateTables() {
user := dm.User{}
color := dm.Color{}
GormDB.CreateTable(&color)
GormDB.CreateTable(&user)
GormDB.Model(&user).AddForeignKey("ColorRefer", "colors(id)", "CASCADE", "CASCADE")
}
or with:
func CreateTables() {
GormDB.AutoMigrate(&dm.User{},&dm.Color{})
}
Sadly it's not working as I would of expect and create the foreign key automatically, but it's works when I do it manually.
My main problem is when I'm trying to query Users
//QueryByStructExample query users table by the struct non-zero (non-default) fields.
func QueryByStructExample(userStruct dm.User) []dm.User {
var results []dm.User
GormDB.Where(userStruct).Find(&results)
return results
}
I created the following function in a try to query users by email with the color property which is my color struct and I tried to play with a lot with the Model,Related and the Association functions, and nothing seems to work and (I'm avoiding to use join by purpose).
The end result is that it query my User but without the color (only with the ID in my colorRefer)
any suggestion?

Do you mean to preload the Color struct? If yes did you try to query it like that
GormDB.Preload('Color').Where(userStruct).Find(&results)

Related

Accessing column in junction table in GORM

I have three tables users, todos and user_todos:
type User struct {
ID uint64
Email string
Todos []Todo `gorm:"many2many:user_todos"`
}
type Todo struct {
ID uint64
Title string
}
type UserTodos struct {
UserID uint64
TodoID uint64
Done bool
}
Now I want to get all users with all todos. db.Preload("Todos").Find(&users) does the trick for that.
But what if I want to get all todos and see if they are already done by the user?
I tried to create a SQL view checked_todos which joins the user_todos table and exchange the Todos attribute in the User struct with a CheckedTodo, which just extends Todo with a boolean field. Preloading never access the right table.
Couldn't find any hints in the GORM documentation how to access the columns in the junction table.
You might be able to achieve what you want with custom preloading. Two options you might want to try:
Option 1
db.Preload("Todos", func(grm *gorm.DB) *gorm.DB {
return grm.Where("user_todos.done = 1")
}).Find(&users)
Option 2
db.Preload("Todos", func(grm *gorm.DB) *gorm.DB {
return grm.Joins("INNER JOIN user_todos ON user_todos.todo_id = todos.id AND user_todos.done = 1")
}).Find(&users)

Error when using preload gorm golang, it's not running the preload table i need

I tried to learn and define a model in gorm golang
But i got this problem... dont know how to find the issue.
Firstly I have created 2 table name User and Schedule in database
this is the model
type User struct {
UserId int `json:",omitempty" gorm:"primaryKey"`
UserName string `json:",omitempty"`
UserPassword string `json:"-"`
UserEmail string `json:",omitempty"`
UserFullName string `json:",omitempty"`
Schedule []*Schedule `gorm:"foreignKey:UserId" json:"working_schedule"`
}
type Schedule struct {
ScheduleId int `gorm:"primaryKey"`
ScheduleDate int
ScheduleStart int
ScheduleEnd int
ScheduleType int
ScheduleCreated int
UserId int
}
And i also do this code in golang for making an query to get all the schedule of specific user by the id i get.
Preload("Schedule").Table("user").Find(&user, "user_id=?", id)
But when showing the result at all the working_schedule is null. And I tried to debug the code above but only found that it just run a select from user... not in schedule
SELECT * FROM `user` WHERE user_id=1
Idk what I was wrong. Please help me. Ty
Since you named your struct Schedule, go-gorm will try to find a table named schedules, because it uses the pluralized snake-case convention. If it doesn't succeed, it will not create a query for it when doing preload.
If your database table is named schedule, you will have to implement the Tabler interface (maybe below the definition of your Schedule struct).
func (Schedule) TableName() string {
return "schedule"
}
You can find more info on this here.

Mapping one type to another

Let's say I have the following types.
type Contract struct {
Id string `json:"id" gorm:"column:uuid"`
Name string `json:"name" gorm:"column:name"`
Description string `json:"descr" gorm:"column:descr"`
ContractTypeId int `json:"contract_type_id" gorm:"column:contract_type_id"`
}
type ContractModel struct {
Id string `json:"id" gorm:"column:uuid"`
Name string `json:"name" gorm:"column:name"`
Description string `json:"descr" gorm:"column:descr"`
}
I have a SQL query using gorm to scan results into a contract object.
How can I map the values from the contract object into contractModel object?
I tried using the package go-automapper as such:
automapper.Map(contract, ContractModel{})
I want to drop the ContractTypeId.
Can I do this for multiple types in a list?
var contractModels []ContractModel
automapper.Map(contracts, &contractModels)
You can do either:
models := []ContractModel{}
automapper.Map(contracts, &models)
Or call automapper.Map in a loop:
models := make([]ContractModel, len(contracts))
for i := range contracts {
automapper.Map(contracts[i], &models[i])
}
You should be aware that automapper uses reflection behind the scenes and thus is much slower than straight forward non-polymorphic copying like #ThinkGoodly suggests. It's a totally fine solution if performance isn't top priority though.

Control over unexported struct fields

So this is my first question in stackoverflow. :)
We have defined a struct in org package like below:
type Employee struct {
FirstName, LastName string
salary int
}
and then in main.go file, we are initializing the struct like below:
func main() {
ross := Employee {
FirstName: "Ross",
LastName: "Geller",
}
fmt.Println(ross)
}
The output will be like below:
{Ross Geller 0}
As salary field is not exported from the Employee struct type, so it's displaying the zero value of int. An end-user will assume that the salary of this employee is 0.
So is there any way to control the unexported fields?
What is the best approach to deal with such a problem in a real-time scenario?
Is this really a problem?
If you're really worried about it, you can override the .String of Employee:
https://play.golang.org/p/PncEOGVP2HP
func (e Employee) String() string {
return fmt.Sprintf("%v", struct{
FirstName string
LastName string
}{e.FirstName, e.LastName})
}
But in reality, are they going to be seeing the output from the console of your program? Most likely this is a non-issue.
Well, If you want to initialize the field you can always write an exported function or method to do that such as
func New(first,last,salary string) Employee{
//...
}
The reason why you can have unexported types is to be able to create something called an opaque type.
You can have methods on your data setters and getters and do complex things without worrying about user braking the internal state of your data.
Imagine you are writing a drawing app and you have a painter Struct which keeps track of cursor and current color and stuff. You really would not want your user to be able to mess with your painter manually. that would break everything.
So the user creates the painter through an initializer and passes it around as a Painter type and using methods such as moveTo,lineTo which updates the state internally.

How to populate and embedded array with gorm?

I have 2 structs with data like this:
type User struct {
Pics Pic[]
}
type Pic struct {
Id int
UserId int64
}
Although everytime I insert an User, Each of the pics are inserted on their table everytime I find the users, pics are not populated:
var users []User
db.Limit(pagesize).Where("updated_at > ?", date).Find(&users)
Am I doing something wrong?
Your models (the structs) don't really make sense because User have a Pic array indicates a 'one to many' user to pics relationship however your user has no id property itself and there for cannot be related to items on the Pic table.
User should have a property Id which will be it's primary key and UserId is a foreign key on Pic that relates to it. Without the 'relation' between these two tables/entities there's no way you're going to return pics by querying users.
I'm not sure what all you need to do to make your code work since the example is incomplete but the first thing you need is an Id property which you should designate as a Primarykey with gorm annotations. You also should have annotations on the Pic struct saying UserId is a foreign key and Id is it's primary key.
Also, just fyi your array is not embedded. Embedding is a language feature which you're not using, if you embed the property it has no name and it's properties can be accessed directly from an instance of the embedding type.
I had these issues once. Then I used Join function.
See my example that works just fine:
type FileType struct {
Id int
}
type File struct {
Id int
FileType `xorm:"extends"`
}
file := File{Id: id}
has, err := eng.
Join("INNER", "FileType", "FileType.IdFileType = File.IdFileType").
Get(&file)
You probably know by now. You got to think as if you are creating a SQL table with 1-to-many relationship. Here is an example:
type Entry struct {
ID int
Name string
...
ContainerID int
}
type Container struct {
ID int
Tag int
Name string
...
Entries []Entry `gorm:"foreignkey:ContainerID"`
}
The trick is to populate it. I am yet to find how to make it in one try. For every such dependency, you got to run something like:
c := getContainerFromDB(...)
if err := getROConn().Model(c).Related(&c.Entries, "Entries").Error; err != nil {
return errors.Wrap(err, "error getting container field")
}
Try Preload
db.Limit(pagesize).Where("updated_at > ?", date).Preload("Pics").Find(&users)

Resources