1

I am practicing by developing a web server written in Go, using Gqlgen and Gorm (postgres dialect).

I started by describing the model of my User:

# dbm.User{}

type User struct {
    BaseModel      
    Email       string  `gorm:"not null;unique_index:idx_email"`
    UserID      *string // External user ID
    Name        *string
    NickName    *string
    FirstName   *string
    LastName    *string
    Location    *string `gorm:"size:1048"`
    Description *string `gorm:"size:1048"`
}

In this case, from what I understand, it is nice to use pointers for certain values in my struct because they can evaluate to <nil> when there is no value attached to it. Whereas if I use no pointers, an int will be 0 if no value is attached to it. Alright, this makes sense...if I am not wrong...

However, the difficulty that I face now, is when getting this value out of a Gorm operation on the DB:

dbRecord := &dbm.User{}

db = db.Where(whereEmail, email).Find(&dbRecord)
fmt.Println("dbRecord:", dbRecord)
// Prints:
// dbRecord: &{{def90b3c-93ff-40f0-8d33-6320dddbe3f2 2019-10-01 14:16:34.365395 +0200 CEST 2019-10-01 14:16:34.365395 +0200 CEST} [email protected] <nil> 0xc0000a1cb0 0xc0000a1cd0 0xc0000a1cc0 <nil> 0xc0000a1cf0 0xc0000a1ce0}

This code queries a User where email is "[email protected]", and obviously some fields in the struct point to the actual address of the value. This makes sense, but it is very annoying if I want to read this data. Is there a conventional way of reading this data?

What I tried is to is to convert this struct into a GQL struct (which was autogenerated by GQLgen):

# gqlm.User{}

type User struct {
    ID          string     `json:"id"`
    Email       string     `json:"email"`
    UserID      *string    `json:"userId"`
    Name        *string    `json:"name"`
    FirstName   *string    `json:"firstName"`
    LastName    *string    `json:"lastName"`
    NickName    *string    `json:"nickName"`
    Description *string    `json:"description"`
    Location    *string    `json:"location"`
    CreatedAt   time.Time  `json:"createdAt"`
    UpdatedAt   *time.Time `json:"updatedAt"`
}

But again, I get the addresses when I want the values, which makes sense.

So the only way I found is to create a new custom struct customUser, Marshal the data from Gorm, then Unmarshal it into my customUser struct:

type customUser struct {
    ID          string    `json:"id"`
    Email       string    `json:"email"`
    UserID      string    `json:"userId"`
    Name        string    `json:"name"`
    FirstName   string    `json:"firstName"`
    LastName    string    `json:"lastName"`
    NickName    string    `json:"nickName"`
    Description string    `json:"description"`
    Location    string    `json:"location"`
    CreatedAt   time.Time `json:"createdAt"`
    UpdatedAt   time.Time `json:"updatedAt"`
}

bs, _ := json.Marshal(record)
var s = string(bs)

newRecord := &customUser{}
bs = []byte(s) // This is maybe not necessary

err = json.Unmarshal(bs, newRecord)
if err != nil {
   fmt.Println(err)
}

fmt.Printf("record: %+v\n", newRecord)
// I now get the right format: record: &{ID:def90b3c-93ff-40f0-8d33-6320dddbe3f2 Email:[email protected] UserID: Name:frer FirstName:fwef LastName: NickName:trgt Description:werfergh rthrt hr Location:wfwf CreatedAt:2019-10-01 14:16:34.365395 +0200 CEST UpdatedAt:2019-10-01 14:16:34.365395 +0200 CEST}

But this seems so overkilled, and lots of effort if I need to create such struct everytime I need to read data from the DB. Is this the way to go? Or do you know a more convenient way?

5
  • Why don't use use plain fields instead of pointer fields? Is that a requirement? If there's one thing unconventional here then that would be it. Commented Oct 1, 2019 at 14:35
  • @mkopriva, you mean Name string instead of Name *string in my Gorm User struct? I thought that using pointers there is actually conventional, because it avoids confusion when a field is empty and that it returns a value anyway Commented Oct 1, 2019 at 14:40
  • Yes that is what I mean. Well you would have to clarify what the actual confusion is in that specific case, i.e. a user with name "" and a user with name nil, both indicate a user with no name, there is no confusion, it's just that one is more difficult to use then the other, right? There are legitimate cases where "" is semantically not the same as nil, however there is less of those cases then cases where those two can be considered the same, in my opinion. Commented Oct 1, 2019 at 14:46
  • You say "but it is very annoying if I want to read this [pointer] data". Why? Reading the value from a *string is dead simple like *Nickname. What is annyoing here? Commented Oct 1, 2019 at 14:48
  • @Volker, by "this data" I meant "this struct", I agree that reading one piece of data is simple Commented Oct 1, 2019 at 14:55

1 Answer 1

1

You need to use sql.NullString for string pointer types.

Please see https://golang.org/pkg/database/sql/#NullString

Specifically one uses sql null types where it makes sense for there to be a concept of "no valid value for this field".

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.