So I have this struct:
type ShippingDoc struct {
gorm.Model
ID string `gorm:"primarykey; size:40;"`
ValidUntil string `json:"valid_until"`
QRCodeVal string `json:"qr_code_val"`
Destination User `gorm:"foreignKey:DestinationID; constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"destination"`
DestinationID string `gorm:"size:40; index; not null" json:"destination_id"`
IncludeVHCs []VHCApplication `gorm:"many2many:shipping_doc_included_vhcs;" json:"included_vhcs"`
IssuanceDate string `gorm:"column:issuance_date"`
ControlNumber string `json:"control_number"`
SharedToHauler User `gorm:"foreignKey:SharedToHaulerID; constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"shared_to_hauler"`
SharedToHaulerID string `gorm:"size:40; index; not null" json:"shared_to_hauler_id"`
Remarks string `json:"remarks"`
IssuedByProvetRep User `gorm:"foreignKey:IssuedByProvetRepID; constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"issued_by_provet_rep"`
IssuedByProvetRepID string `gorm:"size:40; index; not null" json:"issued_by_provet_rep_id"`
IsRequestedByGrower bool `json:"is_requested_by_grower"`
IsRequestedByHauler bool `json:"is_requested_by_hauler"`
Vehicle User `gorm:"foreignKey:VehicleID; constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"vehicle"`
VehicleID string `gorm:"size:40; index; not null" json:"vehicle_id"`
ShipmentDate string `json:"shipment_date"`
LivestockDetails []ShippingDocLivestockRecord `gorm:"many2many:shipping_doc_livestock_details;" json:"livestock_details"`
}
As you can see, its got 2 M2M fields. IncludeVHCs and LivestockDetails. VHCApplication looks like this:
type VHCApplication struct {
gorm.Model
ID string `gorm:"primarykey; size:40;"`
Grower User `gorm:"foreignKey:GrowerID; constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"grower"`
GrowerID string `gorm:"size:40; index; not null" json:"grower_id"`
PermitType1ID string `json:"permit_type_1_id"`
PermitType2ID string `json:"permit_type_2_id"`
PermitType3ID string `json:"permit_type_3_id"`
PermitType4ID string `json:"permit_type_4_id"`
PermitType5ID string `json:"permit_type_5_id"`
PermitType6ID string `json:"permit_type_6_id"`
PermitType7ID string `json:"permit_type_7_id"`
PermitType8ID string `json:"permit_type_8_id"`
PermitType9ID string `json:"permit_type_9_id"`
PermitType10ID string `json:"permit_type_10_id"`
PermitType11ID string `json:"permit_type_11_id"`
PermitType12ID string `json:"permit_type_12_id"`
ApplicationDate string `json:"column:application_date"`
ApprovedByMunicipalRep User `gorm:"foreignKey:ApprovedByMunicipalRepID; constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"approved_by_municipal_rep"`
ApprovedByMunicipalRepID string `gorm:"size:40; index;" json:"approved_by_municipal_rep_id"`
ApprovedByProvetRep User `gorm:"foreignKey:ApprovedByProvetRepID; constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"approved_by_provet_rep"`
ApprovedByProvetRepID string `gorm:"size:40; index;" json:"approved_by_provet_rep_id"`
CheckedByVet User `gorm:"foreignKey:CheckedByVetID; constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"checked_by_vet"`
CheckedByVetID string `gorm:"size:40; index;" json:"checked_by_vet_id"`
HasPaidMunicipalFee bool `json:"has_paid_municipal_fee"`
HasPaidProvetFee bool `json:"has_paid_provet_fee"`
IssuanceDate string `json:"issuance_date"`
ValidUntil string `json:"valid_until"`
ControlNumber string `json:"control_number"`
Remarks string `json:"remarks"`
IssuedToHaulerOnDateTime string `json:"issued_to_hauler_on_datetime"`
IssuedToHauler User `gorm:"foreignKey:IssuedToHaulerID; constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"issued_to_hauler"`
IssuedToHaulerID string `gorm:"size:40; index;" json:"issued_to_hauler_id"`
IssuedToAgentOnDateTime string `json:"issued_to_agent_on_datetime"`
IssuedToAgent User `gorm:"foreignKey:IssuedToAgentID; constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"issued_to_agent"`
IssuedToAgentID string `gorm:"size:40; index;" json:"issued_to_agent_id"`
QRCodeVal string `json:"qr_code_val"`
AIC AICApplication `gorm:"foreignKey:AICID; constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"aic"`
AICID string `gorm:"size:40; index;" json:"aic_id"`
}
And ShippingDocLiveRecord looks like this:
type LivestockSpecies int
const (
Chicken LivestockSpecies = iota + 1
Goat
Cattle
Carabao
Horse
Swine
)
type Sex int
const (
Male Sex = iota + 1
Female
)
type ShippingDocLivestockRecord struct {
gorm.Model
ID string `gorm:"primarykey; size:40;"`
LivestockSpecies LivestockSpecies `json:"livestock_species"`
HeadCount int32 `json:"head_count"`
Sex Sex `json:"sex"`
Remarks string `json:"remarks"`
}
//Sex parser
var (
Sex_name = map[uint8]string{
0: "Male",
1: "Female",
}
Sex_value = map[string]uint8{
"male": 0,
"female": 1,
}
)
func (s Sex) String() string {
return Sex_name[uint8(s)]
}
func ParseSex(sex string) (Sex, error) {
sex = strings.TrimSpace(strings.ToLower(sex))
value, ok := Sex_value[sex]
if !ok {
return Sex(0), fmt.Errorf("%q is not a valid Sex", sex)
}
return Sex(value), nil
}
func (s Sex) MarshalJSON() ([]byte, error) {
return json.Marshal(s.String())
}
func (s *Sex) UnmarshalJSON(data []byte) (err error) {
var sex string
if err := json.Unmarshal(data, &sex); err != nil {
return err
}
if *s, err = ParseSex(sex); err != nil {
return err
}
return nil
}
//end Sex parser
//Livestock parser
var (
LivestockSpecies_name = map[uint8]string{
0: "Chicken",
1: "Goat",
2: "Cattle",
3: "Carabao",
4: "Horse",
5: "Swine",
}
LivestockSpecies_value = map[string]uint8{
"chicken": 0,
"goat": 1,
"cattle": 2,
"carabao": 3,
"horse": 4,
"swine": 5,
}
)
func (ls LivestockSpecies) String() string {
return LivestockSpecies_name[uint8(ls)]
}
func ParseLivestockSpecies(livestock_species string) (LivestockSpecies, error) {
livestock_species = strings.TrimSpace(strings.ToLower(livestock_species))
value, ok := LivestockSpecies_value[livestock_species]
if !ok {
return LivestockSpecies(0), fmt.Errorf("%q is not a valid UserType", livestock_species)
}
return LivestockSpecies(value), nil
}
func (ls LivestockSpecies) MarshalJSON() ([]byte, error) {
return json.Marshal(ls.String())
}
func (ls *LivestockSpecies) UnmarshalJSON(data []byte) (err error) {
var livestockSpecies string
if err := json.Unmarshal(data, &livestockSpecies); err != nil {
return err
}
if *ls, err = ParseLivestockSpecies(livestockSpecies); err != nil {
return err
}
return nil
}
//end Livestock parser
Currently, I’m having issue when associating VHCApplication to ShippingDoc. The error is saying that:
invalid field found for struct
project_name/api/proto/out.ShippingDoc’s field IncludedVhcs:
define a valid foreign key for relations or implement the
Valuer/Scanner interface
which is very confusing to me. I don’t see why VHCApplication needs valuer/scanner when it doesn’t have an unusual structure. I was able to create a VHCApplication on it’s own, but when I use it as an association, it doesn’t work.
Here’s the code I use to create the association:
for _, el := range shipping_doc.IncludedVhcs {
vhc := &models.VHCApplication{
ID: el.Id,
GrowerID: el.GrowerId,
PermitType1ID: el.PermitType_1Id,
PermitType2ID: el.PermitType_2Id,
PermitType3ID: el.PermitType_3Id,
PermitType4ID: el.PermitType_4Id,
PermitType5ID: el.PermitType_5Id,
PermitType6ID: el.PermitType_6Id,
PermitType7ID: el.PermitType_7Id,
PermitType8ID: el.PermitType_8Id,
PermitType9ID: el.PermitType_9Id,
PermitType10ID: el.PermitType_10Id,
PermitType11ID: el.PermitType_11Id,
PermitType12ID: el.PermitType_12Id,
ApplicationDate: el.ApplicationDate,
ApprovedByMunicipalRepID: el.ApprovedByMunicipalRepId,
ApprovedByProvetRepID: el.ApprovedByProvetRepId,
CheckedByVetID: el.CheckedByVetId,
HasPaidMunicipalFee: el.HasPaidMunicipalFee,
HasPaidProvetFee: el.HasPaidProvetFee,
IssuanceDate: el.IssuanceDate,
ValidUntil: el.ValidUntil,
ControlNumber: el.ControlNumber,
Remarks: el.Remarks,
IssuedToHaulerOnDateTime: el.IssuedToHaulerOnDatetime,
IssuedToHaulerID: el.IssuedToHaulerId,
IssuedToAgentOnDateTime: el.IssuedToAgentOnDatetime,
IssuedToAgentID: el.IssuedToAgentId,
QRCodeVal: el.QrCodeVal,
AICID: el.AicId,
}
err := cmmHelpers.GetDB().Debug().Model(&shipping_doc).Association("IncludeVhcs").Append(
&vhc,
)
if err != nil {
return nil, err
}
}
And here’s the json I used for testing:
{
"grower_id": "c66b62f5-bf30-44da-995e-5750e91aed13",
"permit_type_1": "c66b62f5-bf30-44da-995e-5750e91aed13",
"permit_type_2": "c66b62f5-bf30-44da-995e-5750e91aed13",
"permit_type_3": "c66b62f5-bf30-44da-995e-5750e91aed13",
"application_date": "2024-06-20 10:10:00",
"approved_by_municipal_rep_id": "c66b62f5-bf30-44da-995e-5750e91aed13",
"approved_by_provet_rep_id": "c66b62f5-bf30-44da-995e-5750e91aed13",
"checked_by_vet_id": "c66b62f5-bf30-44da-995e-5750e91aed13",
"has_paid_municipal_fee": true,
"has_paid_provet_fee": false,
"valid_until": "2024-06-21 08:13:00",
"control_number": "VHC-09GT3V4",
"remarks": "Fully paid",
"issued_to_hauler_on_datetime": "2024-06-21 08:13:00",
"issued_to_hauler_id": "c66b62f5-bf30-44da-995e-5750e91aed13",
"issued_to_agent_on_datetime": "2024-06-21 08:13:00",
"issued_to_agent_id": "c66b62f5-bf30-44da-995e-5750e91aed13"
}