0
votes

Following an example in GORM's documentation (http://gorm.io/docs/has_many.html#Has-Many), I attempted to make a User and CreditCard model with a one-to-many relation:

package main

import (
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/sqlite"
    "github.com/sirupsen/logrus"
)

type User struct {
    gorm.Model
    CreditCards []CreditCard
}

type CreditCard struct {
    gorm.Model
    Number string
    UserID uint
}

func main() {
    db, err := gorm.Open("sqlite3", "examplegorm.db")
    if err != nil {
        logrus.Fatalf("open db: %v", err)
    }
    defer db.Close()

    db.LogMode(true)
    db.AutoMigrate(&User{})
    db.AutoMigrate(&CreditCard{})
}

However, in resulting logged SQL statements I don't see something like

CREATE TABLE credit_cards(
    ...
    FOREIGN KEY(user_id) REFERENCES users(id)
)

as I would expect from SQLite's documentation (https://www.sqlite.org/foreignkeys.html). What I see is

> go run gorm_has_many.go

(/Users/kurt/Documents/Scratch/gorm_has_many.go:28) 
[2020-01-06 09:05:58]  [0.82ms]  CREATE TABLE "users" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"deleted_at" datetime )  
[0 rows affected or returned ] 

(/Users/kurt/Documents/Scratch/gorm_has_many.go:28) 
[2020-01-06 09:05:58]  [0.55ms]  CREATE INDEX idx_users_deleted_at ON "users"(deleted_at)   
[0 rows affected or returned ] 

(/Users/kurt/Documents/Scratch/gorm_has_many.go:29) 
[2020-01-06 09:05:58]  [0.52ms]  CREATE TABLE "credit_cards" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"deleted_at" datetime,"number" varchar(255),"user_id" integer )  
[0 rows affected or returned ] 

(/Users/kurt/Documents/Scratch/gorm_has_many.go:29) 
[2020-01-06 09:05:58]  [0.50ms]  CREATE INDEX idx_credit_cards_deleted_at ON "credit_cards"(deleted_at)   
[0 rows affected or returned ] 

By contrast, in an example Django project in a djangoapp app, if I have the following models.py:

from django.db import models


class User(models.Model):
    name = models.CharField(max_length=255)
    age = models.PositiveIntegerField()


class CreditCard(models.Model):
    number = models.CharField(max_length=255)
    user = models.ForeignKey('djangoapp.User', on_delete=models.CASCADE)

and I inspect the SQL statements corresponding to the creation of the CreditCard model, I do see a foreign key reference:

kurt@Kurts-MacBook-Pro-13 ~/D/S/djangoproject> python manage.py sqlmigrate djangoapp 0002_creditcard
(0.000) 
            SELECT name, type FROM sqlite_master
            WHERE type in ('table', 'view') AND NOT name='sqlite_sequence'
            ORDER BY name; args=None
(0.000) SELECT "django_migrations"."id", "django_migrations"."app", "django_migrations"."name", "django_migrations"."applied" FROM "django_migrations"; args=()
(0.000) PRAGMA foreign_keys = OFF; args=None
(0.000) PRAGMA foreign_keys; args=None
(0.000) BEGIN; args=None
CREATE TABLE "djangoapp_creditcard" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "number" varchar(255) NOT NULL, "user_id" integer NOT NULL REFERENCES "djangoapp_user" ("id") DEFERRABLE INITIALLY DEFERRED); (params None)
(0.000) PRAGMA foreign_key_check; args=None
CREATE INDEX "djangoapp_creditcard_user_id_eca64c45" ON "djangoapp_creditcard" ("user_id"); (params ())
(0.000) PRAGMA foreign_keys = ON; args=None
BEGIN;
--
-- Create model CreditCard
--
CREATE TABLE "djangoapp_creditcard" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "number" varchar(255) NOT NULL, "user_id" integer NOT NULL REFERENCES "djangoapp_user" ("id") DEFERRABLE INITIALLY DEFERRED);
CREATE INDEX "djangoapp_creditcard_user_id_eca64c45" ON "djangoapp_creditcard" ("user_id");
COMMIT;

Why is the user_id column not being generated with a REFERENCES clause like in the Django app? (It seems that GORM has an addForeignKey() method (https://github.com/jinzhu/gorm/blob/79a77d771dee4e4b60e9c543e8663bbc80466670/scope.go#L1229-L1238) but it isn't getting invoked in this example).

Update

Since it would appear that GORM's AddForeignKey() method doesn't actually get called within GORM itself, I added the following line:

db.Model(&CreditCard{}).AddForeignKey("user_id", "users(id)", "CASCADE", "CASCADE")

However, re-running the script then appears to show a syntax error:

(/Users/kurt/Documents/Scratch/gorm_has_many.go:30) 
[2020-01-06 09:36:29]  near "CONSTRAINT": syntax error 

(/Users/kurt/Documents/Scratch/gorm_has_many.go:30) 
[2020-01-06 09:36:29]  [0.06ms]  ALTER TABLE "credit_cards" ADD CONSTRAINT credit_cards_user_id_users_id_foreign FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE;  
[0 rows affected or returned ] 
1

1 Answers

1
votes

Gorm does not add Foreign Keys when Auto Migrating. You gotta do it manually.

SQLite does not support adding constraints after creating the table. That's why the error you're having. Check this resource: How do I add a foreign key to an existing SQLite table?