Golang Gorm how to write nested objects to the database?

Viewed 201

I am new to golang and gorm.io and so might have made some basic mistakes.

I am trying to add the ability to save tags to the repositories in my project but I am struggling to understand if this is something that gorm can help with and how I would do it with gorm.

Current Environment

go version go1.17.2 darwin/amd64
gorm.io/driver/postgres v1.0.8
gorm.io/gorm v1.21.8

I have the following scenario.

Data Structure: User -> Belongs_to One - Organisation -> Has_Many Repositories -> Has_Many Tags

My Structs

type Organization struct {
  gorm.Model
  UserID  uuid.UUID
  OrgName string
  Repositories []Repository
}

type Repository struct {
  gorm.Model
    Name           string    `gorm:"index"`
    Version        string
    Description    string
    URL            string
    OrganizationID uuid.UUID
    Tags           []*Tag    `gorm:"many2many:repository_tags;"`
}

type Tags struct {
  gorm.Model
  Name string
  Repositories []*Repository `gorm:"many2many:repository_tags;"`
}

type Tag struct {
    database.DefaultModel
    Name         string
    Repositories []*Repository `gorm:"many2many:repository_tags;"`
}

Workflow:

  1. User registers themselves on a site.
  2. During the user creation a personal organisation is created with the username.
  3. User is assigned to the organisation.
  4. User Creates repositories in their organisation.

Each Repository could have one or many tags. Tags can be common across organisations.

Questions

  1. Is my struct design correct for such a multi tier data structure?
  2. When I call the function below the repository record is created but the tags are not.
  3. Is there a better way to do this?
//Create a new repository
func NewRepository(c *fiber.Ctx) error {
    c.Accepts("application/json")
    // Valid Header
    if !helpers.ValidRequestHeader(c) {
        return apperr.UnsupportedMediaType("Not Valid Content Type, Expect application/json")
    }
    var id uuid.UUID
    orgname := strings.ToLower(c.Params("org"))

    user_token := c.Locals("user").(*jwt.Token)
    claims := user_token.Claims.(jwt.MapClaims)
    username := claims["username"].(string)
    db := database.DB
    id = helpers.GetUserIDByUserName(username)
    var organization models.Organization
    repository := new(models.Repository)

    // Check for a valid token
    if !helpers.ValidToken(user_token, id) {
        return c.Status(500).JSON(fiber.Map{"Status": "Error", "Message": "Invalid token id", "Data": nil})
    }

    // find the org in the organization table
    db.Where("org_name = ?", orgname).Find(&organization)
    if username != orgname {
        return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"Status": "Error", "Message": "Unauthorized", "Data": nil})
    }
    if username == orgname {
        db.Model(&organization).Where("org_name = ?", orgname).Association("Repositories").Append(repository)
    }
    return c.JSON(repository)
}

if I use this JSON calling the create function.

{
  "name": "applicationrepo",
  "user": "superman",
  "version": "0.2.2",
  "description": "Repository",
  "url": "https://github.com/repoorg/repo",
  "tags": [
      {"name": "Golang"},
      {"name": "DBBackend"}
      ]
}

I get this returned

{
  "id": "0204f336-a622-46c4-9abb-3de08208c237",
  "name": "applicationrepo",
  "version": "0.2.2",
  "description": "Repository",
  "url": "https://github.com/repoorg/repo",
  "orgid": "fd94183c-e553-423e-a38f-fcecc4540bca",
  "tags": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "Name": "Golang",
      "Repositories": null
    },
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "Name": "DBBackend",
      "Repositories": null
    }
  ]
}

and the tags are not written to the tags table or the join table.

How can I get GORM to do this for me or should I look at some other way to create records in my application?

Thanks in advance.

0 Answers
Related