How to pass dynamic table name in gorm model - mysql

I am using Gorm ORM for my current application. I have one model correspondents to many tables with identical table structures(i.e. column name and type). So my requirement how can I change the table name dynamically while doing the query.
For e.g.
I have product model like Product.go
type Product struct{
ID int
Name strig
Quantity int
}
And we have different products like shirts, jeans and so on and we have same tables like shirts, jeans.
Now I wanted to query the product as per name of the product how can we do that also want to have table created through migrations. But there is only One model than how can we run use Automigrate feature with Gorm.

Updated for GORM v2
DEPRECATED: TableName will not allow dynamic table name anymore, its result will be cached for future uses.
There is a much easier way to create several tables using the same struct:
// Create table `shirts` & `jeans` with the same fields as in struct Product
db.Table("shirts").AutoMigrate(&Product{})
db.Table("jeans").AutoMigrate(&Product{})
// Query data from those tables
var shirts []Product
var jeans []Product
db.Table("shirts").Find(&shirts)
db.Table("jeans").Where("quantity > 0").Find(&shirts)
But, now on the second thought, I would suggest using the embedded struct so that you won't have to call Table in every query and you can also have additional fields per model while still sharing the same table structure.
type ProductBase struct {
ID int
Name strig
Quantity int
}
type Shirt struct {
ProductBase
NeckType string
}
type Jean struct {
ProductBase
Ripped bool
}
db.AutoMigrate(&Shirt{}, &Jean{})
shirt, jeans := Shirt{}, make([]Jean, 0)
db.Where("neck_type = ?", "Mandarin Collar").Last(&shirt)
db.Where("ripped").Find(&jeans)
Old answer for GORM v1
You're almost there by using table field inside the struct:
type Product struct{
ID int
Name strig
Quantity int
// private field, ignored from gorm
table string `gorm:"-"`
}
func (p Product) TableName() string {
// double check here, make sure the table does exist!!
if p.table != "" {
return p.table
}
return "products" // default table name
}
// for the AutoMigrate
db.AutoMigrate(&Product{table: "jeans"}, &Product{table: "skirts"}, &Product{})
// to do the query
prod := Product{table: "jeans"}
db.Where("quantity > 0").First(&prod)
Unfortunately, that does not work with db.Find() when you need to query for multiple records... The workaround is to specify your table before doing the query
prods := []*Product{}
db.Table("jeans").Where("quantity > 0").Find(&prods)

Related

Custom fields in Many2Many JoinTable

I have this model with a custom JoinTable:
type Person struct {
ID int
Name string
Addresses []Address `gorm:"many2many:person_addresses;"`
}
type Address struct {
ID uint
Name string
}
type PersonAddress struct {
PersonID int
AddressID int
Home bool
CreatedAt time.Time
DeletedAt gorm.DeletedAt
}
How is it possible to assign a value to the Home field when creating a new Person?
Method 1
From what I can see in the docs, here's a clean way you might currently do this:
DB.SetupJoinTable(&Person{}, "Addresses", &PersonAddress{})
addr1 := Address{Name: "addr1"}
DB.Create(&addr1)
addr2 := Address{Name: "addr2"}
DB.Create(&addr2)
person := Person{Name: "jinzhu"}
DB.Create(&person)
// Add an association with default values (i.e. Home = false)
DB.Model(&person).Association("Addresses").Append(&addr1)
// Add an association with custom values
DB.Create(&PersonAddress{
PersonID: person.ID,
AddressID: addr2.ID,
Home: true,
})
Here we're using the actual join table model to insert a row with the values we want.
We can also filter queries for the association:
addr := Address{}
// Query association with filters on join table
DB.Where("person_addresses.home = true").
Model(&person).
Association("Addresses").
Find(&addr)
Method 2
Here's a more magical way, by (ab)using the Context to pass values to a BeforeSave hook, in addition to the SetupJoinTable code from above:
func (pa *PersonAddress) BeforeSave(tx *gorm.DB) error {
home, ok := tx.Statement.Context.Value("home").(bool)
if ok {
pa.Home = home
}
return nil
}
// ...
DB.WithContext(context.WithValue(context.Background(), "home", true)).
Model(&person).
Association("Addresses").
Append(&addr2)
This method feels icky to me, but it works.
As you can find this point in the official documents of the GROM, you can implement some methods for each table(struct).
You can implement BeforeCreate() and/or AfterCreate() methods for your join table, gorm will check that method on time!
You can do anything inside those methods to achieve your goal.
here you will find the full documentation.
enjoy ;)

algorithm verifying data from user beween two tables then insert into another table

Greeting I need to get details from users, in those details the user has I have to validate all the User details validate this details with another table and if the date doesn’t match insert on the table but if it does match then don insert anything, this has to be done for all the users, the domains.
User{
String orderNumber
String dealer
Int UserKm
String dateUser
String adviser
Vehicle vehicle
String dateCreated
Date appointmentDate //this date has to be validated with DateNext
appointmentDate from Appointments domain of it doesn’t exit then you can
insert on that table.
}
Appointments{
User user
Date managementDate
Date lasDataApointies
DateNext appointmentDate
Date NextdAteAppointment
Date callDate
String observations
}
def result = User.executeQuery("""select new map(
mmt.id as id, mmt.orderNumber as orderNumber, mmt.dealer.dealer as
dealer, mmt.UserKm as UserKm, mmt.dateUser as dateUser, mmt.adviser as
adviser, mmt.technician as technician, mmt.vehicle.placa as vehicle,
mmt.dateCreated as dateCreated, mmt.currenKm as currenKm) from User as
mmt """)
def result1=result.groupBy{it.vehicle}
List detailsReslt=[]
result1?.each { SlasDataApointing placa, listing ->
def firsT = listing.first()
int firstKM = firsT.UserKm
def lasT = listing.last()
def lasDataApoint = lasT.id
int lastKM = lasT.UserKm
int NextAppointmentKM = lastKM + 5000
int dayBetweenLastAndNext = lastKM - NextAppointmentKM
def tiDur = getDifference(firsT.dateUser,lasT.dateUser)
int dayToInt = tiDur.days
int restar = firstKM - lastKM
int kmPerDay = restar.div(dayToInt)
int nextMaintenaceDays = dayBetweenLastAndNext.div(kmPerDay)
def nextAppointment = lasT.dateUser + nextMaintenaceDays
detailsReslt<<[placa:placa, nextAppointment:
nextAppointment, manageId:lasDataApoint, nextKmUser: NextAppointmentKM]
}
detailsReslt?.each {
Appointments addUserData = new Appointments()
addUserData.User = User.findById(it.manageId)
addUserData.managementDate = null
addUserData.NextdAteAppointment = null
addUserData.observations = null
addUserData.callDate = it.nextAppointment
addUserData.save(flush: true)
}
println "we now have ${detailsReslt}"
}
Based on the details that are not full and looking at the code I can suggest:
no need to do a query to map you can simply query the list of users and check all the properties like user.vehicle. in any case, you need to check each row.
the groupBy{it.vehicle} is not clear but if needed you can do it using createCriteria projections "groupProperty"
Create 2 service method one for iterating all users and one for each user:
validateAppointment(User user){
/* your validation logic */
....
if (validation term){
Appointments addUserData = new Appointments()
...
}
}
validateAppointments(){
List users = User. list()
users.each{User user
validateAppointment(user)
}
}
you can trigger the validateAppointments service from anywhere in the code or create a scheduled job so it will run automatically based on your needs.
if your list of user is big and also for efficiency you can do bulk update - take a look at my post about it: https://medium.com/meni-lubetkin/grails-bulk-updates-4d749f24cba1
I would suggest to create a Custom Validator using a Service, something like this:
class User{
def appointmentService
...
Date appointmentDate
static constraints = {
appointmentDate validator: { val, obj ->
obj.appointmentService.isDateAppointmentValid(obj.appointmentDate)
}
}
}
But keep in mind that validation may run more often than you think. It is triggered by the validate() and save() methods as you’d expect (as explained in the user guide (v3.1.15)). So I'm not sure if this scenario is the best way to validate àppointmentDate` in your domain, so you have to be careful about that.
Hope this help.

golang -> gorm: How I can use sql.NullInt64 to be int(10) in mysql?

type Contact struct {
gorm.Model
PersonID sql.NullInt64
}
type Person struct {
gorm.Model
}
I am trying to use gorm with mysql in the previuos code but I have the following problem:
I want:
Use sql.NullInt64 to work easily with null values.
Use the base model definition gorm.Model, including fields ID, CreatedAt, UpdatedAt, DeletedAt.
Add a constraint Db.Model(&models.Contact{}).AddForeignKey.
My problem:
Person.ID become "int(10)" in mysql.
Contact.PersonID become "bigint(20)"
MySql need the same type for pk and fk.
Some body can help me to solve this?
The "magic" on gorm.Model is only the name of the fields, any struct with these fields look like this according to the gorm documentation, at the end of Conventions
For example: Save records having UpdatedAt field will set it to current time.
Or
Delete records having DeletedAt field, it won't be deleted from database, but only set field DeletedAt's value to current time, and the record is not findable when querying, refer Soft Delete
So solve the issue is very easy, this is the code for my case:
package models
import "time"
type Model struct {
ID uint `gorm:"primary_key;type:bigint(20) not null auto_increment"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time `sql:"index"`
}
So, now I only need use it as base model :)

One to many relationship with gorm in golang doesnt work

I have two tables:
type Person struct {
ID int
FirstName string
LastName string
Functions []Function
}
type Function struct {
gorm.Model
Info string
Person Person
}
I create the tables like this:
db.AutoMigrate(&models.Person{}, &models.Function{})
I then initialize the database:
user := models.Person{
FirstName: "Isa",
LastName: "istcool",
Functions: []models.Function{{Info: "Trainer"}, {Info: "CEO"}},
}
db.Create(&user)
Now the problem is that my Person table only got Firstname and Lastname columns and my Function table only got the Info column.
But when I start my GET request I get people with the column function which is always null.
Here is a screenshot from my GET request and my db
To see the code visit my GitHub repo
Finally found the answer!!
The problem is my GET functions I have to use
db.Preload("Functions").Find(&[]models.Person{})
instead of
db.Find(&[]models.Person{})

Create table using Go-Gorp fails to set column details

Trying to create the table using Gorp-Go ORM package. Was able to successfully create the table in MySql but failed to attach column details.
type Data struct {
id int `db:"pid"`
name string `db:",size:50"`
}
Gorp hook
Dbm.AddTableWithName(Data{}, "data_test").SetKeys(true, "id")
Dbm.CreateTablesIfNotExists()
Dbm is pointer to gorp.DbMap. The resultant table has pid and ,size:50 has name. Have tried with
type Data struct {
id int `db:"pid"`
name string `db:"name:xyz,size:50"`
}
Still the resultant column name is "name:xyz,size:50"
According to this comment, the size feature is still available in only dev branch.
You can achieve this by explicitly setting maxsize though. Example:
dt := Dbm.AddTableWithName(Data{}, "data_test").SetKeys(true, "id")
dt.ColMap("xyz").SetMaxSize(50)
Dbm.CreateTablesIfNotExists()
....
I believe the column name doesn't require "name"
Try db:"xyz,size:50"