3

I am troubleshooting an issue that just came up while using Gorm. I had everything working great with my sqlite3 database and Go data models, but then when I had some issues with dependencies not 'go get'ing in the build environment, so I tried copying/deleting some packages from the vendor folder, re 'go get'ing until I got the build working... But now when I compile and run in my own machine, Im getting issues I never had before.

When I attempt something like this (configID is already checked to ensure it has a valid entry):

    var config models.ConfigurationDescription

    // Find the Configuration
    results := db.Where("id = ?", configID).
        Preload("Location").
        Find(&config)

Gorm is throwing the following error:

 "invalid field found for struct `models.ConfigurationDescription`'s field Location, need to define a valid foreign key for relations or it need to implement the Valuer/Scanner interface"

This is how I have defined the data models (which were working great prior to the dependency jumble):

package models

type LocationDescription struct {
    ID       int    `json:"locationID"`
    Name     string `json:"name"`
    IsActive bool   `json:"isActive"`
}

func (LocationDescription) TableName() string { return "locations" }

type ConfigurationDescription struct {
    ID         int                 `json:"configurationID"`
    Name       string              `json:"name"`
    IsActive   bool                `json:"isActive"`
    LocationID int                 `json:"-"`
    Location   LocationDescription `json:"location,omitempty" gorm:"foreignKey:ID;references:LocationID"`
}

func (ConfigurationDescription) TableName() string { return "configurations" }

Thats being run against a sqlite database with this setup:

CREATE TABLE IF NOT EXISTS locations (
    id           INTEGER PRIMARY KEY AUTOINCREMENT,
    name         TEXT,
    latitude     REAL CHECK (latitude > 0),
    longitude    REAL CHECK (latitude > 0),
    is_active    INTEGER DEFAULT 0
);

CREATE TABLE IF NOT EXISTS configurations (
    id               INTEGER PRIMARY KEY AUTOINCREMENT,
    location_id      INTEGER,
    name             TEXT,
    is_active        INTEGER DEFAULT 0,
    CONSTRAINT location_fk FOREIGN KEY (location_id) REFERENCES locations(id) ON DELETE CASCADE
);

I know it was working and I didn't change any of the code. And everything Im seeing looks like it follows the docs to the T, so it doesn't make sense that it would be like an update to the dependency broke something, because it doesn't appear that there has been any breaking changes.....

AND! The real kicker is that this works:

var location models.Location
DB.Where("is_active = ?", true).
        Preload("Configurations").
        Find(&location)

(these structs and the database actually have many more fields, but for simplicity, Ive trimmed them down)

type Location struct {
    ID             int             `json:"locationID"`
    Name           string          `json:"name"`
    Latitude       null.Float      `json:"latitude,omitempty"`
    Longitude      null.Float      `json:"longitude,omitempty"`
    IsActive       null.Bool       `json:"isActive" gorm:"default:false"`
    Configurations []Configuration `json:"configurations,omitempty" gorm:"foreignKey:LocationID;references:ID"`
}

type Configuration struct {
    ID             int             `json:"configurationID"`
    LocationID     int             `json:"locationID"`
    Name           string          `json:"name"`
    IsActive       null.Bool       `json:"isActive" gorm:"default:false"`
}

So the question is, does anyone know what might be causing this? And more importantly, have any suggestions for how to fix the issue?

Machavity
  • 30,841
  • 27
  • 92
  • 100
Teknein
  • 106
  • 1
  • 1
  • 7

1 Answers1

12

It seems to me that you've got your tags the wrong way around for the LocationDescription field.

First, this is a Belongs-To relationship pattern.

  • foreignKey should name the model-local key field that joins to the foreign entity.
  • references should name the foreign entity's primary or unique key.

Try with:

type ConfigurationDescription struct {
    ID         int                 `json:"configurationID"`
    Name       string              `json:"name"`
    IsActive   bool                `json:"isActive"`
    LocationID int                 `json:"-"`
    Location   LocationDescription `json:"location,omitempty" gorm:"foreignKey:LocationID;references:ID"`
}
Ezequiel Muns
  • 7,492
  • 33
  • 57
  • 1
    That worked! Now I'm *really* confused on how it was ever working in the first place, because it definitely was setup the same way since I started working on this project. – Teknein May 19 '21 at 14:21
  • I'm confused here, if i'm using json and need to marshal it to struct what value i should set for "location" field. Because it's clearly won't accept int because the original type is struct. – Hanif Nr Oct 21 '21 at 09:37
  • 1
    @HanifNr if you're unmarshalling a JSON version of ConfigurationDescription, it should have a "location" key with a value that is an JSON object following the schema of the Location struct, like `{"id":1,"name":"test",...}`, for it to succeed. – Ezequiel Muns Oct 22 '21 at 13:57