ariga / atlas-provider-gorm

GORM Provider for https://atlasgo.io
Apache License 2.0
55 stars 14 forks source link

Foreign key constraints not generated when models are in incorrect order (many-to-many) #34

Closed 3mm4nuel closed 5 months ago

3mm4nuel commented 8 months ago

When UserRole mapping table model is last, foreign key constraints are not generated

image image

However, when UserRole is not last (i.e. first or second), fk constraints are generated

image image image
noamcattan commented 8 months ago

Are you able to provide the model definitions?

3mm4nuel commented 8 months ago

Are you able to provide the model definitions?

image image image

ronenlu commented 8 months ago

Hi @3mm4nuel There is a bug in GORM, so if you defined Customize JoinTable, you should pass it before the structs that has the many to many annotation.

jeandeducla commented 5 months ago

Hello! I had the same issue and hopefully I found this thread. However I cannot seem to make the use case fully work.

The Gorm Customize JoinTable documentation seems to say that you can add extra columns to the join table i.e. columns that are not the many2many FK however when I add extra columns and generate the migration, those columns are not generated. If I take the above example i.e. those models:

type User struct {                                                                                                                                 
    ID    string  `gorm:"primarykey;column:id;"`                                
    Name  string  `gorm:"column:name;"`                                
    Roles []*Role `gorm:"many2many:UserRole;foreignKey:id;references:id;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
}                                                           

type Role struct {                                                                               
    ID          string  `gorm:"primarykey;column:id;"`                                
    Description string  `gorm:"column:description;"`                                
    Users       []*User `gorm:"many2many:UserRole;foreignKey:id;references:id;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
}                                                           

type UserRole struct {                                      
    Coucou string `gorm:"column:coucou;"`
    UserID string `gorm:"primarykey;column:UserID;"`
    RoleID string `gorm:"primarykey;column:RoleID;"`
}

and generate a migration with :


  func main() {                                                                                                                                    
      stmts, err := gormschema.New("postgres").
         Load(
             &postgresql.UserRole{},
             &postgresql.User{},
             &postgresql.Role{},                                                   
        )                                                                         
      if err != nil {                                
          fmt.Fprintf(os.Stderr, "failed to load gorm schema: %v\n", err)                                
          os.Exit(1)                                
      }                                
      io.WriteString(os.Stdout, stmts)                                

I get:

-- Create "roles" table                                                                                                                            
CREATE TABLE "roles" (
    "id" text NOT NULL,                                
    "description" text NULL,                                                    
    PRIMARY KEY ("id")                                                 
);                                        
-- Create "users" table                                     
CREATE TABLE "users" (                                                         
    "id" text NOT NULL,                                                                          
    "name" text NULL,                                                                 
    PRIMARY KEY ("id")                                                              
);                                
-- Create "user_roles" table                                                   
CREATE TABLE "user_roles" (                                
    "role_id" text NOT NULL,                                         
    "user_id" text NOT NULL,                                     
    PRIMARY KEY ("role_id", "user_id"),                                             
    CONSTRAINT "fk_user_roles_role" FOREIGN KEY ("role_id") REFERENCES "roles" ("id") ON UPDATE CASCADE ON DELETE CASCADE,
    CONSTRAINT "fk_user_roles_user" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON UPDATE CASCADE ON DELETE CASCADE
);                                                                                                       

Am I missing something? Thank you so much!

luantranminh commented 5 months ago

Hey @jeandeducla, thank you for reaching out. I noticed that you're using Customize JoinTable but haven't set it up yet. It's a required step for GORM to recognize your JoinTable. We have an adapter for configuring it with the WithJoinTable option. Here is an example: https://github.com/ariga/atlas-provider-gorm?tab=readme-ov-file#frequently-asked-questions

jeandeducla commented 5 months ago

Thanks luantranminh! It's exactly what I was looking for. Sorry I did no see that part of the documentation.

luantranminh commented 5 months ago

@jeandeducla Hey, we've just released a new version of the provider that automatically sets up the Customize JoinTable. Your old code still works, we maintain backward compatibility 🎉. But the new one is more handy and seamlessly does all the jobs. If you feel interested, you can check it out with v0.5.0.

There is no need for the WithJoinTable option anymore.

func main() {                                                                                                                                    
    stmts, err := gormschema.New("postgres").
       Load(
           &postgresql.UserRole{},
           &postgresql.User{},
           &postgresql.Role{},                                                   
      )                                                                         
    if err != nil {                                
        fmt.Fprintf(os.Stderr, "failed to load gorm schema: %v\n", err)                                
        os.Exit(1)                                
    }                                
    io.WriteString(os.Stdout, stmts)   
}