I’m trying to bulid a REST API using go and specifically GORM.
My entities look like this:
type Business struct {
ID string `json:"id" gorm:"primaryKey;not null;type:uuid;default:gen_random_uuid()"`
CreatedAt time.Time `json:"-"`
UpdatedAt time.Time `json:"-"`
Email string `json:"email" gorm:"uniqueIndex;not null;"`
Password []byte `json:"-"`
Name string `json:"name"`
Promos []Promo `json:"promos" gorm:"foreignKey:CompanyID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
}
type Promo struct {
PromoID string `json:"id" gorm:"primaryKey;not null;type:uuid;default:gen_random_uuid()"`
CompanyID string `json:"company_id" gorm:"not null;foreignKey:ID"`
CreatedAt time.Time `json:"-"`
UpdatedAt time.Time `json:"-"`
Target Target `json:"target" gorm:"foreignKey:TargetID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
Active bool `json:"-" gorm:"default:true"`
ActiveFrom time.Time `json:"active_from"`
ActiveUntil time.Time `json:"active_until"`
Description string `json:"description" gorm:"not null"`
ImageURL string `json:"image_url"`
MaxCount int `json:"max_count" gorm:"not null"`
Mode string `json:"mode" gorm:"not null"`
PromoCommon string `json:"promo_common"`
PromoUnique []PromoUnique `json:"promo_unique" gorm:"foreignKey:PromoUniqueID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
}
type PromoUnique struct {
PromoUniqueID string `json:"-" gorm:"primaryKey;not null;type:uuid;default:gen_random_uuid()"`
Body string `json:"-" gorm:"not null"`
Activated bool `json:"-" gorm:"default:false"`
}
type Target struct {
TargetID string `json:"-" gorm:"primaryKey;not null;type:uuid;default:gen_random_uuid()"`
AgeFrom int `json:"age_from"`
AgeUntil int `json:"age_until"`
Country countries.CountryCode `json:"country"`
Categories []Category `json:"categories" gorm:"foreignKey:CategoryID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
}
type Category struct {
CategoryID string `json:"id" gorm:"primaryKey;not null;type:uuid;default:gen_random_uuid()"`
TargetID string `json:"-" gorm:"not null"`
Name string `json:"name" gorm:"not null"`
}
My service’s code looks like this:
func (s *promoService) Create(ctx context.Context, promoDTO dto.PromoCreate) (*entity.Promo, error) {
activeFrom, _ := time.Parse("2006-01-02", promoDTO.ActiveFrom)
activeUntil, _ := time.Parse("2006-01-02", promoDTO.ActiveUntil)
var categories []entity.Category
var promoUniques []entity.PromoUnique
for _, category := range promoDTO.Target.Categories {
categories = append(categories, entity.Category{
Name: category,
})
}
for _, promoUnique := range promoDTO.PromoUnique {
promoUniques = append(promoUniques, entity.PromoUnique{
Body: promoUnique,
})
}
company, _ := s.businessStorage.GetByID(ctx, promoDTO.CompanyID)
promo := entity.Promo{
Target: entity.Target{
AgeFrom: promoDTO.Target.AgeFrom,
AgeUntil: promoDTO.Target.AgeUntil,
Country: countries.ByName(strings.ToUpper(promoDTO.Target.Country)),
Categories: categories,
},
CompanyID: company.ID,
Active: true,
ActiveFrom: activeFrom,
ActiveUntil: activeUntil,
Description: promoDTO.Description,
ImageURL: promoDTO.ImageURL,
MaxCount: promoDTO.MaxCount,
Mode: promoDTO.Mode,
PromoCommon: promoDTO.PromoCommon,
PromoUnique: promoUniques,
}
company.Promos = append(company.Promos, promo)
_, err := s.businessStorage.Update(ctx, company)
if err != nil {
return nil, err
}
return s.promoStorage.Create(ctx, promo)
}
My storage looks like this:
// Create is a method to create a new Promo in database.
func (s *promoStorage) Create(ctx context.Context, promo entity.Promo) (*entity.Promo, error) {
err := s.db.Model(&promo).WithContext(ctx).Create(&promo).Error
return &promo, err
}
But when I try to reach an endpoint I get this error:
/opt/internal/adapters/database/postgres/promo.go:22 ERROR: insert or update on table "promos" violates foreign key constraint "fk_businesses_promos" (SQLSTATE 23503)
What should I do? I’ve researched some other questions on stackoverflow with the same issue, but none helped me.
2
Answers
The error message is actually very obvious: violates foreign key constraint。
Solution:
Suggestion: Learn the foreign key constraints of postgresql.
Your
Promo.CompanyID
having aforeignKey
looks suspicious;from what I understand the reverse (
foreignKey
onBusiness.Promos
) is sufficient,moreover
Promo.CompanyID
refers to anID
without specifying it’s aBusiness
and not aCompany
.