Search code examples
databasepostgresqlgoormgo-gorm

GORM creating/inserting a model with a foreign key field returns an error (cannot convert)


here are two models I have and postgres migrations for these two models (every separate migration and struct is in its separate file):

type BaseModel struct {
    ID        uint64 `gorm:"primarykey"`
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt gorm.DeletedAt `gorm:"index"`
}

type User struct {
    BaseModel
    Password string `gorm:"column:password;size:255;not null"`
    Role     string `gorm:"column:role;size:255;not null"`
}

type UserContact struct {
    BaseModel
    User        User `gorm:"column:user_id;foreignKey:UserID;references:ID;unique;not null"`
    UserID      uint64
    Address     Address `gorm:"column:address_id;foreignKey:AddressID;references:ID;not null"`
    AddressID   uint64
    Email       string  `gorm:"column:email;size:255;unique;not null"`
    Firstname   string  `gorm:"column:firstname;size:255;not null"`
    Lastname    string  `gorm:"column:lastname;size:255;not null"`
    PhoneNumber *string `gorm:"column:phone_number;size:255;unique"`
}

CREATE TABLE "user" (
    "id" bigserial PRIMARY KEY,
    "password" VARCHAR(255) NOT NULL,
    "role" VARCHAR(255) NOT NULL,
    "created_at" timestamptz NOT NULL,
    "updated_at" timestamptz NOT NULL,
    "deleted_at" timestamptz
);

CREATE TABLE "user_contact" (
    "id" bigserial PRIMARY KEY,
    "user_id" bigserial UNIQUE NOT NULL,
    "address_id" bigserial NOT NULL,
    "email" VARCHAR(255) UNIQUE NOT NULL,
    "firstname" VARCHAR(255) NOT NULL,
    "lastname" VARCHAR(255) NOT NULL,
    "phone_number" VARCHAR(255) UNIQUE,
    "created_at" timestamptz NOT NULL,
    "updated_at" timestamptz NOT NULL,
    "deleted_at" timestamptz,
    CONSTRAINT "fk_user_contact_user_id"
        FOREIGN KEY("user_id") 
            REFERENCES "user"("id"),
    CONSTRAINT "fk_user_contact_address_id"
        FOREIGN KEY("address_id") 
            REFERENCES "address"("id")
);

CREATE UNIQUE INDEX idx_user_contact_user_id ON "user_contact"("user_id");
CREATE UNIQUE INDEX idx_user_contact_email ON "user_contact"("email");
CREATE UNIQUE INDEX idx_user_contact_phone_number ON "user_contact"("phone_number");

on this code i have the following error:

func (us *userService) Create(email, password, firstname, lastname string) (*model.User, error) {
    encryptedPassword, err := us.hashPassword(password)
    if err != nil {
        return nil, err
    }
    user := &model.User{
        Password: string(encryptedPassword),
        Role:     enums.UserRoles.PortalUser().String(),
    }
    us.db.Create(user)
    contact := &model.UserContact{
        Email:     email,
        Firstname: firstname,
        Lastname:  lastname,
        User:      *user,
    }
    us.db.Create(contact)

    return user, nil
}
app-1     | 2024/05/14 18:39:46 /app/internatl/pkg/service/user.go:38 cannot convert {{1 2024-05-14 18:39:46.397391868 +0000 UTC 2024-05-14 18:39:46.397391868 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} $2a$08$.LZgf6D1F3VRvK5eNHaO0OuasbZsSvqUUBjy/sPGSO.He55485pfS portal-user} to Int8

it is pointing to line with us.db.Create(contact)

can you help me here? Is it a bug or i'm wrong somewhere?

i tried assigning user.ID to userContact.UserID field but that didn't help. foreign keys (as i see) are set correctly. looks like the issue is GORM can't convert struct to int8 (ID type) because it doesn't see foreignKey:UserID. wdyt?


Solution

  • I figured out what the issue was.

    Instead of

    type UserContact struct {
        BaseModel
        User        User `gorm:"column:user_id;foreignKey:UserID;references:ID;unique;not null"`
        UserID      uint64
        Address     Address `gorm:"column:address_id;foreignKey:AddressID;references:ID;not null"`
        AddressID   uint64
        Email       string  `gorm:"column:email;size:255;unique;not null"`
        Firstname   string  `gorm:"column:firstname;size:255;not null"`
        Lastname    string  `gorm:"column:lastname;size:255;not null"`
        PhoneNumber *string `gorm:"column:phone_number;size:255;unique"`
    }
    

    I should have used this code:

    type UserContact struct {
        BaseModel
        User        User `gorm:"foreignKey:UserID;references:ID;unique;not null"`
        UserID      uint64
        Address     Address `gorm:"foreignKey:AddressID;references:ID;not null"`
        AddressID   uint64
        Email       string  `gorm:"column:email;size:255;unique;not null"`
        Firstname   string  `gorm:"column:firstname;size:255;not null"`
        Lastname    string  `gorm:"column:lastname;size:255;not null"`
        PhoneNumber *string `gorm:"column:phone_number;size:255;unique"`
    }
    

    I hope this topic will help somebody who also decided to put column tag to gorm tag with foreign key :)